【问题标题】:How can I databind to the plain-text value of a RichEditBox in UWP?如何将数据绑定到 UWP 中 RichEditBox 的纯文本值?
【发布时间】:2018-08-12 16:00:06
【问题描述】:

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

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

【问题讨论】:

    标签: uwp richeditbox


    【解决方案1】:

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

    这是一个示例 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>
    

    后面的代码

    这假设您使用默认的MainPage.xaml.cs;根据需要更改构造函数名称

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

    视图模型

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

    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 属性的当前值,以及一些用于操作文本的按钮。

    RichEditBoxExtension.PlainText 的实现非常简单,但是由于依赖属性基础设施和需要避免无休止的属性更新(更改富文本会触发纯文本,从而导致触发富文本触发纯文本,依此类推)。

    附加属性

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

    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
    }
    

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

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

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

    【讨论】:

    • 这个附加的属性实现就像 Caliburn micro 上的魅力一样。 (即使你提到它不相关)。我不认为我可以实现这样的东西,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-05
    相关资源
    最近更新 更多