WPF 绑定依赖属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13477486/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF Binding a Dependency Property
提问by paj7777
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:
我在绑定 UserControl 中的依赖项属性时遇到问题。当它初始化时,它会得到一个值,但它不会更新。我可能错过了一些明显的东西,这里有一些代码片段:
This is where I bind the BalanceContentdependency property:
这是我绑定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 TextBoxin the UserControl:
这是TextBox在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:
以下是更新所选用户的列表,该列表位于使用 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 SelectedUseris defined here in a class that implements INotifyPropertyChanged:
并SelectedUser在实现的类中定义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.
这个想法是当在列表中选择一个新用户时 TextBox 应该更新,但目前它没有这样做。我已经将绑定放在本地 TextBox 上并且它更新得很好,只是不在DependencyProperty. 任何帮助表示赞赏。
采纳答案by Arthur Nunes
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 可能不会更新您的 ViewModel 的 SelectedUser 属性。尝试将 ListView 中的绑定设置为“TwoWay”模式:
<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.
您可以更好地组织 DataContext 的定义方式。请记住,您的 UserControl 的所有子控件都可以在不使用相对绑定的情况下访问其 DataContext(它们继承它)。由于您的 PlayerInfo 控件依赖于 SelectedUser,请考虑将其 DataContext 设置为 SelectedUser,或者将其绑定到 ListView 的 SelectedUser 或 UserData 视图模型中的 SelectedUser。
<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:
当前 SelectedUser 的来源也可以是 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 上执行以下操作,因为它的 DataContext 将与其父项相同:
<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.
如果用户控件依赖根视图模型来执行命令和其他高级逻辑等操作,则将 DataContext 设置为您可以轻松访问 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.
但是,在第二种方法中,您必须检查我不确定的一件事。我知道当一个控件的 DataContext 发生变化时,它会更新所有的依赖绑定。例如,如果您将 PlayerDetails DataContext 更改为另一个 UserData 实例,则 BalanceContent 属性也会更新。但是,在这种情况下,BalanceContent 取决于 UserData 的 SelectedUser 的属性。所以它会监听那个 User 实例的属性变化。如果 SelectedUser.Balance 发生变化(并且 User 实现了 INotifyPropertyChanged,或者它是一个 DependencyProperty),BalanceContent 将更新。现在,如果 UserData 中的 SelectedUser 实例发生更改,我不确定 BalanceContent 会更新,因为我认为绑定不会侦听其路径中每个对象的更改。
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.
最后一点可能是我在使用 xaml 开发时遇到的第一个问题。我在 Silverlight 中有一个 DataGrid,其实体类型具有复杂类型的属性。其中一列取决于复杂类型的属性。如果我更改了复杂类型的值,该列会更新得很好(它实现了 INPC)。如果我更改了一个实体的复杂类型实例,该列不会... 解决方案是级联 DataContexts:我创建了一个模板列,为复杂类型设置列的绑定,而不是它的属性。然后我将模板的 TextBox 的文本绑定到 complextype 的属性,因为它现在是 TextBox 的 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.Text 执行此操作,但不能为 PlayerDetails.BalanceContent 执行此操作。您可以将 TextBox.DataContext 绑定到 UserData 的 SelectedUser,然后将 Text 绑定到 Balance 属性。
<TextBox VerticalAlignment="Center" DataContext="{Binding SelectedUser}" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding Balance}"
Grid.Row="7"></TextBox>
回答by byte
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)
请在更改用户控件内文本框的绑定以绑定到 BalanceContent 后尝试测试,该属性是用户控件依赖属性(您的原始绑定源似乎是数据上下文属性)
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>
回答by muneebshere
You are binding a StaticResource to the property. StaticResources, as opposed to DynamicResources, are loaded only once, when the Window (or UserControl) is being initialised. DynamicResources are loaded only when needed (and each time when needed). Any change that is made to a DynamicResource is picked up immediately and the property is updated accordingly. Simply replace the keyword "StaticResource" with "DynamicResource", and the property binding should update each time the resource changes.
您正在将 StaticResource 绑定到该属性。与 DynamicResources 相反,StaticResources 仅在初始化 Window(或 UserControl)时加载一次。DynamicResources 仅在需要时(以及每次需要时)加载。对 DynamicResource 所做的任何更改都会立即被选取并相应地更新该属性。只需将关键字“StaticResource”替换为“DynamicResource”,每次资源更改时属性绑定都会更新。
Note: If the StaticResource is an object that derives from the Freezable class, it will also behave somewhat like a DynamicResource. For example, if your resource is a Brush, the bound property will update each time you change the Brush object in any way (by changing its opacity, for example), but this is due to the behaviour inherited from the Freezable class. However if you replace the Brush object in the StaticResource by a new Brush object, thischange won't be picked up, because the change has been made to the Resource, which is Static, not the Brush.
注意:如果 StaticResource 是派生自 Freezable 类的对象,它的行为也有点像 DynamicResource。例如,如果您的资源是 Brush,则每次您以任何方式更改 Brush 对象(例如,通过更改其不透明度)时,绑定属性都会更新,但这是由于从 Freezable 类继承的行为。但是,如果您将 StaticResource 中的 Brush 对象替换为新的 Brush 对象,则不会接受此更改,因为已对静态资源而不是 Brush 进行了更改。
Also Note: Even resources that are only available as StaticResources, such as data SystemColors, SystemFonts (which provide access to system settings) can be wrapped in a DynamicResource to ensure that the property is updated at each change. This can be done this way:
另请注意:即使是仅作为静态资源可用的资源,例如数据 SystemColors、SystemFonts(提供对系统设置的访问)也可以包装在 DynamicResource 中,以确保在每次更改时更新属性。这可以通过以下方式完成:
<Control Property="{DynamicResource {x:Static SystemColors.XXX}}"></Control>.
<Control Property="{DynamicResource {x:Static SystemColors.XXX}}"></Control>.
回答by Federico Berasategui
The Textbox is trying to bind to the UserControls DataContext. give an x:Nameto the Usercontrol definition and set the TextBox's DataContext to DataContext="{Binding ElementName=[YourControlName]}"
Textbox 正在尝试绑定到 UserControls DataContext。给x:NameUsercontrol 定义一个,并将 TextBox 的 DataContext 设置为DataContext="{Binding ElementName=[YourControlName]}"
回答by yo chauhan
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding SelectedItem.BalanceContent, ElementName=lstAccounts}" Grid.Row="7"></TextBox>
forget binding of UserControl , Directly bind TextBox to SelectedItem . I hope this will help.
忘记绑定 UserControl ,直接绑定 TextBox 到 SelectedItem 。我希望这将有所帮助。

