2012-11-20 191 views
4

我有一个问题绑定UserControl中的依赖项属性。初始化它时会得到一个值,但不会更新。我可能错过了一些显而易见的,这里有一些代码段:WPF绑定依赖属性

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

这里是在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> 

这里是依赖属性:

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

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

这里是所选择的用户进行更新列表中,这是一个使用用户控件一个观点:

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

回答

3

有一些可能性,你可以尝试:

首先,你的ListView可能不是UPD查看你的ViewModel的SelectedUser属性。尝试将您的ListView中的绑定设置为“双向”模式:

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

无论哪种方式,您将能够做到在文本框下面的,因为它的DataContext将是相同的其母公司的一个:

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

如果用户控件依赖于根视图模型对于像命令和其他高层次的逻辑,然后设置的DataContext它在某种程度上可以EA sily访问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:我创建了一个模板列,为复杂类型设置列的绑定,而不是其属性。然后我将我的模板的文本框的文本绑定到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> 
+0

感谢您的回复。在我详细阅读整篇文章之前,请快速发表评论。 ViewModel中的所选用户正在更新,因为这是我认为的第一件事情。我也有一个TextBox,它是窗口的成员,它使用与依赖项属性上使用的绑定相同的绑定来更新余额。该值初​​始化正常,但后续更新控件的TextBox中的值保持不变,因此我认为您的最后一点值得进一步调查 – paj7777

+0

因此,即使XAML抱怨无法解析Balance,仍然将SelectedUser的源更改为ListView ,我猜在设计时它显然会做。 – paj7777

0

该文本框试图绑定到UserControls DataContext。举一个x:Name到该用户控件定义和设置TextBox的DataContext的,以DataContext="{Binding ElementName=[YourControlName]}"

+0

感谢您的输入,但仍然表现与这些更改相同。 – paj7777

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

忘记用户控件的结合,直接绑定文本框到的SelectedItem。我希望这将有所帮助。

+0

对不起,我不认为我很清楚。 lstAccounts是使用TextBox所属的UserControl的视图。 – paj7777

1

请尝试测试修改为您的用户控件中的文本框绑定来绑定到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> 
+0

谢谢你的尝试,但它的行为方式相同。 – paj7777

+0

我编辑了我的编码以反映您的建议,至少源代码更加明显。但是,它仍然像以前一样工作,其中值在初始化时更新,但值不会在更改时更新。 – paj7777

+0

是的,更新绑定的原因是原始绑定源绑定到DataContext上的某个属性,但它看起来像是您的自定义控件的依赖项属性。 请问您可以发布您的依赖属性代码? – byte

0

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

注意:如果StaticResource是从Freezable类派生的对象,它的行为也有点像DynamicResource。例如,如果您的资源是Brush,则每次更改Brush对象时(例如,通过更改其不透明度),绑定属性都会更新,但这是由于从Freezable类继承的行为所致。但是,如果用一个新的画笔对象替换StaticResource中的画笔对象,则这个更改不会被拾取,因为已对“静态”资源进行更改,而不是“画笔”。

另请注意:即使只能以StaticResources形式提供的资源(如数据SystemColors,SystemFonts(可提供对系统设置的访问权限))也可以包装在DynamicResource中,以确保在每次更改时更新属性。这可以通过这种方式完成:

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