如何将数据绑定到 UWP 中 RichEditBox 的纯文本值? [英] How can I databind to the plain-text value of a RichEditBox in UWP?

查看:26
本文介绍了如何将数据绑定到 UWP 中 RichEditBox 的纯文本值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 UWP 中的普通 TextBox,您可以将数据绑定到 Text 属性,并轻松地从 ViewModel 获取或设置值.RichEditBox 没有数据绑定的 Text 属性;相反,您必须使用 ITextDocument interfaceDocument 属性公开,并使用各种方法获取和设置文本.

With a normal TextBox in UWP, you can databind to the Text property and easily get or set the value from a ViewModel. The RichEditBox doesn't have a data-bindable Text property though; instead you have to use the ITextDocument interface exposed by the Document property and use various methods to get and set text.

如何将纯文本数据绑定到 ViewModel 中的某些内容?

How can I databind the plain text to something in my ViewModel?

推荐答案

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

It is possible to data-bind the plain-text of a RichEditBox by using a custom attached property. This attached property handles the conversion between the rich text and the plain text of the document.

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

Here is an example XAML page, code-behind, and ViewModel showing the usage of the attached property:

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

Copy this as the content of a new page in your project

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

背后的代码

这假设您使用默认的MainPage.xaml.cs;适当更改构造函数名称

Code behind

This assumes you're using the default MainPage.xaml.cs; change the constructor name as appropriate

public MainPage()
{
  InitializeComponent();
  DataContext = model = new ViewModel();
  model.PlainText = "Hello, world";
}

private void ChangeText(object sender, RoutedEventArgs e)
{
  model.PlainText = "Here is some plain text";
}

private void ChangeRichText(object sender, RoutedEventArgs e)
{
  richedit.Document.SetText(TextSetOptions.None, "Here is some rich text");
  var selection = richedit.Document.Selection;
  selection.StartPosition = 8;
  selection.EndPosition = 12;
  selection.CharacterFormat.Underline = UnderlineType.Single;
  selection.MoveStart(TextRangeUnit.Word, 1);
  selection.Expand(TextRangeUnit.Word);
  selection.CharacterFormat.Weight = FontWeights.Bold.Weight;
}

private void MakeBold(object sender, RoutedEventArgs e)
{
  richedit.Document.Selection.CharacterFormat.Weight = FontWeights.Bold.Weight;
}

视图模型

没什么特别的;只是一个字符串属性.您可以将其放在自己的文件中,也可以将其粘贴到主代码隐藏文件中.

ViewModel

Nothing special; just a single string property. You can put this in its own file, or paste it into the main code-behind file.

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));
  }
}

到目前为止,没什么特别的.RichEditBox 使用附加属性 RichEditBoxExtension.PlainText 并将其绑定到 ViewModel 属性 PlainText.页面上还有另一个 TextBlock 用于显示 PlainText 属性的当前值,以及一些用于操作文本的按钮.

So far, nothing special. The RichEditBox uses the attached property RichEditBoxExtension.PlainText and binds it to the ViewModel property PlainText. There is another TextBlock on the page to show the current value of the PlainText property, and a few buttons to manipulate the text.

RichEditBoxExtension.PlainText 的实现非常简单,但是由于依赖属性基础结构和避免无休止的属性更新的需要(改变丰富的text触发纯文本,富文本触发纯文本,依此类推).

The implementation of RichEditBoxExtension.PlainText is pretty straight-forward, but it takes a fair amount of code due to the dependency-property infrastructure and the need to avoid endless property updates (where changing the rich text triggers the plain text, which triggers the rich text which triggers the plain text, and so on and so on).

这可以在它自己的文件中,也可以再次粘贴到代码隐藏文件中.

This can be in its own file or again just pasted into the code-behind file.

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
}

附加属性基本上做了两件事,但是围绕它有很多样板代码和状态机制:

The attached property basically does two things, but there's a lot of boilerplate code and state machinery surrounding it:

  1. PlainText 附加属性发生更改时,它会使用 source.Document.SetText(TextSetOptions.None, text) 使用纯文本更新 RichEditBox
  2. RichEditBox 文本更改(包括富文本更改)时,它会使用 richEdit.Document.GetText(TextGetOptions.None, outtext) 然后 o.SetValue(PlainTextProperty, text).
  1. When the PlainText attached property is changed, it updates the RichEditBox with the plain text using source.Document.SetText(TextSetOptions.None, text)
  2. When the RichEditBox text changes (including rich text changes), it updates the PlainText attached property using richEdit.Document.GetText(TextGetOptions.None, out text) and then o.SetValue(PlainTextProperty, text).

请注意,这种基本方法可用于对您想要根据实际数据可绑定属性进行计算的其他派生"属性进行数据绑定.

Note that this basic approach can be used to data-bind other "derived" properties that you want to compute based off real data-bindable properties.

这篇关于如何将数据绑定到 UWP 中 RichEditBox 的纯文本值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆