Привязка Richtextbox wpf

Чтобы выполнить привязку данных Document в WPF RichtextBox, я до сих пор видел 2 решения, которые должны быть производными от RichtextBox и добавить DependencyProperty, а также решение с «прокси».

Ни первое, ни второе неудовлетворительны. Кто-нибудь знает другое решение или вместо этого коммерческий элемент управления RTF, который поддерживает DataBinding? Обычный TextBox не альтернатива, так как нам нужно форматирование текста.

Любая идея?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
78
0
109 653
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

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

    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;
        FlowDocument document = e.NewValue as FlowDocument;
        if (document  == null)
        {
            control.RTB.Document = new FlowDocument(); //Document is not amused by null :)
        }
        else
        {
            control.RTB.Document = document;
        }
    }

Это решение, вероятно, является тем "прокси" решением, которое вы где-то видели ... Однако ... RichTextBox просто не имеет Document как DependencyProperty ... Так что вам нужно сделать это по-другому ...

HTH

В последней строке вы используете «документ», который вызывает ошибку в моем коде. Это должен быть экземпляр Document из-за статического метода. Но пример чего? Я устанавливаю документ, который я получаю через DependencyProperty, «Документ». Удаление «статики» нарушает последний аргумент DependencyProperty. Итак, я застрял. Вспомогательный класс сверху тоже не показывает никакого текста :(

ecth 13.05.2016 19:14

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

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

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

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

Создайте класс, который предоставит FlowDocument. Связывание требует наличия DependencyProperty, поэтому этот класс наследуется от 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); }
    }
}

Создайте Window с полем форматированного текста в XAML.

<Window x:Class = "samples.Window1"
    xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
    Title = "Flow Document Binding" Height = "300" Width = "300"
    >
    <Grid>
      <RichTextBox Name = "richTextBox" />
    </Grid>
</Window>

Дайте Window поле типа HasDocument.

HasDocument hasDocument;

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

hasDocument = new HasDocument();

InitializeComponent();

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

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

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

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;

+1 за хороший ответ, но одна придирка: есть причина сделать свойство Document свойством зависимости - для облегчения использования элемента управления с шаблоном MVVM.

David Veeneman 26.07.2010 19:10

Справедливо, но я не согласен; Тот факт, что MVVM широко используется в приложениях WPF, не означает, что API WPF следует изменять только для того, чтобы приспособиться к нему. Мы работаем над этим любым возможным способом. Это одно из решений. Мы также можем просто инкапсулировать наше текстовое поле Rich Text Box в пользовательский элемент управления и иметь свойство зависимости, определенное в UserControl.

Szymon Rozga 30.07.2010 18:01
Ответ принят как подходящий

Есть способ намного проще!

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

<TextBox Text = "{Binding FirstName}" />
<TextBox Text = "{Binding LastName}" />
<RichTextBox local:RichTextBoxHelper.DocumentXaml = "{Binding 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[] вместо string.

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

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

Если у вас есть несколько RichTextBox в одном окне, это решение не сработает.

Kelly 28.07.2010 00:53

@Kelly, используйте DataFormats.Rtf, это может решить проблему с множеством richtextboxes.

CharlieShi 24.10.2012 06:36

Двусторонний у меня не работает (с использованием Rtf). Событие range.Changed никогда не вызывается.

Patrick 27.03.2013 19:33

@FabianBigler - Привет - на случай, если у кого-то возникнет такая же проблема - вам нужно добавить объявление xmlns: local в ваш файл xaml, который будет указывать на пространство имен, где это доступно

Bartosz 19.03.2015 22:40

может кто-нибудь привести пример ценности Автобиографии?

longlostbro 15.03.2017 22:37

@longlostbro, вот это пример значения: gist.github.com/bakulev/614a665241cc4fd69409e1ab3e03a9ba

Anton Bakulev 19.11.2018 16:17

@AntonBakulev Спасибо!

longlostbro 17.02.2019 02:59

Строка поддержки (Autobiography) в виртуальной машине должна быть FlowDocument в формате xml. Например: <FlowDocument xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presenta‌​tion"><Paragraph Foreground = "Red"><Bold>Hello</Bold></Paragraph></FlowDocumen‌​t>

Istvan Heckl 17.12.2020 17:35

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

public class RichTextBoxHelper : DependencyObject
{
    private static HashSet<Thread> _recursionProtection = new HashSet<Thread>();

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

Спасибо, Лоло! У меня тоже были проблемы с исходным классом. Это исправило это для меня. Огромная экономия времени!

Mark Bonafe 27.01.2015 00:54

Я обнаружил небольшую проблему с этим решением. Можно установить ловушку для TextChanged несколько раз, если представление не закрывается и не создается заново между вызовами. Я создаю единое представление один раз и загружаю его через выбор списка. Чтобы исправить это, я создал более типичный метод перехвата события TextChanged. Затем я просто отцепляю метод, прежде чем подключать его. Это гарантирует, что он будет зацеплен только один раз. Больше никаких утечек памяти (и больше никакого медленно работающего кода).

Mark Bonafe 27.01.2015 01:41

Это хорошее рабочее решение, однако, по моему опыту, оно не работает с несколькими элементами управления, см. Мой отвечать.

Ajeeb.K.P 21.02.2018 18:37

Это здорово, спасибо. Я предполагаю, что не работает, если вы хотите установить привязку программно, потому что поток, устанавливающий привязку, отличается от того, который устанавливается через XAML. Поэтому мне пришлось добавить метод SetDocumentXamlFirst, который не использует защиту от рекурсии и будет вызываться только вручную, когда вы впервые захотите установить значение.

stuzor 05.07.2019 11:10

Строка поддержки в виртуальной машине должна быть FlowDocument в формате xml, иначе загрузка не удастся. Например: <FlowDocument xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presenta‌​tion"><Paragraph Foreground = "Red"><Bold>Hello</Bold></Paragraph></FlowDocumen‌​t>

Istvan Heckl 17.12.2020 17:36

Вот версия ответа Лоло на 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

Конец класса

Эта версия VB.Net работает в моей ситуации. Я удалил семафор коллекции потоков, вместо этого используя RemoveHandler и AddHandler. Кроме того, поскольку FlowDocument может быть привязан только к одному RichTextBox за раз, я проверил, что IsLoaded = True для RichTextBox. Начнем с того, как я использовал класс в приложении 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

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

RichTextBox из Extended WPF Toolkit очень медленный, я бы не рекомендовал его.

Kapitán Mlíko 20.03.2013 14:55

@ViktorLaCroix, вы же понимаете, что это всего лишь WPF RichTextBox с дополнительным свойством, верно?

user288295 13.06.2014 21:51

(переходите к 2017 году ...) Наборы инструментов wpf RichTextBox сразу после установки работает с форматированным или обычным текстом. Это также кажется намного быстрее, чем использование вспомогательного метода ниже (который вызывает исключение, если вы просто скопируете / вставите его)

DanW 09.10.2017 16:32

Их бесплатная лицензия предназначена только для некоммерческого использования. : /

Eric 14.02.2020 06:10

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

Это настоящий ответ.

Den 25.05.2016 13:39

Вы не можете редактировать текст в FlowDocumentScrollViewer

Eric 14.02.2020 06:13

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

<RichTextBox>
    <FlowDocument>
        <Paragraph>
            <Run Text = "{Binding Mytextbinding}"/>
        </Paragraph>
    </FlowDocument>
</RichTextBox>

В моем случае это не работает без тега FlowDocument.

Klaonis 08.12.2014 16:12

Свойство Text для Run не является свойством зависимости, поэтому оно даже не компилируется. Только свойства зависимости поддерживают такую ​​привязку.

Hopeless 25.08.2015 13:17

Это было именно то, что мне нужно.

Christopher Painter 13.02.2019 19:50
 <RichTextBox>
     <FlowDocument PageHeight = "180">
         <Paragraph>
             <Run Text = "{Binding Text, Mode=TwoWay}"/>
          </Paragraph>
     </FlowDocument>
 </RichTextBox>

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

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

В моем случае это решение показывает текст свойства Text в модели представления по вертикали, то есть по одному для каждой строки.

kintela 22.11.2018 16:38

Это все, что мне нужно. Спасибо!

Christopher Painter 13.02.2019 19:50

Чем это решение отличается от обычного TextBox, привязанного к свойству Text? Это противоречит цели поддержки форматирования поля форматированного текста, которое эффективно отключается с помощью этого кода.

Daap 03.09.2019 10:04

Это было идеально для меня. Чем проще, тем лучше!

gcdev 17.12.2019 18:01

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

 public class RichTextBoxHelper : DependencyObject
    {
        private static List<Guid> _recursionProtection = new List<Guid>();

        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 от лучевые ожоги. Вот как использовать помощник.

<RichTextBox local:RichTextBoxHelper.DocumentXaml = "{Binding Autobiography}" />

Вот мое решение, основанное на ответе Рэя Бернса с DataBinding и преобразованием XAML-строки в RichTextBox-Document:

ViewModel

    TestText = @"<FlowDocument xmlns = ""http://schemas.microsoft.com/winfx/2006/xaml/presentation""><Paragraph><Bold>Hello World!</Bold></Paragraph></FlowDocument>";

Вид

<RichTextBox local:RichTextBoxHelper.DocumentXaml = "{Binding TestText}"/>

RichTextBoxHelper

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;
                var    xaml        = GetDocumentXaml(richTextBox);
                Stream sm          = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
                richTextBox.Document = (FlowDocument) XamlReader.Load(sm);
                sm.Close();
            }
        }
    );
}

Другие вопросы по теме