Home > Software design >  Conditional Binding Issue
Conditional Binding Issue

Time:01-24

I am currently trying to bind a textbox conditionally based on 3 different objects depending on a Level enum value. In the code sample below, what I hoping to achieve is to display values depending on the following condition:

  • Person Level0, bind/display ViewModel.Person.Level0.Name
  • Person Level1, bind/display ViewModel.Person.Level1.Name
  • Person Level2, bind/display ViewModel.Person.Level2.Name

With all that, the problem i have is that not of the objects are displaying as i am hoping. The textbox remains empty, with no values on display. What am i doing wrong?

<TextBox Margin="0,0,0,5">
    <interactivity:Interaction.Behaviors>
        <core:DataTriggerBehavior Binding="{x:Bind ViewModel.Person.Level, FallbackValue='', Mode=OneWay}" ComparisonCondition="Equal" Value="Level0">
            <core:ChangePropertyAction PropertyName="Text" Value="{x:Bind ViewModel.Person.Level0.Name, FallbackValue=''}" />
        </core:DataTriggerBehavior>
        <core:DataTriggerBehavior Binding="{x:Bind ViewModel.Person.Level, FallbackValue='', Mode=OneWay}" ComparisonCondition="Equal" Value="Level1">
            <core:ChangePropertyAction PropertyName="Text" Value="{x:Bind ViewModel.Person.Level1.Name, FallbackValue='', Mode=OneWay}" />
        </core:DataTriggerBehavior>
        <core:DataTriggerBehavior Binding="{x:Bind ViewModel.Person.Level, FallbackValue='', Mode=OneWay}" ComparisonCondition="Equal" Value="Level2">
            <core:ChangePropertyAction PropertyName="Text" Value="{x:Bind ViewModel.Person.Level2.Name, FallbackValue='', Mode=OneWay}" />
        </core:DataTriggerBehavior>
    </interactivity:Interaction.Behaviors>
</TextBox>

CodePudding user response:

This should work.

Notes:

  • This code uses a string instead of Level in the Person class because enum binding seems not to work (I'm not sure) with ChangePropertyAction.
  • The MainPage is named as "ThisPage" so we can bind the ViewModel inside the ChangePropertyAction.

MainPage.xaml

<Page
    x:Class="ConditionalBindingTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
    xmlns:local="using:ConditionalBindingTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Name="ThisPage"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">
    <Grid>
        <TextBox>
            <interactivity:Interaction.Behaviors>
                <core:DataTriggerBehavior
                    Binding="{x:Bind ViewModel.Person.Level, Mode=OneWay}"
                    ComparisonCondition="Equal"
                    Value="Level0">
                    <core:ChangePropertyAction
                        PropertyName="Text"
                        Value="{Binding ElementName=ThisPage, Path=ViewModel.Person.Level0.Name, Mode=OneWay}" />
                </core:DataTriggerBehavior>
                <core:DataTriggerBehavior
                    Binding="{x:Bind ViewModel.Person.Level, Mode=OneWay}"
                    ComparisonCondition="Equal"
                    Value="Level1">
                    <core:ChangePropertyAction
                        PropertyName="Text"
                        Value="{Binding ElementName=ThisPage, Path=ViewModel.Person.Level1.Name, Mode=OneWay}" />
                </core:DataTriggerBehavior>
            </interactivity:Interaction.Behaviors>
        </TextBox>
    </Grid>
</Page>

MainPage.xaml.cs

using Microsoft.UI.Xaml.Controls;

namespace ConditionalBindingTest;

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public MainPageViewModel ViewModel { get; } = new();
}

MainPageViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;

namespace ConditionalBindingTest;

public enum Level
{
    Level0,
    Level1,
}

public class LevelName
{
    public LevelName(Level level)
    {
        Name = $"{level} Name";
    }

    public string Name { get; }
}

public class Person
{
    public string Level { get; } = ConditionalBindingTest.Level.Level0.ToString();

    public LevelName Level0 { get; } = new(ConditionalBindingTest.Level.Level0);

    public LevelName Level1 { get; } = new(ConditionalBindingTest.Level.Level1);
}

public partial class MainPageViewModel : ObservableObject
{
    [ObservableProperty]
    private Person person = new();
}

CodePudding user response:

What am i doing wrong?

  1. You use x:Bind where it's not supported
  2. You implement logic in the XAML markup of the view

x:Bind directly to another read-only property of the view model that is defined something like this:

public string DisplayValue
{
    get
    {
        switch (Person.Level)
        {
            case "Level0":
                return Person.Level0.Name;
            case "Level1":
                return Person.Level1.Name;
            case "Level2":
                return Person.Level2.Name;
        }

        return string.Empty;
    }
}

C# is a much more expressive, more concise language than XAML, and while it may be possible to create an entire fairly complex view in markup only, it doesn't mean that it's always a good idea doing so. In this case, you should move the logic to the view model or the Person class itself.

  • Related