为什么有些控件需要 Dependency 属性来进行绑定,而有些则不需要?如何绑定RichTextBox.Document?

Why do some controls need Dependency properties for binding while others do not? How to bind RichTextBox.Document?

我有 1 个自定义控件需要将数据绑定到 2 个不同的用户控件:

        <TextBlock
                Text="{Binding Title}">
        </TextBlock>

        <RichTextBox
                x:Name="RichTextBoxControl"
                Focusable="False"
                Document="{Binding UserLogs}">
        </RichTextBox>

他们试图绑定的属性在同一个 DataContext 中并且定义如下:

public string Title {
    get => return _title;
    set {
        _title = value;
        NotifyPropertyChanged();
    }
}
private string _title;

public FlowDocument UserLogs {
    get => return _userlogs;
    set {
        _userlogs = value;
        NotifyPropertyChanged();
    }
}
private FlowDocument _userlogs;

我在 TextBlock 的绑定方面遇到了 0 个问题(无论是在编译时还是在运行时)。 尽管对于 RichTextBox 的绑定,我在 Visual Studio 的分析器中遇到错误(在运行时在我的 UserControl 的 InitializeComponent() 中):“Impossible to define 'Binding' on the 'Document' property of type 'RichTextBox'. A binding can only be defined on a DependencyProperty of a DependencyObject

所以我非常清楚如何使用 DependencyProperty 技巧修复该错误,但我真的不明白这两个绑定之间有什么区别以及为什么我必须对第二个绑定使用 DependencyProperty 而第一个没有它就像一个魅力。

我有点想在不理解背后原因的情况下解决这个问题,这对我来说是向无法理解的黑魔法迈出的一步T.T

欢迎任何解释:)

绑定的目标 必须DependencyProperty。由于您在 RichTextBox 的 属性 上设置了绑定,因此此控件是绑定目标,而 Binding.Path 指定的 属性 是数据源。源可以是通用 CLI 属性 或任何 .NET 对象和 XML 数据。


Read Microsoft Docs: Basic data binding concepts (image source)

FlowDocument 的基本思想是它是可编辑的并且支持富文本格式。由于文档是可编辑的,因此 FlowDocument 的实例预计不会更改。相反,实例本身,即文档内容将被用户(或应用程序)修改。因此,设计者选择将其作为 CLI 属性 来促进对文档内容的直接编辑,因为加载新的 FlowDocument 实例会严重影响性能(我有根据的猜测)。

如果您仍然需要绑定到文档源,则需要实施附加行为:

RichTextBox.cs

public class RichTextBox : DependencyObject
{
  #region DocumentSource attached property

  public static readonly DependencyProperty DocumentSourceProperty = DependencyProperty.RegisterAttached(
    "DocumentSource", 
    typeof(FlowDocument), 
    typeof(RichTextBox), 
    new PropertyMetadata(default(FlowDocument), RichTextBox.OnDocumentSourceChanged));

  public static void SetDocumentSource(DependencyObject attachingElement, FlowDocument value) => attachingElement.SetValue(RichTextBox.DocumentSourceProperty, value);

  public static FlowDocument GetDocumentSource(DependencyObject attachingElement) => (FlowDocument) attachingElement.GetValue(RichTextBox.DocumentSourceProperty);

  #endregion

  private static void OnDocumentSourceChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (!(attachingElement is System.Windows.Controls.RichTextBox richTextBox))
    {
      return;
    }

    richTextBox.Document = e.NewValue as FlowDocument;
  }
}

用法

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <RichTextBox RichTextBox.DocumentSource="{Binding UserLogs}" />
</Window>

ViewModel.cs

class ViewModel : INotifyPropertyChanged
{
  private FlowDocument userlogs;
  public FlowDocument UserLogs 
  {
    get => return this.userlogs;
    set 
    {
      this.userlogs = value;
      NotifyPropertyChanged();
    }
}