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.
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>
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With