2
votes

I am trying to make a RichTextBox with a FlowDocument that I can insert text at the caret position. I can add text to the end of the document. I think I am missing something in my setup to that allows my VM to access the Flowocument or I am setting it up wrong. If I create a FlowDocument in my VM and try to set my RichTextBox to it I get an error that my MyEditor (RichTextBox) does not exist. I can add text to the RichTextBox using what I call the AddItemBtn from a ListBox so at least that much works.

My question is "How should I set my RichTextBox/FlowDocument up?

XAML code

<Window x:Class="Scripter.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Scripter.ViewModels"
    xmlns:wpftoolkit="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
    Title="MainWindow" Height="350" Width="725">


<Grid HorizontalAlignment="Stretch">

    <Grid HorizontalAlignment="Stretch" Height="72" Margin="10,14,0,0" VerticalAlignment="Top" Width="auto">
        <WrapPanel HorizontalAlignment="Left" Height="50" Margin="10,0,0,0" VerticalAlignment="Top">


        </WrapPanel>
        <Button x:Name="OpenFilesBtn" Content="Open" HorizontalAlignment="Left" Margin="15,10,0,0" VerticalAlignment="Top" Width="75" Command="{Binding OpenFileBtn}"/>
        <Button x:Name="SavefilesBtn" Content="Save" HorizontalAlignment="Left" Margin="104,10,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveFileBtn}"/>
        <TextBlock x:Name="OpenFile" Text="{Binding OpenFile,Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="15,37,0,0" VerticalAlignment="Top" Width="353"/>
        <ComboBox x:Name="TipsBtn" SelectedIndex="0" ItemsSource="{Binding Path=Tabs, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Path=SelectedOption}"  HorizontalAlignment="Left" Margin="538,10,0,0" VerticalAlignment="Top" Width="120"/>
        <Button x:Name="AddItemBtn" Content="Add Item" HorizontalAlignment="Left" Margin="417,10,0,0" VerticalAlignment="Top" Width="100" Command="{Binding AddItemBtn}" CommandParameter="{Binding ElementName=AddItemList,Path=SelectedItem}"/>

    </Grid>
    <Grid Margin="10,100,10,0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <RichTextBox  Grid.Column="0" x:Name="MyEditor" SelectionChanged="MyEditor_SelectionChanged" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0" Height="Auto" HorizontalAlignment="Stretch"  VerticalAlignment="Stretch" Width="Auto" IsDocumentEnabled="True" AcceptsTab="True" AcceptsReturn="True" >
            <RichTextBox.Resources>
                <Style TargetType="{x:Type Paragraph}">
                    <Setter Property="Margin" Value="0" ></Setter>
                    <Setter Property="FontSize" Value="15"></Setter>
                </Style>
            </RichTextBox.Resources>
            <FlowDocument >
                <Paragraph >
                    <Run Text="{Binding TestText}" ></Run>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
        <ListBox x:Name="AddItemList" Grid.Column="1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=OptionsToChoose}" SelectedItem="ItemSelected">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock x:Name="TextSelected" Text="{Binding Description}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

    </Grid>
</Grid>

VM code that has the issue

public ScripterViewModel()
    {
        ScripterModel scripterModel = new ScripterModel();

        ObservableCollection<string> tabsChoice = new ObservableCollection<string>();
        tabsChoice.Add("Tabs");
        tabsChoice.Add("Buttons");
        Tabs = tabsChoice;

        this.OpenFileBtn = new DelegateCommand(chooseFile, canChooseFile).ObservesProperty(() => OpenFile);
        this.SaveFileBtn = new DelegateCommand(saveFile, canSaveFile).ObservesProperty(() => SaveFile);
        this.AddItemBtn = new DelegateCommand<Tabbed>(addItem);

        FlowDocument flowDoc = new FlowDocument();

        Paragraph p = new Paragraph(new Run("new paragraph"));
        flowDoc.Blocks.Add(new Paragraph(new Run("Paragraph 1")));
        flowDoc.Blocks.Add(p);

        //MyEditor = flowDoc;

    }

    public void MyEditor_SelectionChanged(object sender, RoutedEventArgs e)
    {
      // TextRange tempRange = new TextRange(MyEditor.Document.ContentStart, MyEditor.Selection.Start);
        MessageBox.Show("Selection Changed");

    }

    private string _testText;
    public string TestText
    {
        get
        {
            return _testText;
        }

        set
        {
            string _temp;
            _temp = _testText + value;
            SetProperty(ref _testText, value);
        }

    }
1

1 Answers

1
votes

Hey I am new to WPF and MVVM but I'll give my best to help you. So don't blame me if I'm wrong.

1. Set Window.DataContext

First of all you have to tell your View where it can get the data from. This can be done by adding this code to your View.xaml:

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

But make sure your namespace variable (here "local") points to your ViewModels.

xmlns:local="clr-namespace:Client.ViewModel"

This for ex. points to my ViewModel folder.

2. Define a OnPropertyChanged method

Your View won't know if you have modified a variable. So you need a method to notify your View about the changes. First of all implement the interface INotifyPropertyChanged to your ViewModel.

public class MainViewModel : ViewModelBase, INotifyPropertyChanged

Now add this code:

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

3. Use OnPropertyChanged

So now you have a method to tell your View that a variable has changed but how do you use it? To explain this to you I'll use your FlowDocument flowDoc.

So let's begin by defining your FlowDocument setting up :

private FlowDocument _flowDoc;
    

Now lets write a getter & setter for flowDoc:

    public FlowDocument FlowDoc
    {
        get
        {
            return _flowDoc;
        }
        set
        {
           _flowDoc = value;
        }
    }

Now it's time to use our OnPropertyChanged method which we created in 2. In the setter section you want to add the following Code:

OnPropertyChanged("variable");

Your result should now look like this:

public FlowDocument FlowDoc
    {
        get
        {
            return _flowDoc;
        }
        set
        {
           _flowDoc = value;
           OnPropertyChanged("FlowDoc");
        }
    }

Important: remember to apply this to all your variables!

4. Use MVVM pattern right

In MVVM you have a Model a View and a ViewModel. The Model is for your data so if possible don't store data in your ViewModel instead use a data class for ex.

You may have a look at this and/or this.


As I said in the beginning I'm new to all of this but i hope it helps you. Feel free to ask.