Связывание Richtextbox wpf

Чтобы сделать DataBinding документа в WPF RichtextBox , я видел 2 решения до сих пор, которые должны быть получены из RichtextBox и добавить DependencyProperty, а также решение с «прокси». Ни первый, ни второй не являются удовлетворительными. Кто-нибудь знает другое решение или вместо этого коммерческий RTF-контроль, способный к DataBinding ? Обычное текстовое поле не является альтернативой, так как нам требуется текстовое форматирование.

Есть идеи?

Я знаю, что это старый пост, но ознакомьтесь с расширенным набором инструментов WPF . Он имеет RichTextBox, который поддерживает то, что вы пытаетесь сделать.

Существует гораздо более простой способ!

Вы можете легко создать прикрепленное свойство DocumentXaml (или DocumentRTF ), которое позволит вам связать документ RichTextBox. Он используется, как это, где Autobiography является строковым свойством в вашей модели данных:

    

Вуаля! Полностью связываемые данные RichTextBox!

Реализация этого свойства довольно проста: когда свойство установлено, загрузите XAML (или RTF) в новый FlowDocument. Когда FlowDocument изменится, обновите значение свойства.

Этот код должен сделать трюк:

 using System.IO; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; public class RichTextBoxHelper : DependencyObject { public static string GetDocumentXaml(DependencyObject obj) { return (string)obj.GetValue(DocumentXamlProperty); } public static void SetDocumentXaml(DependencyObject obj, string value) { obj.SetValue(DocumentXamlProperty, value); } public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached( "DocumentXaml", typeof(string), typeof(RichTextBoxHelper), new FrameworkPropertyMetadata { BindsTwoWayByDefault = true, PropertyChangedCallback = (obj, e) => { var richTextBox = (RichTextBox)obj; // Parse the XAML to a document (or use XamlReader.Parse()) var xaml = GetDocumentXaml(richTextBox); var doc = new FlowDocument(); var range = new TextRange(doc.ContentStart, doc.ContentEnd); range.Load(new MemoryStream(Encoding.UTF8.GetBytes(xaml)), DataFormats.Xaml); // Set the document richTextBox.Document = doc; // When the document changes update the source range.Changed += (obj2, e2) => { if(richTextBox.Document==doc) { MemoryStream buffer = new MemoryStream(); range.Save(buffer, DataFormats.Xaml); SetDocumentXaml(richTextBox, Encoding.UTF8.GetString(buffer.ToArray())); } }; }}); } 

Этот же код можно использовать для TextFormats.RTF или TextFormats.XamlPackage. Для XamlPackage у вас должно быть свойство типа byte [] вместо строки.

Формат XamlPackage имеет несколько преимуществ перед простым XAML, особенно возможность включать такие ресурсы, как изображения, и более гибкая и удобная в работе, чем RTF.

Трудно поверить, что этот вопрос сидел в течение 15 месяцев, и никто не указывал на простой способ сделать это.

Я могу дать вам хорошее решение, и вы можете пойти с ним, но прежде чем я это сделаю, я попытаюсь объяснить, почему Document не является DependencyProperty для начала.

В течение срока действия элемента управления RichTextBox свойство Document вообще не изменяется. RichTextBox инициализируется FlowDocument. Этот документ отображается, его можно редактировать и искажать разными способами, но базовое значение свойства Document остается таким, что один экземпляр FlowDocument. Поэтому нет причин, по которым это должно быть свойство зависимостей, т. Е. Bindable. Если у вас несколько местоположений, ссылающихся на этот FlowDocument, вам нужна только одна ссылка. Поскольку это тот же самый экземпляр везде, изменения будут доступны для всех.

Я не думаю, что FlowDocument поддерживает уведомления об изменении документа, хотя я не уверен.

Это, как говорится, вот решение. Прежде чем вы начнете, поскольку RichTextBox не реализует INotifyPropertyChanged, а Document не является свойством зависимости, у нас нет уведомлений, когда свойство документа RichTextBox изменяется, поэтому привязка может быть только OneWay.

Создайте class, который предоставит FlowDocument. Связывание требует наличия свойства зависимостей, поэтому этот class наследуется от DependencyObject.

 class HasDocument : DependencyObject { public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register("Document", typeof(FlowDocument), typeof(HasDocument), new PropertyMetadata(new PropertyChangedCallback(DocumentChanged))); private static void DocumentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { Debug.WriteLine("Document has changed"); } public FlowDocument Document { get { return GetValue(DocumentProperty) as FlowDocument; } set { SetValue(DocumentProperty, value); } } } 

Создайте окно с богатым текстовым полем в XAML.

      

Дайте окну поле типа HasDocument.

 HasDocument hasDocument; 

Конструктор windows должен создать привязку.

 hasDocument = new HasDocument(); InitializeComponent(); Binding b = new Binding("Document"); b.Source = richTextBox; b.Mode = BindingMode.OneWay; BindingOperations.SetBinding(hasDocument, HasDocument.DocumentProperty, b); 

Если вы хотите объявить привязку в XAML, вам нужно сделать свой class HasDocument из FrameworkElement, чтобы его можно было вставить в логическое дерево.

Теперь, если вы изменили свойство Document на HasDocument, документ документа с расширенным текстовым полем также изменится.

 FlowDocument d = new FlowDocument(); Paragraph g = new Paragraph(); Run a = new Run(); a.Text = "I showed this using a binding"; g.Inlines.Add(a); d.Blocks.Add(g); hasDocument.Document = d; 

Я немного настроил предыдущий код. Прежде всего range.Changed не работает для меня. После того, как я изменил range.Changed на richTextBox.TextChanged, оказывается, что обработчик события TextChanged может рекурсивно вызывать SetDocumentXaml, поэтому я предоставил ему защиту. Я также использовал XamlReader / XamlWriter вместо TextRange.

 public class RichTextBoxHelper : DependencyObject { private static HashSet _recursionProtection = new HashSet(); public static string GetDocumentXaml(DependencyObject obj) { return (string)obj.GetValue(DocumentXamlProperty); } public static void SetDocumentXaml(DependencyObject obj, string value) { _recursionProtection.Add(Thread.CurrentThread); obj.SetValue(DocumentXamlProperty, value); _recursionProtection.Remove(Thread.CurrentThread); } public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached( "DocumentXaml", typeof(string), typeof(RichTextBoxHelper), new FrameworkPropertyMetadata( "", FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (obj, e) => { if (_recursionProtection.Contains(Thread.CurrentThread)) return; var richTextBox = (RichTextBox)obj; // Parse the XAML to a document (or use XamlReader.Parse()) try { var stream = new MemoryStream(Encoding.UTF8.GetBytes(GetDocumentXaml(richTextBox))); var doc = (FlowDocument)XamlReader.Load(stream); // Set the document richTextBox.Document = doc; } catch (Exception) { richTextBox.Document = new FlowDocument(); } // When the document changes update the source richTextBox.TextChanged += (obj2, e2) => { RichTextBox richTextBox2 = obj2 as RichTextBox; if (richTextBox2 != null) { SetDocumentXaml(richTextBox, XamlWriter.Save(richTextBox2.Document)); } }; } ) ); } 

Создайте UserControl с RichTextBox. Теперь добавьте следующее свойство зависимостей:

  public FlowDocument Document { get { return (FlowDocument)GetValue(DocumentProperty); } set { SetValue(DocumentProperty, value); } } public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register("Document", typeof(FlowDocument), typeof(RichTextBoxControl), new PropertyMetadata(OnDocumentChanged)); private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RichTextBoxControl control = (RichTextBoxControl) d; if (e.NewValue == null) control.RTB.Document = new FlowDocument(); //Document is not amused by null :) control.RTB.Document = document; } 

Это решение, вероятно, это «прокси-решение», которое вы где-то видели. Однако … RichTextBox просто не имеет документа как DependencyProperty … Поэтому вам нужно сделать это по-другому …

НТН

Почему бы просто не использовать FlowDocumentScrollViewer?

         

Это кажется самым простым способом и не отображается ни в одном из этих ответов.

В модели просмотра просто есть переменная Text .

Вот версия VB.Net ответа Лоло:

 Public Class RichTextBoxHelper Inherits DependencyObject Private Shared _recursionProtection As New HashSet(Of System.Threading.Thread)() Public Shared Function GetDocumentXaml(ByVal depObj As DependencyObject) As String Return DirectCast(depObj.GetValue(DocumentXamlProperty), String) End Function Public Shared Sub SetDocumentXaml(ByVal depObj As DependencyObject, ByVal value As String) _recursionProtection.Add(System.Threading.Thread.CurrentThread) depObj.SetValue(DocumentXamlProperty, value) _recursionProtection.Remove(System.Threading.Thread.CurrentThread) End Sub Public Shared ReadOnly DocumentXamlProperty As DependencyProperty = DependencyProperty.RegisterAttached("DocumentXaml", GetType(String), GetType(RichTextBoxHelper), New FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender Or FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, Sub(depObj, e) RegisterIt(depObj, e) End Sub)) Private Shared Sub RegisterIt(ByVal depObj As System.Windows.DependencyObject, ByVal e As System.Windows.DependencyPropertyChangedEventArgs) If _recursionProtection.Contains(System.Threading.Thread.CurrentThread) Then Return End If Dim rtb As RichTextBox = DirectCast(depObj, RichTextBox) Try rtb.Document = Markup.XamlReader.Parse(GetDocumentXaml(rtb)) Catch rtb.Document = New FlowDocument() End Try ' When the document changes update the source AddHandler rtb.TextChanged, AddressOf TextChanged End Sub Private Shared Sub TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs) Dim rtb As RichTextBox = TryCast(sender, RichTextBox) If rtb IsNot Nothing Then SetDocumentXaml(sender, Markup.XamlWriter.Save(rtb.Document)) End If End Sub 

Конечный class

Эта версия VB.Net работает для моей ситуации. Я удалил семафор сбора streamов, вместо этого использовал RemoveHandler и AddHandler. Кроме того, поскольку FlowDocument может быть привязан только к одному RichTextBox за один раз, я поставил проверку, что RichTextBox IsLoaded = True. Начнем с того, как я использовал class в приложении MVVM, которое использует ResourceDictionary вместо Window.

  ' Loaded and Unloaded events seems to be the only way to initialize a control created from a Resource Dictionary ' Loading document here because Loaded is the last available event to create a document Private Sub Rtb_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) ' only good place to initialize RichTextBox.Document with DependencyProperty Dim rtb As RichTextBox = DirectCast(sender, RichTextBox) Try rtb.Document = RichTextBoxHelper.GetDocumentXaml(rtb) Catch ex As Exception Debug.WriteLine("Rtb_Loaded: Message:" & ex.Message) End Try End Sub ' Loaded and Unloaded events seems to be the only way to initialize a control created from a Resource Dictionary ' Free document being held by RichTextBox.Document by assigning New FlowDocument to RichTextBox.Document. Otherwise we'll see an of "Document belongs to another RichTextBox" Private Sub Rtb_Unloaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Dim rtb As RichTextBox = DirectCast(sender, RichTextBox) Dim fd As New FlowDocument RichTextBoxHelper.SetDocumentXaml(rtb, fd) Try rtb.Document = fd Catch ex As Exception Debug.WriteLine("PoemDocument.PoemDocumentView.PoemRtb_Unloaded: Message:" & ex.Message) End Try End Sub Public Class RichTextBoxHelper Inherits DependencyObject Public Shared Function GetDocumentXaml(ByVal depObj As DependencyObject) As FlowDocument Return depObj.GetValue(DocumentXamlProperty) End Function Public Shared Sub SetDocumentXaml(ByVal depObj As DependencyObject, ByVal value As FlowDocument) depObj.SetValue(DocumentXamlProperty, value) End Sub Public Shared ReadOnly DocumentXamlProperty As DependencyProperty = DependencyProperty.RegisterAttached("DocumentXaml", GetType(FlowDocument), GetType(RichTextBoxHelper), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender Or FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, Sub(depObj, e) RegisterIt(depObj, e) End Sub)) Private Shared Sub RegisterIt(ByVal depObj As System.Windows.DependencyObject, ByVal e As System.Windows.DependencyPropertyChangedEventArgs) Dim rtb As RichTextBox = DirectCast(depObj, RichTextBox) If rtb.IsLoaded Then RemoveHandler rtb.TextChanged, AddressOf TextChanged Try rtb.Document = GetDocumentXaml(rtb) Catch ex As Exception Debug.WriteLine("RichTextBoxHelper.RegisterIt: ex:" & ex.Message) rtb.Document = New FlowDocument() End Try AddHandler rtb.TextChanged, AddressOf TextChanged Else Debug.WriteLine("RichTextBoxHelper: Unloaded control ignored:" & rtb.Name) End If End Sub ' When a RichTextBox Document changes, update the DependencyProperty so they're in sync. Private Shared Sub TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs) Dim rtb As RichTextBox = TryCast(sender, RichTextBox) If rtb IsNot Nothing Then SetDocumentXaml(sender, rtb.Document) End If End Sub End Class 

Большинство моих потребностей было удовлетворено этим ответом https://stackoverflow.com/a/2989277/3001007 от krzysztof . Но одна проблема с этим кодом (я столкнулся с этим), привязка не будет работать с несколькими элементами управления. Поэтому я изменил _recursionProtection с реализацией на основе Guid . Таким образом, он работает с несколькими элементами управления в том же окне.

  public class RichTextBoxHelper : DependencyObject { private static List _recursionProtection = new List(); public static string GetDocumentXaml(DependencyObject obj) { return (string)obj.GetValue(DocumentXamlProperty); } public static void SetDocumentXaml(DependencyObject obj, string value) { var fw1 = (FrameworkElement)obj; if (fw1.Tag == null || (Guid)fw1.Tag == Guid.Empty) fw1.Tag = Guid.NewGuid(); _recursionProtection.Add((Guid)fw1.Tag); obj.SetValue(DocumentXamlProperty, value); _recursionProtection.Remove((Guid)fw1.Tag); } public static readonly DependencyProperty DocumentXamlProperty = DependencyProperty.RegisterAttached( "DocumentXaml", typeof(string), typeof(RichTextBoxHelper), new FrameworkPropertyMetadata( "", FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (obj, e) => { var richTextBox = (RichTextBox)obj; if (richTextBox.Tag != null && _recursionProtection.Contains((Guid)richTextBox.Tag)) return; // Parse the XAML to a document (or use XamlReader.Parse()) try { string docXaml = GetDocumentXaml(richTextBox); var stream = new MemoryStream(Encoding.UTF8.GetBytes(docXaml)); FlowDocument doc; if (!string.IsNullOrEmpty(docXaml)) { doc = (FlowDocument)XamlReader.Load(stream); } else { doc = new FlowDocument(); } // Set the document richTextBox.Document = doc; } catch (Exception) { richTextBox.Document = new FlowDocument(); } // When the document changes update the source richTextBox.TextChanged += (obj2, e2) => { RichTextBox richTextBox2 = obj2 as RichTextBox; if (richTextBox2 != null) { SetDocumentXaml(richTextBox, XamlWriter.Save(richTextBox2.Document)); } }; } ) ); } 

Для полноты позвольте мне добавить еще несколько строк из оригинального ответа https://stackoverflow.com/a/2641774/3001007 ray-burns . Вот как использовать помощника.

  

Ребята, зачем беспокоиться обо всем. Это прекрасно работает. Не требуется код

        
  • WPF: привязка ContextMenu к команде MVVM
  • Привязка данных Android с использованием тега include
  • WPF привязка данных к интерфейсу, а не к фактическому объекту - возможность литья?
  • Нажатие свойств GUI только для чтения обратно в ViewModel
  • Можно ли связать свойство Canvas's Children в XAML?
  • Похоже, что привязки данных не обновляются
  • WPF привязка к локальной переменной
  • Как я могу привязать данные строк к ListBox в WPF / WP7?
  • Что означает «{Binding Path =.}» В привязке WPF?
  • Каковы различные режимы привязки WPF?
  • Когда следует использовать # и = в элементах управления ASP.NET?
  • Interesting Posts

    Кэширование изображений в Интернете

    Переключение между задачами панели задач последовательно

    Как получить все контакты для Android, но без тех, которые находятся на SIM-карте

    Использование classа приложения Android для сохранения данных

    Как безопасно сохранять имя пользователя / пароль (локально)?

    Компиляция простого парсера с Boost.Spirit

    Преобразование набора данных в XML

    Почему я не могу определить статический метод в интерфейсе Java?

    Скрыть div, если размер экрана меньше определенной ширины

    Ловушка внешней переменной

    Полоса пропускания и перетаскивания пробелов из строки Java

    Как запланировать хранимую процедуру в MySQL

    Как происходит разыменование функционального указателя?

    Конструктор преобразования и оператор преобразования: приоритет

    Что это значит, что автоматически проверяет изменения конфигурации (например, в / etc) в Git?

    Давайте будем гением компьютера.