What is the correct MVVM way to put custom data into RichTextBox

Christopher Bauer 6 Reputation points
2021-03-25T23:21:40.573+00:00

I'm running into massive headaches while trying to create an editor using a RichTextBox.

I have been searching for a week, and all of the answers I have found describe making a custom control RichTextBox, and binding to a "string DocumentXaml" property, or using Xceed's Toolkit that has that ability to bind built in, and then using some text converter to convert the data to a string representation in Xaml and feeding that to the RichTextBox.

The problem with just translating my data into a string is then the RichTextBox loses all context of what the data is. I don't need to just display the data, I need to enforce certain restrictions on the way the text is edited.

Here's a simplified version of my data structures:

class Script {
    public ObservableCollection<Scene> Scenes;
};

class Scene {
    public ObservableCollection<Note> SceneNotes;
    public ObservableCollection<Line> Lines;
};

class Line {
    public ObservableCollection<Note> Notes;
    public string Actor;
    public string Text;
};

class Note {
    public NoteType Type;
    public string Text;
};

In my editor, a line of text is not just in 12 point font and center justified. That block of text is an Actor name, so there are restrictions on what you can do with it. I can't allow the user to change the font or the color, or make it left justified. That text represents an Actor, so it must ALWAYS be center justified in 12 point font. And so on. I need very strict control over what the user can and can't do inside the RichTextBox.

How do I do this? Is this just impossible in MVVM? My View class has the RichTextBox, but it has no idea about the Model. The ViewModel has the data, but I can not find a way to get the ViewModel access to the FlowDocument that lives inside the RichTextBox. What is the proper way to set up my classes so that I have some piece of code that can tightly control what goes into the RichTextBox? Or am I just approaching this problem completely incorrectly?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,669 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,223 questions
0 comments No comments
{count} vote

1 answer

Sort by: Most helpful
  1. DaisyTian-1203 11,616 Reputation points
    2021-03-26T05:58:28.43+00:00

    How about setting the Foreground and FontSize for the FlowDocument in the converter? I make a demo for it.

    <Window.Resources>  
            <local:FlowDocumentToXamlConverter x:Key="flowDocumentConverter"/>  
        </Window.Resources>  
        <Window.DataContext>  
            <local:MainWindowViewModel></local:MainWindowViewModel>  
        </Window.DataContext>  
    <local:MyRichTextBox Grid.Row="0" x:Name="EditBox" Document="{Binding Path=DocumentXaml, Converter={StaticResource flowDocumentConverter}, Mode=TwoWay}"/>  
    

    The code for converter is:

    [ValueConversion(typeof(string), typeof(FlowDocument))]  
        public class FlowDocumentToXamlConverter : IValueConverter  
        {  
            public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)  
            {  
                var flowDocument = new FlowDocument();  
                if (value != null)  
                {  
                    var xamlText = (string)value;  
                    flowDocument = (FlowDocument)XamlReader.Parse(xamlText);  
                }  
                flowDocument.FontSize = 30;  
                flowDocument.FontStyle = FontStyles.Italic;  
                flowDocument.Foreground = Brushes.Red;  
                flowDocument.TextAlignment = TextAlignment.Center;  
                  
                return flowDocument;  
            }  
            public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)  
            {  
                if (value == null) return string.Empty;  
                var flowDocument = (FlowDocument)value;  
                flowDocument.FontSize = 30;  
                flowDocument.FontStyle = FontStyles.Italic;  
                flowDocument.Foreground = Brushes.Red;  
                flowDocument.TextAlignment = TextAlignment.Center;  
                return XamlWriter.Save(flowDocument);  
            }  
        }  
    

    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.