簡體   English   中英

如何將數據綁定到 UWP 中 RichEditBox 的純文本值?

[英]How can I databind to the plain-text value of a RichEditBox in UWP?

使用 UWP 中的普通TextBox ,您可以將數據綁定到Text屬性,並輕松地從 ViewModel 獲取或設置值。 RichEditBox沒有數據綁定的Text屬性; 相反,您必須使用Document屬性公開的ITextDocument接口並使用各種方法來獲取和設置文本。

如何將純文本數據綁定到 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>

背后的代碼

這假設您使用的是默認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的實現非常簡單,但由於依賴屬性基礎結構和需要避免無休止的屬性更新(其中更改富文本觸發純文本,從而觸發富文本),它需要相當數量的代碼text 觸發純文本,依此類推)。

附屬財產

這可以在它自己的文件中,也可以再次粘貼到代碼隱藏文件中。

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文本更改(包括富文本的變化),它更新了PlainText使用附加屬性richEdit.Document.GetText(TextGetOptions.None, out text) ,然后o.SetValue(PlainTextProperty, text)

請注意,此基本方法可用於對您想要根據實際數據可綁定屬性進行計算的其他“派生”屬性進行數據綁定。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM