【问题标题】:Binding IsEnabled and other Properties to Public Properties, does it work like Dependency Properties?将 IsEnabled 和其他属性绑定到公共属性,它是否像依赖属性一样工作?
【发布时间】:2009-11-18 13:46:40
【问题描述】:

在我的场景中,我有很多按钮或其他控件,我想依赖于代码隐藏文件中的公共属性。我们称之为 IsEverythingLoaded,它是一个布尔值。

现在我想要一个像这样的按钮

<Button Click="DoTheMagic" 
     IsEnabled="{Binding Path=IsEverythingLoaded}">Click Me</Button>

为了让它运行,我发现我需要将它指向相对源,所以通过将它添加到我的&lt;Window&gt; decleration 中,我得到了启动和可视化的工作。

DataContext="{Binding RelativeSource={RelativeSource Self}}"

但是,假设我使用另一个按钮引发了一个事件,然后将 IsEverythingLoaded 设置为 true,我想每个按钮上的 IsEnabled 也会如此。因此可以再次点击,但我错了,这不是 DependencyProperties 应该如何工作的吗?

澄清..

我确实希望将 IsEverythingLoaded 写为 DependencyProperty。我希望 Button 依赖 A CLR 属性

【问题讨论】:

  • 感谢您的澄清!顺便说一句,将 DependencyProperties 用于 other 对象所依赖的属性在 MVVM 架构中非常常见,请参见,例如stackoverflow.com/questions/291518

标签: c# .net wpf binding


【解决方案1】:

需要通知您的按钮(即它的数据绑定)您的公共属性已更改。这可以通过两种方式完成:

  • 让您的班级实现 INotifyPropertyChanged 并在更改 IsEverythingLoaded 后引发 PropertyChanged 事件或
  • 将您的公共属性设为 DependencyProperty,在这种情况下,通知由框架自动完成。 DependencyProperty MSDN page 包含进一步的解释和如何执行此操作的示例。

对于第一个选项,Thomas 写了一个例子;对于第二个选项,您的代码将如下所示:

public static readonly DependencyProperty IsEverythingLoadedProperty =
    DependencyProperty.Register("IsEverythingLoaded", typeof(Boolean),
                                 typeof(YourCodeBehindClass), new PropertyMetadata(false));

public Boolean IsEverythingLoaded {
    get { return (Boolean)this.GetValue(IsEverythingLoadedProperty); }
    set { this.SetValue(IsEverythingLoadedProperty, value); } 
}

【讨论】:

  • 我知道如何创建 DP 以及如何使用它们,我的问题是为什么当您更改值时它的行为不像有序 DP。
  • 所以你是说IsEverythingLoaded 已经是一个依赖属性了?如果是,你真的不需要 INotifyPropertyChanged。
  • 不,不是,我也不希望这样。正如我所写的,我想将 DependencyProperty ( IsEnabled ) 绑定到代码隐藏属性,并且当我触发 CLR 属性时,我希望通知 DP。请再次阅读我的问题。
  • 我确实阅读了您的问题。请阅读我的回答。 :-) 如果你想进行数据绑定(target 是 DependencyProperty),source 需要 (1) 实现 INotifyPropertyChanged 或 (2) 也是 DependencyProperty ,在这种情况下,通知过程会自动完成。我的答案现在更清楚了吗?
  • 我已更新我的答案以使这一点更清楚。当我写下我的答案时,我的印象是你认为你的 public property 是一个依赖属性。
【解决方案2】:

我强烈不同意您关于绑定 DP -> DP 是“糟糕的设计”的笼统说法。

以下是 DP -> DP 绑定可能被认为是好的设计的一些原因:

  1. 将 DependencyProperty 绑定到另一个 DependencyProperty 比将其绑定到实现 INotifyPropertyChanged 的​​ CLR 属性效率更高。此用例已在 WPF 中进行了大量优化,并且是您应该采用的方式。

  2. DependencyProperties 不易出错,可让您的数据保持同步。很容易忘记调用 PropertyChanged 事件或没有正确分析您可能需要调用它的所有情况。 DependencyProperties 可以减轻您的负担。由于我从主要使用 INotifyPropertyChanged 切换到主要使用 DependencyProperties,我的数据错误同步错误可能减少了 100 倍,几乎为零,而且几乎总是在 INotifyPropertyChanged 代码中发现这些错误。

  3. 在编写代码演示时,您经常意外地遇到需要对您一直视为纯“数据”属性的内容进行动画处理。如果不是 DependencyProperty,则无法完成。我可以向您展示几个使用 DependencyProperties 真正节省时间的示例。

  4. 如果操作正确,创建 DependencyProperty 作为数据源的代码比支持 INotifyPropertyChanged 的​​相应 CLR 属性的代码更少(而且更简单)。

  5. 如果您使用 DependencyProperty 作为源,则可以使用业务规则添加强制和验证,而 CLR 属性无法做到这一点。

  6. 存储大量 DependencyProperties 比存储大量 CLR 属性更有效,使其非常适合具有许多“null”、0、“false”或“Empty”属性值的数据对象.

  7. DependencyProperties 提供了一种出色的线程安全机制,因为如果您尝试不正确地使用它们而不是默默地做错事,它们会立即提醒您。

  8. 如果您绑定到 DependencyProperties,绑定可以在必要时自动安全地跨线程。如果您要绑定到 CLR 属性,则根本没有保护,因此,如果您的应用程序有多个线程,唯一安全的方法是将 lock() 代码锁定到您的对象。由于缓存和加载/存储排序规则可能带来微妙的线程问题,不这样做会充满危险。

  9. DependencyProperties 可以使用 OverrideMetadata 有效地支持按对象类型改变默认值。经典 CLR 属性只能通过在每次构造对象时运行额外代码来实现。

我可以继续列出许多其他使用 DependencyProperties 作为绑定源的充分理由,但我想你明白了。

WPF 是基于您将绑定到 DependencyProperties 的假设构建的,而 INotifyPropertyChanged 只是后来才添加的(而且效率低下)。这证明了一群非常优秀的软件架构师清楚地认为绑定 DP -> DP 是一个很好的设计。

我意识到在某些情况下使用 INotifyPropertyChanged 代替 DP 有一些优势。尽管如此,DP -> DP 绑定肯定不是“糟糕的设计”,而且在许多情况下它是最好的设计。

【讨论】:

  • 这让一切都变得有争议,如果你要为你的答案添加一个解决方案,那就太好了。我明白你的意思,我想我以前没有从那个角度看过它。谢谢。
  • 你是对的。对不起,我来得相当强。我已经大大降低了我的cmets。 Heinzi 对您的问题给出了很好的直接回答,我投了赞成票,因此对于实际的解决方案,我将简单地参考他的回答。
【解决方案3】:

你的类需要实现INotifyPropertyChanged,这样才能通知绑定属性值发生了变化。

public class MyClass : INotifyPropertyChanged
{
    public bool IsEverythingLoaded
    {
        get { return _isEverythingLoaded; }
        set
        {
            _isEverythingLoaded = value;
            OnPropertyChanged("IsEverythingLoaded");
        }
    }

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
       PropertyChangedEventHandler handler = PropertyChanged;
       if (handler != null)
           handler(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 2013-03-30
    • 1970-01-01
    • 2011-09-09
    相关资源
    最近更新 更多