Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Binding a Dependency Property

I am having an issue binding a dependency property in a UserControl. When it initializes it gets a value but then it will not update. I've probably missed something obvious, here are some code snippets:

This is where I bind the BalanceContent dependency property:

<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" 
                          BalanceContent="{Binding Source={StaticResource UserData}, Path=SelectedUser.Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

    </Game:PlayerDetails>

Here is the TextBox in the UserControl:

 <TextBox  VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2" 
         Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}" 
             Grid.Row="7"></TextBox>

Here is the Dependency Property:

public static readonly DependencyProperty BalanceContentProperty = DependencyProperty.Register(
        "BalanceContent", typeof(string), typeof(PlayerDetails));

    public string BalanceContent
    {
        get
        {return (string) GetValue(BalanceContentProperty);}
        set
        {SetValue(BalanceContentProperty, value);}
    }

Here is the list where the selected user is updated, which is in a view that uses the UserControl:

<ListView x:Name="lstAccounts"  Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="4" 
              ItemsSource="{Binding Source={StaticResource UserData}, Path=CurrentUserSearch}" 
              SelectedItem="{Binding Source={StaticResource UserData}, Path=SelectedUser}"

And SelectedUser is defined here in a class that implements INotifyPropertyChanged:

 public User SelectedUser
    {
        get
        {
            return _selectedUser;
        } 
        set
        {
            _selectedUser = value;
            OnPropertyChanged(new PropertyChangedEventArgs("SelectedUser"));
        }
    }

The idea is that the TextBox should update when a new user is selected in the list but at the moment it is not doing so. I've put the binding on local TextBox and it updates fine, just not on a DependencyProperty. Any help appreciated.

like image 726
paj7777 Avatar asked Nov 20 '12 16:11

paj7777


2 Answers

There are some possibilities you could try:

First, your ListView may not be updating yours ViewModel's SelectedUser property. Try setting the binding in your ListView to "TwoWay" mode:

<ListView x:Name="lstAccounts"  Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="4" 
          ItemsSource="{Binding Source={StaticResource UserData}, Path=CurrentUserSearch}" 
          SelectedItem="{Binding Source={StaticResource UserData}, Path=SelectedUser, Mode=TwoWay}"/>

You can organize better the way the DataContext's are defined. Remember that all the child controls of your UserControl will have access to its DataContext without the use of relative binding (they inherit it). As your PlayerInfo control depends on the SelectedUser, consider setting it's DataContext to the SelectedUser, either binding it to the SelectedUser of the ListView or the SelectedUser in the UserData viewmodel.

 <Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{Binding Source={StaticResource UserData}, Path=SelectedUser}"
                      BalanceContent="{Binding Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

</Game:PlayerDetails>

The source of the current SelectedUser could also be the ListView:

<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{Binding SelectedItem, ElementName=lstAccounts}"
                      BalanceContent="{Binding Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

</Game:PlayerDetails>

Either way, you will then be able to do the following on the TextBox, because its DataContext will be the same as the one of its parent:

<TextBox  VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2" 
     Text="{Binding Balance}" 
         Grid.Row="7"></TextBox>

If the usercontrol depends on the root viewmodel for things like commands and other high level logic, then set the DataContext to it in a way you can easily access the SelectedUser.

<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{StaticResource UserData}"
                      BalanceContent="{Binding SelectedUser.Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

</Game:PlayerDetails>

So you can do this:

<TextBox  VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2" 
     Text="{Binding SelectedUser.Balance}" 
         Grid.Row="7"></TextBox>

In this second approach, however, you will have to check one thing I'm not sure about. I know that when the DataContext of a control changes, it will update all the dependent bindings allright. For example, if you changed the PlayerDetails DataContext to another UserData instance, the BalanceContent property would update as well. However, in this case the BalanceContent depends on property of the SelectedUser of the UserData. So it will listen to Property changes of that instance of User. If the SelectedUser.Balance changes (and User implements INotifyPropertyChanged, or it is a DependencyProperty), BalanceContent will update. Now, if the SelectedUser instance in the UserData changes, I'm not sure BalanceContent will update, because I think that a binding does not listen to changes of every object in its path.

EDIT

The last point was perhaps the first problem I hit when developing with xaml. I had a DataGrid in Silverlight whose entity type had a property of a complex type. One of the columns depended on a property of the complex type. If I changed the value of the complex type, the column would update fine (it implemented INPC). If I changed the complex type instance of an entity, the column would not... The solution was to cascade DataContexts: I created a template column, set the binding of the column for the complex type, instead of its property. Then I bound the text of the TextBox of my template to the property of the complextype, because it was now the TextBox's DataContext.

In your case you can do it for the TextBox.Text, but not for the PlayerDetails.BalanceContent. You can bind the TextBox.DataContext to the SelectedUser of the UserData and then bind Text to the Balance property.

<TextBox  VerticalAlignment="Center" DataContext="{Binding SelectedUser}" FontFamily="Formata" FontSize="20" Grid.Column="2" 
 Text="{Binding Balance}" 
     Grid.Row="7"></TextBox>
like image 132
Arthur Nunes Avatar answered Sep 19 '22 09:09

Arthur Nunes


Please try testing after changing your binding for text box inside your user control to bind to BalanceContent which is User Controls Dependency Property (your original binding source seems to be data context property)

Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"

EDIT: Please try the following code

public User SelectedUser
    {
        get
        {
            return _selectedUser;
        } 
        set
        {
            _selectedUser = value;
            OnPropertyChanged(new PropertyChangedEventArgs("SelectedUser"));
            OnPropertyChanged(new PropertyChangedEventArgs("SelectedUserBalance"));
        }
    }

public string SelectedUserBalance
    {
        get
        {
            return _selectedUser.Balance;
        } 
    }

<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" 
                          BalanceContent="{Binding Source={StaticResource UserData}, Path=SelectedUserBalance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

</Game:PlayerDetails>

<TextBox  VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2" 
         Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"
             Grid.Row="7"></TextBox>
like image 42
byte Avatar answered Sep 20 '22 09:09

byte