# MVVM Integration Patterns

> Build an MVVM window around the WPF TextControl: a view model with the document, ZoomFactor binding, RelayCommand, an editor interface and ServerTextControl.

- **Product:** TX Text Control .NET for WPF
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for WPF > Integration Guide > MVVM Integration Patterns
- **5598 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/mvvm/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/mvvm/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/mvvm/llms-full.txt

---

In a Model-View-ViewModel (MVVM) application, the window binds to a view model that holds the data and the commands, and the view model knows nothing about controls. A word processor fits this pattern with a clear division: the `TextControl` stays in the view, while the document, the settings and the commands live in the view model. The samples of Text Control are written with code-behind, so no ready-made MVVM sample exists; the example below assembles one from bindings, an attached property and a small interface, and it compiles against version 34.0.

### Who Does What

| Part | Holds | Talks to the editor through |
|---|---|---|
| View model | The document as data, settings such as the zoom factor, the commands | Bindings, and an interface for actions that need the control |
| View (*MainWindow.xaml* and its code-behind) | The `TextControl`, its bars and the other controls | Its fields, for example `textControl1` |
| `ServerTextControl` | A document in memory, without user interface | Nothing: the view model uses it directly |

Three kinds of connections carry the traffic between view model and view:

- **Bindings to dependency properties** such as [ZoomFactor](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/zoomfactor-property/llms-full.txt) and [ViewMode](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/viewmode-property/llms-full.txt), in both directions.
- **An attached property for the document,** which loads the bound byte array into the editor and writes the edited document back. [Binding Document Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/data-binding/document-content/#bind-the-whole-document) builds it; this page uses it unchanged.
- **Code-behind** for everything else: properties that aren't dependency properties, such as [EditMode](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/editmode-property/llms-full.txt), and actions that need the control, such as printing or opening a built-in dialog box.

Toolbar controls that follow the formatting at the input position bind to [InputFormat](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/inputformat/llms-full.txt) with `ElementName` and don't involve the view model at all.

### Write the View Model

The view model uses a minimal `ICommand` implementation. Add it as *RelayCommand.cs*:

```csharp
using System;
using System.Windows.Input;

namespace MyWPFApp
{
    public class RelayCommand : ICommand
    {
        private readonly Action execute;
        private readonly Func<bool> canExecute;

        public RelayCommand(Action execute, Func<bool> canExecute = null)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }

        // Let WPF ask again whenever the input changes
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object parameter)
        {
            return canExecute == null || canExecute();
        }

        public void Execute(object parameter)
        {
            execute();
        }
    }
}
```

Actions that only the control can carry out are described by an interface, *IEditorService.cs*, which the window implements:

```csharp
namespace MyWPFApp
{
    // What the view model may ask of the editor, without knowing the control
    public interface IEditorService
    {
        void Print();
        void ShowFindDialog();
    }
}
```

The view model itself, *EditorViewModel.cs*, keeps the document in a file below the local application data folder; replace the two file calls with the database or service of your application:

```csharp
using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Input;
using TXTextControl;

namespace MyWPFApp
{
    public class EditorViewModel : INotifyPropertyChanged
    {
        private readonly IEditorService editor;
        private readonly string storePath = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "letter.tx");

        private byte[] document;
        private int zoomFactor = 100;
        private bool isReadOnly;
        private bool isDirty;

        public EditorViewModel(IEditorService editor)
        {
            this.editor = editor;

            NewCommand = new RelayCommand(() => { Document = null; IsDirty = false; });
            SaveCommand = new RelayCommand(Save, () => IsDirty);
            ExportPdfCommand = new RelayCommand(ExportPdf, () => document != null);
            PrintCommand = new RelayCommand(() => editor.Print());
            FindCommand = new RelayCommand(() => editor.ShowFindDialog());

            if (File.Exists(storePath))
            {
                document = File.ReadAllBytes(storePath);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public ICommand NewCommand { get; }
        public ICommand SaveCommand { get; }
        public ICommand ExportPdfCommand { get; }
        public ICommand PrintCommand { get; }
        public ICommand FindCommand { get; }

        // The document in the TX Text Control format, bound to the editor
        public byte[] Document
        {
            get { return document; }
            set
            {
                document = value;
                IsDirty = true;
                OnPropertyChanged(nameof(Document));
            }
        }

        // Bound to the ZoomFactor dependency property
        public int ZoomFactor
        {
            get { return zoomFactor; }
            set { zoomFactor = value; OnPropertyChanged(nameof(ZoomFactor)); }
        }

        // Applied to EditMode by the view
        public bool IsReadOnly
        {
            get { return isReadOnly; }
            set { isReadOnly = value; OnPropertyChanged(nameof(IsReadOnly)); }
        }

        public bool IsDirty
        {
            get { return isDirty; }
            private set { isDirty = value; OnPropertyChanged(nameof(IsDirty)); }
        }

        private void Save()
        {
            File.WriteAllBytes(storePath, document ?? Array.Empty<byte>());
            IsDirty = false;
        }

        private void ExportPdf()
        {
            byte[] pdf;

            // Convert without the editor: ServerTextControl has no user interface
            using (ServerTextControl tx = new ServerTextControl())
            {
                tx.Create();
                tx.Load(document, BinaryStreamType.InternalUnicodeFormat);
                tx.Save(out pdf, BinaryStreamType.AdobePDF);
            }

            File.WriteAllBytes(Path.ChangeExtension(storePath, ".pdf"), pdf);
        }

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
```

The view model works as follows:

- `Document` holds the document as a byte array in the TX Text Control format. The attached property sets it whenever the user changes the document, and the setter marks the document as changed, which enables the **Save** command.
- `ZoomFactor` is an ordinary property; the view binds the dependency property of the same name to it.
- `ExportPdf` converts the document with a [ServerTextControl](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/llms-full.txt), the document engine without user interface, which is part of the WPF package. [Create](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/create-method/llms-full.txt) comes first; [Load](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/load-method/llms-full.txt) and [Save](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/servertextcontrol/save-method/llms-full.txt) with [BinaryStreamType](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol/binarystreamtype/enumeration/llms-full.txt) `AdobePDF` convert. PDF export requires the Professional or Enterprise edition; see [Editions and Feature Availability](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/overview/editions/llms-full.txt).
- `Print` and `Find` go through `IEditorService`, so the view model calls the editor without a reference to the control, and a unit test can pass in a fake implementation.

### Write the View

*MainWindow.xaml* binds the toolbar, the slider and the editor to the view model:

```xml
<Window x:Class="MyWPFApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:WPF="clr-namespace:TXTextControl.WPF;assembly=TXTextControl.WPF"
        xmlns:local="clr-namespace:MyWPFApp"
        Title="MainWindow" Height="450" Width="800">
    <Window.InputBindings>
        <KeyBinding Key="S" Modifiers="Control" Command="{Binding SaveCommand}" />
    </Window.InputBindings>
    <DockPanel>
        <ToolBar DockPanel.Dock="Top">
            <Button Content="New" Command="{Binding NewCommand}" />
            <Button Content="Save" Command="{Binding SaveCommand}" />
            <Button Content="Export PDF" Command="{Binding ExportPdfCommand}" />
            <Button Content="Print" Command="{Binding PrintCommand}" />
            <Button Content="Find" Command="{Binding FindCommand}" />
            <CheckBox Content="Read only" IsChecked="{Binding IsReadOnly}" />
            <ToggleButton Content="B" Focusable="False"
                          IsChecked="{Binding ElementName=textControl1, Path=InputFormat.Bold, Mode=TwoWay}" />
        </ToolBar>
        <Slider DockPanel.Dock="Bottom" Minimum="50" Maximum="200"
                Value="{Binding ZoomFactor}" />
        <WPF:StatusBar x:Name="statusBar1" DockPanel.Dock="Bottom" />
        <WPF:TextControl x:Name="textControl1"
                         StatusBar="statusBar1"
                         ZoomFactor="{Binding ZoomFactor, Mode=TwoWay}"
                         local:DocumentContent.Data="{Binding Document}" />
    </DockPanel>
</Window>
```

The code-behind in *MainWindow.xaml.cs* creates the view model, implements `IEditorService` and applies the read-only setting:

```csharp
using System.ComponentModel;
using System.Windows;
using TXTextControl;

namespace MyWPFApp
{
    public partial class MainWindow : Window, IEditorService
    {
        private readonly EditorViewModel viewModel;

        public MainWindow()
        {
            InitializeComponent();

            viewModel = new EditorViewModel(this);
            viewModel.PropertyChanged += ViewModel_PropertyChanged;
            DataContext = viewModel;
        }

        private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // EditMode is not a dependency property: apply it here
            if (e.PropertyName == nameof(EditorViewModel.IsReadOnly))
            {
                textControl1.EditMode = viewModel.IsReadOnly ? EditMode.ReadAndSelect : EditMode.Edit;
            }
        }

        public void Print()
        {
            textControl1.Print("Letter", true);
        }

        public void ShowFindDialog()
        {
            textControl1.Find();
        }
    }
}
```

The view works as follows:

- `ZoomFactor="{Binding ZoomFactor, Mode=TwoWay}"` sets the zoom of the editor from the view model; the slider changes the view model, and the editor follows.
- `local:DocumentContent.Data="{Binding Document}"` is the attached property of [Binding Document Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/data-binding/document-content/#bind-the-whole-document). Add its class, *DocumentContent.cs*, to the project.
- `StatusBar="statusBar1"` connects the status bar by name. Such connections are strings in XAML and not part of the view model.
- `EditMode` isn't a dependency property, so a binding can't set it. The view listens to `PropertyChanged` of the view model and sets [EditMode](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/editmode-property/llms-full.txt) itself: `ReadAndSelect` displays the document and lets the user select text, with editing restricted as the document permissions specify; `Edit` allows every change.
- [Print](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/print-method/llms-full.txt) opens the print dialog box and prints; `true` starts the dialog box with the settings of the previous call. [Find](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/find-method/llms-full.txt) opens the built-in Find dialog box.

Run the application, type a few words and click **Save**: the button is disabled again until the next change. Close and start the application, and the saved letter appears in the editor.

### Where Code-Behind Remains

Some work stays in the view because it needs the control or its events:

- **Built-in dialog boxes,** such as [Load](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/load-method/llms-full.txt) without parameters or [FontDialog](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/textcontrol/fontdialog-method/llms-full.txt), belong to the editor. Add a method to `IEditorService` for each one the view model needs. Inside a `Window`, compare their result with `TXTextControl.WPF.DialogResult.OK`, written in full, because `DialogResult` alone is the property of the window.
- **Events of the editor** other than `Changed` reach the view model through the code-behind, which calls a method or sets a property of the view model.
- **The ribbon, the rulers and the status bar** are connected to the editor by name in XAML, as in [Create a WPF Ribbon Application](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/getting-started/document-editor/ribbon/llms-full.txt).

Keep this code-behind thin: it forwards to the view model or to the control, and holds no decisions of its own.

### Next Steps

- [Commands and Input Bindings](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/commands/llms-full.txt): routed commands and key bindings for the editor.
- [Binding Document Content](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/data-binding/document-content/llms-full.txt): the attached property and the bindings to `InputFormat`.
- [ServerTextControl](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/overview/components/servertextcontrol/llms-full.txt): the document engine without user interface.
- [MVVM Integration Examples](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/examples/mvvm/llms-full.txt): the sources and samples to build on.

---

## Nearby

- **Previous:** [Value Converters](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/data-binding/value-converters/llms-full.txt)
- **Next:** [Commands and Input Bindings](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/integration/commands/llms-full.txt)
