WPF绑定依赖属性

时间:2012-11-20 16:21:04

标签: c# wpf xaml binding dependency-properties

我遇到了在UserControl中绑定依赖项属性的问题。初始化时,它获取一个值,但不会更新。我可能错过了一些明显的东西,这里有一些代码片段:

这是我绑定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>

以下是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>

这是依赖属性:

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

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

以下是更新所选用户的列表,该列表位于使用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}"

SelectedUser在这里定义了一个实现INotifyPropertyChanged的类:

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

我们的想法是,当在列表中选择新用户时,TextBox应该更新,但目前还没有这样做。我已将绑定放在本地TextBox上并且它更新正常,而不是DependencyProperty。任何帮助表示赞赏。

5 个答案:

答案 0 :(得分:3)

您可以尝试一些可能性:

首先,您的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}"/>

您可以更好地组织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>

当前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>

无论哪种方式,您都可以在TextBox上执行以下操作,因为它的DataContext将与其父文件相同:

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

如果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>

所以你可以这样做:

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

然而,在第二种方法中,你必须检查一件我不确定的事情。我知道当控件的DataContext发生更改时,它会更新所有依赖绑定。例如,如果您将PlayerDetails DataContext更改为另一个UserData实例,则BalanceContent属性也会更新。但是,在这种情况下,BalanceContent取决于UserData的SelectedUser的属性。所以它会监听User实例的Property更改。如果SelectedUser.Balance更改(并且User实现了INotifyPropertyChanged,或者它是DependencyProperty),则BalanceContent将更新。现在,如果UserData中的SelectedUser实例发生更改,我不确定BalanceContent是否会更新,因为我认为绑定不会侦听其路径中每个对象的更改。

修改

最后一点可能是我在使用xaml开发时遇到的第一个问题。我在Silverlight中有一个DataGrid,其实体类型具有复杂类型的属性。其中一列取决于复杂类型的属性。如果我更改了复杂类型的值,该列将更新正常(它实现了INPC)。如果我更改了实体的复杂类型实例,则该列不会...解决方案是级联DataContexts:我创建了一个模板列,为复杂类型设置了列的绑定,而不是其属性。然后我将模板的TextBox文本绑定到complextype的属性,因为它现在是TextBox的DataContext。

在您的情况下,您可以为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>

答案 1 :(得分:1)

请在更改用户控件中文本框的绑定后尝试测试,以绑定到BalanceContent,即User Control Dependency Property(您的原始绑定源似乎是数据上下文属性)

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

编辑:请尝试以下代码

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>

答案 2 :(得分:0)

Textbox正在尝试绑定到UserControls DataContext。给Usercontrol定义一个x:Name并将TextBox的DataContext设置为DataContext="{Binding ElementName=[YourControlName]}"

答案 3 :(得分:0)

<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2" 
       Text="{Binding SelectedItem.BalanceContent, ElementName=lstAccounts}" Grid.Row="7"></TextBox>
忘记绑定UserControl,直接将TextBox绑定到SelectedItem。我希望这会有所帮助。

答案 4 :(得分:0)

您正在将StaticResource绑定到该属性。与DynamicResources相反,StaticResources仅在初始化Window(或UserControl)时加载一次。 DynamicResources仅在需要时(每次需要时)加载。将立即获取对DynamicResource所做的任何更改,并相应地更新属性。只需将关键字“StaticResource”替换为“DynamicResource”,每次资源更改时都应更新属性绑定。

注意:如果StaticResource是一个派生自Freezable类的对象,它的行为也会像DynamicResource。例如,如果您的资源是Brush,则每次以任何方式更改Brush对象时都会更新bound属性(例如,通过更改其不透明度),但这是由于从Freezable类继承的行为。但是,如果使用新的Brush对象替换StaticResource中的Brush对象,则不会拾取更改,因为已对Resource进行了更改,即Static,而不是Brush。

另请注意:即使只作为StaticResources可用的资源(例如数据SystemColors,SystemFonts(提供对系统设置的访问权限))也可以包装在DynamicResource中,以确保在每次更改时更新属性。这可以这样做:

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