Click here to Skip to main content
15,867,686 members
Articles / Web Development / HTML

AvalonDock [2.0] Tutorial Part 4 - Integrating AvalonEdit Options

Rate me:
Please Sign up or sign in to vote.
5.00/5 (9 votes)
31 Jan 2014CPOL3 min read 59.2K   1.8K   27   15
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.

Image 1 

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) 

C#
#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:

C#
#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:

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

Here is the declaration for both buttons:   

XML
<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:

XML
<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): 

C#
#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:  

C#
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. 

XML
<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

XML
<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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
The Windows Presentation Foundation (WPF) and C# are among my favorites and so I developed Edi

and a few other projects on GitHub. I am normally an algorithms and structure type but WPF has such interesting UI sides that I cannot help myself but get into it.

https://de.linkedin.com/in/dirkbahle

Comments and Discussions

 
GeneralMy vote of 5 Pin
leiyangge15-Jul-13 3:37
leiyangge15-Jul-13 3:37 
Excellent except error when new one file.
GeneralRe: My vote of 5 Pin
Dirk Bahle15-Jul-13 8:04
Dirk Bahle15-Jul-13 8:04 
QuestionException in StartSelectionOrSetEndpoint() Pin
PraiseTheSun2-Apr-13 13:25
PraiseTheSun2-Apr-13 13:25 
AnswerRe: Exception in StartSelectionOrSetEndpoint() Pin
Dirk Bahle2-Apr-13 19:51
Dirk Bahle2-Apr-13 19:51 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
molidort16-Jun-13 23:17
molidort16-Jun-13 23:17 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
Dirk Bahle17-Jun-13 5:08
Dirk Bahle17-Jun-13 5:08 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
leiyangge15-Jul-13 2:52
leiyangge15-Jul-13 2:52 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
Dirk Bahle15-Jul-13 8:13
Dirk Bahle15-Jul-13 8:13 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
leiyangge15-Jul-13 15:41
leiyangge15-Jul-13 15:41 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
Dirk Bahle16-Jul-13 5:20
Dirk Bahle16-Jul-13 5:20 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
leiyangge16-Jul-13 15:38
leiyangge16-Jul-13 15:38 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
Dirk Bahle17-Jul-13 7:50
Dirk Bahle17-Jul-13 7:50 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
leiyangge17-Jul-13 15:38
leiyangge17-Jul-13 15:38 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
Dirk Bahle18-Jul-13 5:28
Dirk Bahle18-Jul-13 5:28 
GeneralRe: Exception in StartSelectionOrSetEndpoint() Pin
leiyangge18-Jul-13 15:27
leiyangge18-Jul-13 15:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.