简体   繁体   English

WPF绑定依赖属性

[英]WPF Binding a Dependency Property

I am having an issue binding a dependency property in a UserControl. 我在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: 这是我绑定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 the UserControl : 这是UserControlTextBox

 <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 SelectedUser is 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. 我们的想法是,当在列表中选择新用户时,TextBox应该更新,但此时它不会这样做。 I've put the binding on local TextBox and it updates fine, just not on a DependencyProperty . 我已将绑定放在本地TextBox上并且它更新正常,而不是在DependencyProperty Any help appreciated. 任何帮助赞赏。

There are some possibilities you could try: 您可以尝试一些可能性:

First, your ListView may not be updating yours ViewModel's SelectedUser property. 首先,ListView可能不会更新ViewModel的SelectedUser属性。 Try setting the binding in your ListView to "TwoWay" mode: 尝试将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. 您可以更好地组织DataContext的定义方式。 Remember that all the child controls of your UserControl will have access to its DataContext without the use of relative binding (they inherit it). 请记住,UserControl的所有子控件都可以访问其DataContext而无需使用相对绑定(它们继承它)。 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. 由于您的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. 如果usercontrol依赖于命令和其他高级逻辑之类的根视图模型,则以可以轻松访问SelectedUser的方式将DataContext设置为它。

<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. 我知道当控件的DataContext发生更改时,它会更新所有依赖绑定。 For example, if you changed the PlayerDetails DataContext to another UserData instance, the BalanceContent property would update as well. 例如,如果您将PlayerDetails DataContext更改为另一个UserData实例,则BalanceContent属性也会更新。 However, in this case the BalanceContent depends on property of the SelectedUser of the UserData. 但是,在这种情况下,BalanceContent取决于UserData的SelectedUser的属性。 So it will listen to Property changes of that instance of User. 所以它会监听User实例的Property更改。 If the SelectedUser.Balance changes (and User implements INotifyPropertyChanged, or it is a DependencyProperty), BalanceContent will update. 如果SelectedUser.Balance更改(并且User实现了INotifyPropertyChanged,或者它是DependencyProperty),则BalanceContent将更新。 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. 现在,如果UserData中的SelectedUser实例发生更改,我不确定BalanceContent是否会更新,因为我认为绑定不会侦听其路径中每个对象的更改。

EDIT 编辑

The last point was perhaps the first problem I hit when developing with xaml. 最后一点可能是我在使用xaml开发时遇到的第一个问题。 I had a DataGrid in Silverlight whose entity type had a property of a complex type. 我在Silverlight中有一个DataGrid,其实体类型具有复杂类型的属性。 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). 如果我更改了复杂类型的值,该列将更新正常(它实现了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. 如果我更改了实体的复杂类型实例,则该列不会...解决方案是级联DataContexts:我创建了一个模板列,为复杂类型设置了列的绑定,而不是其属性。 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. 然后我将模板的TextBox文本绑定到complextype的属性,因为它现在是TextBox的DataContext。

In your case you can do it for the TextBox.Text, but not for the PlayerDetails.BalanceContent. 在您的情况下,您可以为TextBox.Text执行此操作,但不能为PlayerDetails.BalanceContent执行此操作。 You can bind the TextBox.DataContext to the SelectedUser of the UserData and then bind Text to the Balance property. 您可以将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>

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,即User Control Dependency 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>

The Textbox is trying to bind to the UserControls DataContext . Textbox正在尝试绑定到UserControls DataContext give an x:Name to the Usercontrol definition and set the TextBox's DataContext to DataContext="{Binding ElementName=[YourControlName]}" 给Usercontrol定义一个x:Name ,并将TextBox的DataContext设置为DataContext="{Binding ElementName=[YourControlName]}"

<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 . 忘记绑定UserControl,直接将TextBox绑定到SelectedItem。 I hope this will help. 我希望这将有所帮助。

You are binding a StaticResource to the property. 您正在将StaticResource绑定到该属性。 StaticResources, as opposed to DynamicResources, are loaded only once, when the Window (or UserControl) is being initialised. 与DynamicResources相反,StaticResources仅在初始化Window(或UserControl)时加载一次。 DynamicResources are loaded only when needed (and each time when needed). DynamicResources仅在需要时(每次需要时)加载。 Any change that is made to a DynamicResource is picked up immediately and the property is updated accordingly. 将立即获取对DynamicResource所做的任何更改,并相应地更新属性。 Simply replace the keyword "StaticResource" with "DynamicResource", and the property binding should update each time the resource changes. 只需将关键字“StaticResource”替换为“DynamicResource”,每次资源更改时都应更新属性绑定。

Note: If the StaticResource is an object that derives from the Freezable class, it will also behave somewhat like a DynamicResource. 注意:如果StaticResource是一个派生自Freezable类的对象,它的行为也会像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. 例如,如果您的资源是Brush,则每次以任何方式更改Brush对象时都会更新bound属性(例如,通过更改其不透明度),但这是由于从Freezable类继承的行为。 However if you replace the Brush object in the StaticResource by a new Brush object, this change won't be picked up, because the change has been made to the Resource, which is Static, not the Brush. 但是,如果使用新的Brush对象替换StaticResource中的Brush对象,则不会拾取更改,因为已对Resource进行了更改,即Static,而不是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. 另请注意:即使只作为StaticResources可用的资源(例如数据SystemColors,SystemFonts(提供对系统设置的访问))也可以包装在DynamicResource中,以确保在每次更改时更新属性。 This can be done this way: 这可以这样做:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM