Resolved Call Control.Method from Page(View) in MainViewModel

Tewl

Member
Joined
Jan 13, 2022
Messages
8
Programming Experience
5-10
I have recently started moving from WinForms to WPF and I have been trying to learn MVVM practices. At the moment I am converting an old project for that helps me monitor server activity.

I have multiple views bound to a content control on my mainwindow that are toggled through with radio buttons. In one of these views (LogView), I have a custom richtextbox that formats, colors, and appends text via an AppendText method.

I need to be able to call append method when an event fires on my mainviewmodel. My initial thought was to create a dependency property in the rtb then bind to it as just call the method in the property declaration.
ExRichTextBox on LogView:
public partial class ExRichTextBox : RichTextBox
{
    public static readonly DependencyProperty AppendMessageProperty =
           DependencyProperty.Register("AppendMessage", typeof(string),
                typeof(ExRichTextBox), new PropertyMetadata(string.Empty));

    public string AppendMessage
        {
        get { return (string)GetValue(AppendMessageProperty); }
        set {
            SetValue(AppendMessageProperty, value);
            AppendText(value);
        }
    }

LogView Xaml:
<controls:ExRichTextBox x:Name="LogWindow"
                                Margin="0,0,0,5"
                                IsReadOnly="True"
                                Padding="1,1,1,1"
                                Background="Transparent"
                                BorderThickness="0"
                                VerticalScrollBarVisibility="Auto"
                                AppendMessage="{Binding LastMessage, Mode=OneWay}" />

MainViewModel:
private string _lastmsg = string.Empty;
public string LastMessage
{
    get { return _lastmsg; }
    set
    {
        _lastmsg = value;
        OnPropertyChanged();
    }
}
// Generic event
private void OnDataArrival(string data)
{
    LastMessage = data;
}

I don't know if I am going about this the right way or not I am still new at this. No text is being displayed and I am not getting any errors and I know the connection is being made.

Most of the examples/videos I find on the topic only talk about binding properties or lists and usually fire based on some input from the user rather than some automated input.

Thanks for any help!
 
As a rule of thumb in WPF using MVVM, if you are talking to the UI directly, you are probably doing something wrong (or you are writing a brand new custom view control and you are writing purely view code). What you should be doing is not appending text to the rich text box, but rather appending text to the model that holds the text being exposed by the view model. The rich text box should just bind to that property exposed by the view model. When the view model notifies the view that the view model's property's in underlying data has been updated, the view will know that it need to update the UI.
 
The rich text box should just bind to that property exposed by the view model.
I thought that was what I was doing by creating a dependency property and letting the richtextbox append the message internally and not actually in the view code.

Sorry, having a hard time wrapping my head around this after doing nothing but WinForms for so long.
 
In the MVVM world, when a property is bound to the UI, the UI shows all the data in the property. Your property only has the last thing that you appended to the rich text box. Also consider what happens if a refresh is forced on the rich text box. Will the value be appended again?

The right way would be to bind the Document to a FlowDocument.

I do see the quandary that you are in. It's easy to send a short string and just have it appended. It's much harder to manipulate a flow document and keep it up to date.

I guess now it's time to do a lot of debugging to see why nothing is happening. Have you set breakpoints to see what gets called, and what doesn't get called?
 
I did end up looking into binding the document but still needed to create dependency in order to do this. I found a few threads on stackoverflow that led me to trying this

RichTextHelper:
    public class RichTextBoxHelper : DependencyObject
    {
        public static FlowDocument GetDocumentRtf(DependencyObject obj)
        {
            return (FlowDocument)obj.GetValue(DocumentRtfProperty);
        }
        public static void SetDocumentRtf(DependencyObject obj, FlowDocument value)
        {
            obj.SetValue(DocumentRtfProperty, value);
        }
        public static readonly DependencyProperty DocumentRtfProperty =
          DependencyProperty.RegisterAttached(
            "DocumentRtf",
            typeof(FlowDocument),
            typeof(RichTextBoxHelper),
            new FrameworkPropertyMetadata
            {
                BindsTwoWayByDefault = true,
                PropertyChangedCallback = (obj, e) =>
                {
                    var richTextBox = (ExRichTextBox)obj;
                    if ((richTextBox != null) && (e.NewValue != null))
                        richTextBox.Document = e.NewValue as FlowDocument;
                }
            });
    }

I ran into a few errors when switching views, I had to set and unset the binding for the document on load/unload because of an error 'Document already belongs to another RichTextBox'

At the moment, fingers crossed, everything seems functional. I know it is not perfect but it is just a personal project and practice with WPF and MVVM.

Thanks again for the advice.
 
A great guide for getting in the "zen" of MVVM is Josh Smith's blog as well as his book.
 
Back
Top Bottom