如何将数据绑定到UWP中RichEditBox的纯文本值?

如何将数据绑定到UWP中RichEditBox的纯文本值?,uwp,richeditbox,Uwp,Richeditbox,使用UWP中的普通文本框,您可以数据绑定到文本属性,并从ViewModel轻松获取或设置值。但是,没有数据可绑定的Text属性;相反,您必须使用exposed by文档属性,并使用各种方法来获取和设置文本 如何将纯文本数据绑定到ViewModel中的某个对象?可以使用自定义数据绑定RichEditBox的纯文本。此附加属性处理文档的富文本和纯文本之间的转换 下面是一个示例XAML页面、代码隐藏和ViewModel,显示了附加属性的用法: XAML 将其复制为项目中新页面的内容 <Grid

使用UWP中的普通
文本框
,您可以数据绑定到
文本
属性,并从ViewModel轻松获取或设置值。但是,没有数据可绑定的
Text
属性;相反,您必须使用exposed by
文档
属性,并使用各种方法来获取和设置文本


如何将纯文本数据绑定到ViewModel中的某个对象?

可以使用自定义数据绑定
RichEditBox
的纯文本。此附加属性处理文档的富文本和纯文本之间的转换

下面是一个示例XAML页面、代码隐藏和ViewModel,显示了附加属性的用法:

XAML 将其复制为项目中新页面的内容

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <StackPanel Margin="30">
    <RichEditBox local:RichEditBoxExtension.PlainText="{Binding PlainText,
      Mode=TwoWay}" x:Name="richedit"/>
    <Button Content="Bold selection" Click="MakeBold"/>
    <Button Content="Change plain text (view model)" Click="ChangeText"/>
    <Button Content="Change rich text (control property)" Click="ChangeRichText"/>
    <TextBlock Text="PlainText property is..." />
    <TextBlock Text="{Binding PlainText, Mode=OneWay}" />
  </StackPanel>
</Grid>
视图模型 没什么特别的;只有一个字符串属性。您可以将其放入自己的文件中,或将其粘贴到主代码隐藏文件中

public class ViewModel : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  string plainText;
  public string PlainText
  {
    get { return plainText; }
    set
    {
      plainText = value;
      RaisePropertyChanged();
    }
  }

  void RaisePropertyChanged([CallerMemberName] string propertyName = "")
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}
public class RichEditBoxExtension
{
  // Standard attached property. It mimics the "Text" property of normal text boxes
  public static readonly DependencyProperty PlainTextProperty =
    DependencyProperty.RegisterAttached("PlainText", typeof(string),
    typeof(RichEditBoxExtension), new PropertyMetadata(null, OnPlainTextChanged));

  // Standard DP infrastructure
  public static string GetPlainText(DependencyObject o)
  {
    return o.GetValue(PlainTextProperty) as string;
  }

  // Standard DP infrastructure
  public static void SetPlainText(DependencyObject o, string s)
  {
    o.SetValue(PlainTextProperty, s);
  }

  private static void OnPlainTextChanged(DependencyObject o, 
    DependencyPropertyChangedEventArgs e)
  {
    var source = o as RichEditBox;
    if (o == null || e.NewValue == null)
      return;

    // This attaches an event handler for the TextChange event in the RichEditBox,
    // ensuring that we're made aware of any changes
    AttachRichEditBoxChangingHelper(o);

    // To avoid endless property updates, we make sure we only change the RichText's 
    // Document if the PlainText was modified (vs. if PlainText is responding to 
    // Document being modified)
    var state = GetState(o);
    switch (state)
    {
      case RichEditChangeState.Idle:
        var text = e.NewValue as string;
        SetState(o, RichEditChangeState.PlainTextChanged);
        source.Document.SetText(Windows.UI.Text.TextSetOptions.None, text);
        break;

      case RichEditChangeState.RichTextChanged:
        SetState(o, RichEditChangeState.Idle);
        break;

      default:
        Debug.Assert(false, "Unknown state");
        SetState(o, RichEditChangeState.Idle);
        break;
    }
  }

  #region Glue

  // Trivial state machine to determine who last changed the text properties
  enum RichEditChangeState
  {
    Idle,
    RichTextChanged,
    PlainTextChanged,
    Unknown
  }

  // Helper class that just stores a state inside a textbox, determining
  // whether it is already being changed by code or not
  class RichEditChangeStateHelper
  {
    public RichEditChangeState State { get; set; }
  }

  // Private attached property (never seen in XAML or anywhere else) to attach
  // the state variable for us. Because this isn't used in XAML, we don't need
  // the normal GetXXX and SetXXX static methods.
  static readonly DependencyProperty RichEditChangeStateHelperProperty =
    DependencyProperty.RegisterAttached("RichEditChangeStateHelper",
    typeof(RichEditChangeStateHelper), typeof(RichEditBoxExtension), null);

  // Inject our state into the textbox, and also attach an event-handler
  // for the TextChanged event.
  static void AttachRichEditBoxChangingHelper(DependencyObject o)
  {
    if (o.GetValue(RichEditChangeStateHelperProperty) != null)
      return;

    var richEdit = o as RichEditBox;
    var helper = new RichEditChangeStateHelper();
    o.SetValue(RichEditChangeStateHelperProperty, helper);

    richEdit.TextChanged += (sender, args) =>
    {
      // To avoid re-entrancy, make sure we're not already changing
      var state = GetState(o);
      switch (state)
      {
        case RichEditChangeState.Idle:
          string text = null;
          richEdit.Document.GetText(Windows.UI.Text.TextGetOptions.None, out text);
          if (text != GetPlainText(o))
          {
            SetState(o, RichEditChangeState.RichTextChanged);
            o.SetValue(PlainTextProperty, text);
          }
          break;

        case RichEditChangeState.PlainTextChanged:
          SetState(o, RichEditChangeState.Idle);
          break;

        default:
          Debug.Assert(false, "Unknown state");
          SetState(o, RichEditChangeState.Idle);
          break;
      }
    };
  }

  // Helper to set the state managed by the textbox
  static void SetState(DependencyObject o, RichEditChangeState state)
  {
    (o.GetValue(RichEditChangeStateHelperProperty) 
      as RichEditChangeStateHelper).State = state;
  }

  // Helper to get the state managed by the textbox
  static RichEditChangeState GetState(DependencyObject o)
  {
    return (o.GetValue(RichEditChangeStateHelperProperty) 
      as RichEditChangeStateHelper).State;
  }
  #endregion
}
到目前为止,没有什么特别的。
RichEditBox
使用附加属性
RichEditBoxExtension.PlainText
并将其绑定到ViewModel属性
PlainText
。页面上还有另一个
TextBlock
,用于显示
PlainText
属性的当前值,还有几个按钮用于操作文本

RichEditBoxExtension.PlainText
的实现非常简单,但由于依赖性属性基础结构和避免无休止的属性更新的需要,它需要相当多的代码(其中更改富文本会触发纯文本,这会触发触发纯文本的富文本,依此类推)

附属财产 这可以在它自己的文件中,也可以只是粘贴到代码隐藏文件中

public class ViewModel : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  string plainText;
  public string PlainText
  {
    get { return plainText; }
    set
    {
      plainText = value;
      RaisePropertyChanged();
    }
  }

  void RaisePropertyChanged([CallerMemberName] string propertyName = "")
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}
public class RichEditBoxExtension
{
  // Standard attached property. It mimics the "Text" property of normal text boxes
  public static readonly DependencyProperty PlainTextProperty =
    DependencyProperty.RegisterAttached("PlainText", typeof(string),
    typeof(RichEditBoxExtension), new PropertyMetadata(null, OnPlainTextChanged));

  // Standard DP infrastructure
  public static string GetPlainText(DependencyObject o)
  {
    return o.GetValue(PlainTextProperty) as string;
  }

  // Standard DP infrastructure
  public static void SetPlainText(DependencyObject o, string s)
  {
    o.SetValue(PlainTextProperty, s);
  }

  private static void OnPlainTextChanged(DependencyObject o, 
    DependencyPropertyChangedEventArgs e)
  {
    var source = o as RichEditBox;
    if (o == null || e.NewValue == null)
      return;

    // This attaches an event handler for the TextChange event in the RichEditBox,
    // ensuring that we're made aware of any changes
    AttachRichEditBoxChangingHelper(o);

    // To avoid endless property updates, we make sure we only change the RichText's 
    // Document if the PlainText was modified (vs. if PlainText is responding to 
    // Document being modified)
    var state = GetState(o);
    switch (state)
    {
      case RichEditChangeState.Idle:
        var text = e.NewValue as string;
        SetState(o, RichEditChangeState.PlainTextChanged);
        source.Document.SetText(Windows.UI.Text.TextSetOptions.None, text);
        break;

      case RichEditChangeState.RichTextChanged:
        SetState(o, RichEditChangeState.Idle);
        break;

      default:
        Debug.Assert(false, "Unknown state");
        SetState(o, RichEditChangeState.Idle);
        break;
    }
  }

  #region Glue

  // Trivial state machine to determine who last changed the text properties
  enum RichEditChangeState
  {
    Idle,
    RichTextChanged,
    PlainTextChanged,
    Unknown
  }

  // Helper class that just stores a state inside a textbox, determining
  // whether it is already being changed by code or not
  class RichEditChangeStateHelper
  {
    public RichEditChangeState State { get; set; }
  }

  // Private attached property (never seen in XAML or anywhere else) to attach
  // the state variable for us. Because this isn't used in XAML, we don't need
  // the normal GetXXX and SetXXX static methods.
  static readonly DependencyProperty RichEditChangeStateHelperProperty =
    DependencyProperty.RegisterAttached("RichEditChangeStateHelper",
    typeof(RichEditChangeStateHelper), typeof(RichEditBoxExtension), null);

  // Inject our state into the textbox, and also attach an event-handler
  // for the TextChanged event.
  static void AttachRichEditBoxChangingHelper(DependencyObject o)
  {
    if (o.GetValue(RichEditChangeStateHelperProperty) != null)
      return;

    var richEdit = o as RichEditBox;
    var helper = new RichEditChangeStateHelper();
    o.SetValue(RichEditChangeStateHelperProperty, helper);

    richEdit.TextChanged += (sender, args) =>
    {
      // To avoid re-entrancy, make sure we're not already changing
      var state = GetState(o);
      switch (state)
      {
        case RichEditChangeState.Idle:
          string text = null;
          richEdit.Document.GetText(Windows.UI.Text.TextGetOptions.None, out text);
          if (text != GetPlainText(o))
          {
            SetState(o, RichEditChangeState.RichTextChanged);
            o.SetValue(PlainTextProperty, text);
          }
          break;

        case RichEditChangeState.PlainTextChanged:
          SetState(o, RichEditChangeState.Idle);
          break;

        default:
          Debug.Assert(false, "Unknown state");
          SetState(o, RichEditChangeState.Idle);
          break;
      }
    };
  }

  // Helper to set the state managed by the textbox
  static void SetState(DependencyObject o, RichEditChangeState state)
  {
    (o.GetValue(RichEditChangeStateHelperProperty) 
      as RichEditChangeStateHelper).State = state;
  }

  // Helper to get the state managed by the textbox
  static RichEditChangeState GetState(DependencyObject o)
  {
    return (o.GetValue(RichEditChangeStateHelperProperty) 
      as RichEditChangeStateHelper).State;
  }
  #endregion
}
附加属性基本上做两件事,但它周围有很多样板代码和状态机制:

  • 更改
    纯文本
    附加属性时,它使用
    source.Document.SetText(TextSetOptions.None,text)
  • RichEditBox
    文本更改(包括富文本更改)时,它会使用
    richEdit.Document.GetText(TextGetOptions.None,out text)
    更新
    o.SetValue(PlainTextProperty,text)

  • 请注意,此基本方法可用于数据绑定其他基于真实数据绑定属性计算的“派生”属性。

    此附加属性实现在Caliburn micro上的效果非常好。(尽管您提到了它与此无关)。我认为我无法实现类似的任何功能,谢谢。