【问题标题】:WPF Binding a Dependency PropertyWPF 绑定依赖属性
【发布时间】:2012-11-08 18:33:06
【问题描述】:

我在用户控件中绑定依赖项属性时遇到问题。当它初始化时,它会获得一个值,但随后它不会更新。我可能错过了一些明显的东西,这里有一些代码 sn-ps:

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

这是UserControl中的TextBox

 <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 上。任何帮助表示赞赏。

【问题讨论】:

    标签: c# wpf xaml binding dependency-properties


    【解决方案1】:

    您可以尝试一些可能性:

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

    如果用户控件在命令和其他高级逻辑等方面依赖于根视图模型,则将 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>
    

    所以你可以这样做:

    <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 的属性。所以它将监听该用户实例的属性更改。如果 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>
    

    【讨论】:

    • 感谢您的回复。在我详细阅读整个帖子之前,请快速评论一下。 ViewModel 中的 selecteduser 正在更新,因为这是我想到的第一件事。我还有一个 TextBox,它是窗口的成员,它使用与依赖属性相同的绑定来更新余额。该值已初始化良好,但随后更新控件的 TextBox 中的值保持不变,因此我认为您的最后一点值得进一步调查
    • 因此,将 SelectedUser 的源更改为 ListView 仍然有效,即使 XAML 抱怨无法解决 Balance,我猜在设计时它显然会这样做。
    【解决方案2】:

    请在更改用户控件内的文本框绑定以绑定到作为用户控件依赖属性的 BalanceContent 后尝试测试(您的原始绑定源似乎是数据上下文属性)

    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>
    

    【讨论】:

    • 感谢您的尝试,但它的行为方式相同。
    • 我已经编辑了我的代码以反映您的建议,至少来源更明显。但是,它仍然像以前一样工作,其中值在初始化时更新,但不是在值更改时更新。
    • 是的,更新绑定的原因是原始绑定源绑定到 DataContext 上的一个属性,但它似乎是您自定义控件的依赖属性。您能否发布您的依赖属性代码?
    • 您能否在您的 ViewModel 上创建另一个属性,我们称之为 SelectedUserBalance 并将此属性用作控件的 BalanceContent 属性的来源?同时更新 SelectedUser 以包括 OnPropertyChanged(new PropertyChangedEventArgs("SelectedUserBalance"));每次设置。
    • 好的,我已经添加了依赖属性的代码。添加 SelectedUserBalance 属性无效。
    【解决方案3】:

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

    【讨论】:

    • 感谢您的输入,但这些更改的行为仍然相同。
    【解决方案4】:
    <TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2" 
           Text="{Binding SelectedItem.BalanceContent, ElementName=lstAccounts}" Grid.Row="7"></TextBox>
    

    忘记绑定 UserControl ,直接绑定 TextBox 到 SelectedItem 。我希望这会有所帮助。

    【讨论】:

    • 抱歉,我想我不是很清楚。 lstAccounts 是一个使用 TextBox 所属的 UserControl 的视图。
    【解决方案5】:

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

    注意:如果 StaticResource 是派生自 Freezable 类的对象,它的行为也会有点像 DynamicResource。例如,如果您的资源是 Brush,则每次您以任何方式更改 Brush 对象(例如,通过更改其不透明度)时,绑定属性都会更新,但这是由于从 Freezable 类继承的行为所致。但是,如果您将 StaticResource 中的 Brush 对象替换为新的 Brush 对象,则不会获取 更改,因为已对资源进行了更改,该资源是静态的,而不是 Brush。

    另请注意:即使是仅作为静态资源可用的资源,例如数据 SystemColors、SystemFonts(提供对系统设置的访问)也可以包装在 DynamicResource 中,以确保在每次更改时更新属性。这可以通过这种方式完成:

    &lt;Control Property="{DynamicResource {x:Static SystemColors.XXX}}"&gt;&lt;/Control&gt;.

    【讨论】:

      猜你喜欢
      • 2014-08-31
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-03
      • 2010-10-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多