Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

AvalonDock [2.0] Tutorial Part 4 - Integrating AvalonEdit Options

0.00/5 (No votes)
31 Jan 2014 1  
Integrate AvalonEdit with text editing options into AvalonDock [2.0]

Introduction 

The AvalonDock [2.0] series of articles continues and you are reading part 4. In this installment we are going to talk about using text related options in AvalonEdit. This includes the usage of WordWrap and the display of line numbers, -and continues to show how AvalonEdit can be setup toward text style rules, such as, using or not using spaces, tabs, their size etc.

 

Prerequisite  

Be sure to download and continue the sample from the previous tutorial's step 3 [1] if you want to follow this article as I talk us through it.

Word Wrap and Line Number Indicator

In this section we are going to explore how word wrap and the display of line numbers can be switched on and off in AvalonEdit. This switching is done via a command handler that will accept five different parameters defined through an enumeration and we use CLR properties to give the view a chance for displaying the current state of each setting. We adjust our document viewmodel with new properties first, add a new command, and bind to the new properties later on. 

Add a WordWrap and ShowLineNumbers boolean property into the FileViewModel class (I am showing only the WordWrap since ShowLineNumbers is very similar) 

#region WordWrap
// Toggle state WordWrap
private bool mWordWrap = false;
public bool WordWrap
{
  get
  {
    return this.mWordWrap;
  }

  set
  {
    if (this.mWordWrap != value)
    {
      this.mWordWrap = value;
      this.RaisePropertyChanged("WordWrap");
    }
  }
}
#endregion WordWrap

Add a WordWrap and ShowLineNumbers toggle command in the Workspace class. This command accepts a ToggleEditorOption enumeration that is also defined in the namespace of the Workspace class. The command will be extended further below in this article. So, here is the enumeration: 

public enum ToggleEditorOption
{
  WordWrap = 0,
  ShowLineNumber = 1,
  ShowEndOfLine = 2,
  ShowSpaces = 3,
  ShowTabs = 4
}

And here is the code for the new command:

#region ToggleEditorOptionCommand
RelayCommand _toggleEditorOptionCommand = null;
public ICommand ToggleEditorOptionCommand
{
  get
  {
    if (this._toggleEditorOptionCommand == null)
    {
      this._toggleEditorOptionCommand = new RelayCommand((p) => OnToggleEditorOption(p),
                                                         (p) => CanToggleEditorOption(p));
    }

    return this._toggleEditorOptionCommand;
  }
}

private bool CanToggleEditorOption(object parameter)
{
  if (this.ActiveDocument != null)
    return true;

  return false;
}

private void OnToggleEditorOption(object parameter)
{
  FileViewModel f = this.ActiveDocument;

  if (parameter == null)
    return;

  if ((parameter is ToggleEditorOption) == false)
    return;

  ToggleEditorOption t = (ToggleEditorOption)parameter;

  if (f != null)
  {
    switch (t)
    {
      case ToggleEditorOption.WordWrap:
          f.WordWrap = !f.WordWrap;
        break;

      case ToggleEditorOption.ShowLineNumber:
          f.ShowLineNumbers = !f.ShowLineNumbers;
        break;

      default:
        break;
    }
  }
}
#endregion ToggleEditorOptionCommand

No we add a WordWrap and ShowLineNumbers button in MainWindow.xaml to give the user an interface. Using the ToggleEditorOption enumeration requires to declare its namespace in XAML:

xmlns:vm="clr-namespace:Edi.ViewModel" 

Here is the declaration for both buttons:   

<ToggleButton ToolTip="Click this button to switch word wrap on or off"

              CommandParameter="{x:Static vm:ToggleEditorOption.WordWrap}"

              Command="{Binding ToggleEditorOptionCommand}"

              IsChecked="{Binding ActiveDocument.WordWrap, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
  <Image Height="32" Source="/Edi;component/Images/App/ShowWordWrap32.png" />
</Togglebutton>

<Togglebutton ToolTip="Click this button to switch display of line numbers on or off"

              CommandParameter="{x:Static vm:ToggleEditorOption.ShowLineNumber}"

              Command="{Binding ToggleEditorOptionCommand}"

              IsChecked="{Binding ActiveDocument.ShowLineNumbers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
  <Image Height="32" Source="/Edi;component/Images/App/ShowLineNumbers32.png" />
</togglebutton>

Add a WordWrap and ShowLineNumbers binding into the AvalonEdit statement in the MainWindow.xaml:

<avalonEdit:TextEditor

Document="{Binding Document, UpdateSourceTrigger=PropertyChanged}"

SyntaxHighlighting="{Binding HighlightDef}"

IsModified="{Binding Path=IsDirty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

IsReadOnly="{Binding IsReadOnly}"

WordWrap="{Binding Path=WordWrap, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

ShowLineNumbers="{Binding Path=ShowLineNumbers, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

        ...
</avalonEdit:TextEditor>

Add Edit Text Options support

This section gives an introduction to setting up some basic text options, such as, the usage of spaces and tabs.

Add a TextOptions property into the FileViewModel class (note the declaration in the constructor of the TextEditorOptions class - it replaces tabs with two spaces and switches tabs off in the editor): 

#region TextEditorOptions
    private ICSharpCode.AvalonEdit.TextEditorOptions mTextOptions
      = new ICSharpCode.AvalonEdit.TextEditorOptions()
      {
        ConvertTabsToSpaces= false,
        IndentationSize = 2
      };

    public ICSharpCode.AvalonEdit.TextEditorOptions TextOptions
    {
      get
      {
        return this.mTextOptions;
      }

      set
      {
        if (this.mTextOptions != value)
        {
          this.mTextOptions = value;
          this.RaisePropertyChanged("TextOptions");
        }
      }
    }
#endregion TextEditorOptions

We can use the same ToggleEditorOptionCommand as we used before by extending the corresponding enumeration and handling the new cases in OnToggleEditorOption method listed above:  

case ToggleEditorOption.ShowSpaces:
  f.TextOptions.ShowSpaces = !f.TextOptions.ShowSpaces;
  break;

case ToggleEditorOption.ShowTabs:
  f.TextOptions.ShowTabs = !f.TextOptions.ShowTabs;
  break;

case ToggleEditorOption.ShowEndOfLine:
  f.TextOptions.ShowEndOfLine = !f.TextOptions.ShowEndOfLine;
  break;

Now lets add the toggle buttons into the MainWindow.xaml toolbar. You will notice that we can bind directly to the TextOptions properties's sub-property. This spares us implementing another FileViewModel property for each setting. 

<ToggleButton IsChecked="{Binding ActiveDocument.TextOptions.ShowEndOfLine, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"

  Command="{Binding ToggleEditorOptionCommand}"

  CommandParameter="{x:Static vm:ToggleEditorOption.ShowEndOfLine}"

  ToolTip="Highlighted end of line in text (toggle on/off)">
  <Image Source="/Edi;component/Images/App/ShowEnter32.png" Height="32"/>
</ToggleButton>

<ToggleButton IsChecked="{Binding ActiveDocument.TextOptions.ShowSpaces, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"

  Command="{Binding ToggleEditorOptionCommand}"

  CommandParameter="{x:Static vm:ToggleEditorOption.ShowSpaces}"

  ToolTip="Highlight spaces characters in text (toggle on/off)">

  <Image Source="/Edi;component/Images/App/ShowSpaces32.png" Height="32"/>
</ToggleButton>

<ToggleButton IsChecked="{Binding ActiveDocument.TextOptions.ShowTabs, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"

  Command="{Binding ToggleEditorOptionCommand}"

  CommandParameter="{x:Static vm:ToggleEditorOption.ShowTabs}"

  ToolTip="Highlight tab characters in text (toggle on/off)">

  <Image Source="/Edi;component/Images/App/ShowTab32.png" Height="32"/>

</ToggleButton> 

Add a text options property in MainWindow.xaml binding

<avalonEdit:TextEditor

Document="{Binding Document, UpdateSourceTrigger=PropertyChanged}"

SyntaxHighlighting="{Binding HighlightDef}"

IsModified="{Binding Path=IsDirty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

IsReadOnly="{Binding IsReadOnly}"

WordWrap="{Binding Path=WordWrap, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

ShowLineNumbers="{Binding Path=ShowLineNumbers, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Options="{Binding TextOptions}">
        ...

</avalonEdit:TextEditor> 

Conclusions

It is not too hard to use MVVM with AvalonDock [2.0] and AvalonEdit. I even started my own repository in the mean time (if you feel like contributing) https://github.com/Dirkster99/AvalonDock.

The next part of the series will introduce a more advanced feature like scaling of the font via combobox in the scrollbar space (as you might have seen it in Visual Studio 2010). After that we shall look at theming and explain how items, such as, hyperlinks can be themed using a technique that I call themable highlighting.

I would like to see your feedback. Please tell me if you have additional input, -if you find something wrong, or miss-leading.

History

  • 31.03.2013 Created first version of this article

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here