Search code examples
c#xamldata-bindingmvvm-lightinotifypropertychanged

Binding two XAML element properties to the same ViewModel property does not work


Problem description

I use the Master Details View control. It has two properties Details Header and SelectedItem. I bind both to the Selected property in ViewModel. The goal is to change the DetailsHeader header depending on the selected item. The problem is that only SelectedItem is updated. The text does not appear in the DetailsHeader.

DetailsHeader="{x:Bind ViewModel.Selected, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.Selected, Mode=OneWay}"

If link one to the other, it works correctly - both are updated.

DetailsHeader="{x:Bind masterDetailsView.SelectedItem, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.Selected, Mode=OneWay}"

I do not find in the documentation a restriction on the fact that a property from a context can only be bound to one property of a XAML element. What could be the problem?

Code

ViewModelBase from MVVM Light is used for notification of changes. Its Set() method updates the property and raises a change event.

private SampleVendorModel _selected;
public SampleVendorModel Selected 
{ 
    get => _selected;
    set => Set(ref _selected, value);
}

Model

public class SampleVendorModel
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public string MiddleName { get; set; }

    public string FullName => $"{Surname} {Name} {MiddleName}";
    public string NameWithoutSurname => $"{Name} {MiddleName}";
}

MasterDetailsView

<controls:MasterDetailsView Name="masterDetailsView"
                            MasterHeader="{x:Bind ViewModel.Title}"
                            MasterHeaderTemplate="{StaticResource MasterHeaderTemplate}"  
                            DetailsHeader="{x:Bind masterDetailsView.SelectedItem, Mode=OneWay}"
                            DetailsHeaderTemplate="{StaticResource DetailsHeaderTemplate}"                                   
                            ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}"
                            SelectedItem="{x:Bind ViewModel.Selected, Mode=OneWay}"
                            ItemTemplate="{StaticResource ItemTemplate}"
                            DetailsTemplate="{StaticResource DetailsTemplate}"                                    
                            BorderBrush="Transparent"
                            BackButtonBehavior="Manual">
</controls:MasterDetailsView>

Resource's page

<Page.Resources>
        <Style x:Key="HeaderStyle" TargetType="TextBlock">
            <Setter Property="FontSize" Value="30" />
            <Setter Property="FontWeight" Value="Light" />
            <Setter Property="Margin" Value="0, 10, 0, 10" />
            <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
        </Style>

        <DataTemplate x:Key="MasterHeaderTemplate">
            <TextBlock Text="{Binding}" Style="{StaticResource HeaderStyle}"/>
        </DataTemplate>
        <DataTemplate x:Key="DetailsHeaderTemplate" x:DataType="viewmodels:SampleVendorModel">
            <TextBlock Text="{x:Bind FullName}" Style="{StaticResource HeaderStyle}" />
        </DataTemplate>
        <DataTemplate x:Key="ItemTemplate" x:DataType="viewmodels:SampleVendorModel">
            <Grid Margin="0, 10, 0, 10" RowSpacing="2">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <TextBlock Grid.Row="0" Text="{x:Bind Surname}" FontWeight="Bold" FontSize="16" TextTrimming="CharacterEllipsis" />
                <TextBlock Grid.Row="1" Text="{x:Bind NameWithoutSurname}" TextTrimming="CharacterEllipsis" />
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="DetailsTemplate"
                      x:DataType="viewmodels:SampleVendorModel">
            <StackPanel Margin="10">
                <TextBlock Text="{x:Bind Surname}" />
                <TextBlock Text="{x:Bind Name}" />
                <TextBlock Text="{x:Bind MiddleName}" />
            </StackPanel>
        </DataTemplate>
</Page.Resources>

Solution

  • The problem is that only SelectedItem is updated

    I wronged. MasterDetailsView.SelectedItem is updated via property ViewModel.Selected in the code. In case UI navigation, ViewModel.Selected doesn't change. Because binding mode is OneWay. As a consequence, MasterDetailsView.DetailsHeader doesn't get notify about change selected.

    In order to solve the problem, need to set mode as TwoWay for MasterDetailsView.SelectedItem.

    DetailsHeader="{x:Bind ViewModel.Selected, Mode=OneWay}"
    SelectedItem="{x:Bind ViewModel.Selected, Mode=TwoWay}"