WPF binding dependency property

I had a problem binding dependency properties in UserControl. When it initializes, it gets the value, but then it will not be updated. I probably missed something obvious, here are some code snippets:

Here I bind the BalanceContent dependency BalanceContent :

 <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 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 in which the selected user is selected, which is in the view using 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 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 be updated when a new user is selected in the list, but at the moment he does not. I put the binding in a local TextBox and it updates perfectly, not DependencyProperty . Any help was appreciated.

+4
source share
5 answers

There are several possibilities you could try:

First, your ListView may not update your ViewModel 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 better organize a way to define a DataContext. Remember that all child UserControl controls will have access to its DataContext without using relative binding (they inherit it). Since your PlayerInfo control is dependent on SelectedUser, consider setting the DataContext to SelectedUser by binding it to a SelectedUser ListView or SelectedUser in UserData view mode.

  <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 can also be a 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> 

In any case, you can do the following in a TextBox, because its DataContext will be the same as its parent:

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

If usercontrol depends on the root view model for things like commands and other high-level logic, then set the DataContext so that you can easily access 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 need to check one thing, which I'm not sure about. I know that when you change the DataContext of the control, it will update all dependent bindings. For example, if you change the DataContext PlayerDetails to another instance of UserData, the BalanceContent property will also be updated. However, in this case, the BalanceContent depends on the SelectedUser UserData property. Therefore, it will listen for changes to the properties of this user instance. If SelectedUser.Balance changes (and the user implements INotifyPropertyChanged, or it is DependencyProperty), the BalanceContent will be updated. Now, if the instance of SelectedUser in UserData changes, I'm not sure that the BalanceContent will be updated, because I think the binding is not listening for changes to every object in its path.

EDIT

The last question was perhaps the first problem that I encountered while developing using xaml. I had a DataGrid in Silverlight whose object type had a property of a complex type. One of the columns depended on a property of a complex type. If I changed the value of a complex type, the column would update the penalty (it implemented INPC). If I changed the instance of a complex type of an object, the column would not be ... The solution was to cascade the DataContexts: I created a template column, set the column binding for the complex type, and not its property. Then I attached the text TextBox of my template to the complextype property, because now it is a TextCox DataContext.

In your case, you can do this for TextBox.Text, but not for PlayerDetails.BalanceContent. You can bind TextBox.DataContext to SelectedUser 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> 
+3
source

Try testing after changing the binding for the text field inside your user control to bind to the BalanceContent, which is a dependency property of the user controls (your original binding source seems to be a 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> 
+1
source

The text field is trying to bind to the UserControls DataContext . type x:Name in the Usercontrol definition and set the TextCox DataContext to DataContext="{Binding ElementName=[YourControlName]}"

0
source
 <TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2" Text="{Binding SelectedItem.BalanceContent, ElementName=lstAccounts}" Grid.Row="7"></TextBox> 

forget the UserControl binding, directly bind the TextBox to the SelectedItem. Hope this helps.

0
source

You bind the StaticResource property to the property. StaticResources, unlike DynamicResources, are loaded only once when the window (or UserControl) is initialized. DynamicResources are loaded only when necessary (and every time when necessary). Any changes made to DynamicResource are immediately selected, and the corresponding property is updated accordingly. Just replace the StaticResource keyword with DynamicResource, and the property binding should be updated every time the resource changes.

Note. If StaticResource is an object that comes from the Freezable class, it will also behave like a DynamicResource. For example, if your resource is Brush, the bound property will be updated every time you change the Brush object in any way (for example, by changing its opacity), but this is due to the behavior inherited from the Freezable class. However, if you replace the Brush object in StaticResource with a new Brush object, this change will not be selected because a change has been made to the resource, which is Static, not Brush.

Also note. Even those resources that are available only as StaticResources, such as SystemColors data, SystemFonts (which provide access to system settings), can be wrapped in DynamicResource to ensure that the property is updated with every change. This can be done as follows:

<Control Property="{DynamicResource {x:Static SystemColors.XXX}}"></Control> .

0
source

Source: https://habr.com/ru/post/1447273/


All Articles