【问题标题】:Linking DataContext with another property in WPF将 DataContext 与 WPF 中的另一个属性链接
【发布时间】:2011-11-06 07:36:03
【问题描述】:

问题是我总是想针对 DataContext 属性绑定控件。因为我不喜欢在 Binding 表达式中指定 Source。但是,我不想显式绑定控件的 DataContext 属性,而是使用另一个指定的自描述属性,例如 Items 或 Plugins 或任何控制目的。 现在,只要设置了真正确定控件上下文的自描述属性,我就需要一种方法来设置 DataContext 属性。此外,我希望 DataContext 始终与此属性相同。如何做到这一点?

我尝试使用回调指定 PropertyMetadata,该回调会将 DataContext 设置为新收到的值。它不起作用。首先,因为没有为属性的默认值调用此回调。其次,最有趣的是,我观察到了非常奇怪的行为。让我使用调用堆栈来解释这一点:

这是我的 PropertyMetadata,回调中有断点:

new PropertyMetadata((d, e) => ((SearchSettings)d).DataContext = e.NewValue)

这是调用栈(行尾有 cmets):

3. Bla-bla-bla.SearchSettings..cctor.AnonymousMethod__7(DependencyObject d, DependencyPropertyChangedEventArgs e) /* Setting property back to null. Why??? */
2. [External Code]  
1. Bla-bla-bla.SearchSettings..cctor.AnonymousMethod__7(DependencyObject d, DependencyPropertyChangedEventArgs e) /* This is where the breakpoint hit first. Settings a new value to a property as a result of databinding. */

那么,我该怎么做呢?或者我应该想要这样做吗?顺便说一句,我是 WPF 的新手,所以我可能会弄错。

【问题讨论】:

  • 你想用你的依赖属性实现什么?为什么将页面 DataContext 设置为某些属性的新值,而不是在页面上设置具有相同值的某些属性并绑定到该值?
  • 比起Items={Binding ElementName=me, Path=User.Name},我更喜欢Name={Binding Path=Name}。据我所知,这只有在DataContext 属性等于User 属性时才有可能。因此,我希望DataContext 属性等于User
  • 这可能被重置为 null 的原因可能是因为 DataContext 属性正在更改。这将流向设置绑定的控件。然后假设绑定被指定为TwoWay,然后将告诉控件更新它的Binding source,然后它会流回到PropertyMetadata 对象中指定的PropertyChangedCallback。这不会让您陷入无限循环的原因是因为在幕后进行的值检查。
  • 您只能使用 ElementName 绑定到 XAML 中控件的属性。这是你想要做的吗?例如,一个控件试图将它的一个属性设置为另一个控件的值?
  • 我就是不明白。你想做什么?你能再展示一些代码/XAML 来澄清一下吗?

标签: wpf data-binding


【解决方案1】:

根据您的latest question 中的答案,DataContext 是您的绑定将用作其数据源的对象。

当我建议将 DataContext 设置为页面或控件(如下,在原始答案中)时,您不必按照原始问题中的说明在 Binding 中指定 Source 属性。

示例 - 而不是像这样指定您的绑定:

{Binding Path=PropertyName, RelativeSource={RelativeSource AncestorType={x:Type ns:ControlName}}}

你本来可以用的

{Binding Path=PropertyName}

因为容器 DataContext 属性设置为 ControlName 的实例(仅当您指定 DataContext = this 时才会出现这种情况, DataContext = this.DataContext 因为这只会将其设置为本身并毫无意义)。

现在对于PageWindow 对象,PropertyName 可以是寄存器DependencyProperty 实例,也可以是引发INotifyPropertyChanged.PropertyChanged 事件以通知孩子的股票标准Property属性已更改的 XAML,它们会根据需要自行更新。

对于 Control 对象,您仍然可以使用任一方法 - 但是,如果您希望能够绑定到这些属性,则需要将它们定义为 DependencyPropertys。

您能否使用任何来源更新您的帖子以供我们查看?


原答案:

我不完全确定我理解您的最终目标,但为您的控件指定 DataContext 的最懒惰的方法是在构造函数中将页面的 DataContext 属性设置为自身。

this.DataContext = this;

这消除了在 Binding 中设置 Source 和 RelativeSource 属性的需要,同时仍然允许您直接绑定到页面属性。

例子:

public class MyControl : UserControl
{
    public static readonly DependencyProperty SomeStringProperty = DependencyProperty.Register("SomeString", typeof(string), typeof(ownerclass), new UIPropertyMetadata(0));

    public string SomeString
    {
        get { return (string)GetValue(SomeStringProperty); }
        set { SetValue(SomeStringProperty, value); }
    }

    public MyControl()
    {
        InitializeComponent();
        DataContext = this;
    }
}

然后在你的 XAML 中:

<TextBlock Text="{Binding Path=SomeString}" />

【讨论】:

  • 感谢您的回复。不幸的是,它不起作用,我也不知道。想象一个控件,它声明了一个依赖属性(为了确定性,我们称之为Items)。该控件在代码或 xaml 中的任何位置都没有提及 DataContext 属性。如果您运行应用程序,该属性将设置为某个值。现在我在InitializeControl(); 调用之后在控件的构造函数中添加this.DataContext = this;,这就是所有的变化。运行应用程序,执行相同的场景。结果是属性Items从不设置!很奇怪。
  • 嗨,你能发布一个快速重现你的问题吗?我确定您知道 DataContext 可以像任何其他 DP 一样在 XAML 中正确设置 - 在您的 Style 中,例如: .
  • 我可以,但现在不行。没时间。我回家后会发布它。也会尝试你描述的设置DataContext的方法。
  • 好的,我回来了。我的控件与作为示例编写的 fatty 仅在类名称、属性名称和类型方面有所不同,他使用的是UIPropertyMetadata,而我使用的是PropertyMetadata。控件本身的XAML中没有DataContext这个词,也不是在单独的地方使用了控件。然而,在构造函数中添加DataContext = this; 之后,SomeString 属性永远不会设置。并且从不设置我的意思是,如果您订阅处理程序到此属性更改事件,它将永远不会被调用。怎么样?
  • 因为没变?在您的输出窗口中,您是否有任何绑定错误? (注意:这些只会被记录,不会在 IDE 中作为异常抛出)。也许之前调用属性更改处理程序的原因是由于双向绑定,现在您已经更改了 DataContext,您绑定到的属性不再存在于同一绑定路径中?没有看到你的 XAML 就很难说。
【解决方案2】:

我真的没有理由创建一个替代 DataContext 的属性

DataContext 的目的是引用您控件背后的实际数据。为什么要为已经存在的第二个属性创建第二个属性?对我来说,这就像说你想重写一个控件的IsReadOnly 属性,所以它读取为DisableEditing - 它没有任何目的。

此外,您为什么要将它绑定到您的应用程序层? WPF 的全部意义在于将业务/应用程序逻辑与 UI 分离,而您要做的是将两者联系在一起。如果您想要这种行为,请使用 WinForms。

如果您想确保您的 UserControl 仅与 User DataContext 一起使用,请使用 DataTemplate

<DataTemplate DataType="{x:Type local:User}">
    <local:MyUserControl /> <!-- DataContext will be User -->
</DataTemplate>

<!-- This will display a User object, but because of the DataTemplate it will 
     draw it using your UserControl instead of the default User.ToString() -->
<ContentControl Content="{Binding CurrentUser}" /> 

如果你真的想确保你的UserControl 只接收User 对象作为DataContext,请对初始化进行某种验证检查this.DataContextUser 对象。不要创建人们需要记住在他们想要使用您的控件的任何时候绑定的某种自定义属性。

【讨论】:

  • 为什么输入控件具有值或文本等属性?为什么他们不使用他们的 DataContexts?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-12
  • 2011-10-21
  • 2012-03-27
  • 2011-03-28
  • 2016-07-18
相关资源
最近更新 更多