Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions CS/CodeEditor/Docs/CodeEditor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# CodeEditor

A WPF code editor control with syntax highlighting and modern code display and navigation capabilities. The control embeds the Monaco Editor, the editor used in Visual Studio Code, through WebView2.

## Properties

| Name | Type | Description |
| ---- | ---- | ----------- |
| `Text` | `string` | Gets or sets the editor text. This is a dependency property that supports two-way binding. |
| `EditorLanguage` | `string` | Gets or sets the programming language for syntax highlighting. This is a dependency property. Default: `"csharp"` |
| `ReadOnly` | `bool` | Gets or sets whether the editor is read-only. This is a dependency property. Default: `false` |
| `IsModified` | `bool` | Gets whether the content has been modified since the last save. This is a read-only dependency property. |
| `ShowLineNumbers` | `bool` | Gets or sets whether line numbers are displayed. This is a dependency property. Default: `true` |
| `LineNumbersMinChars` | `int` | Gets or sets the minimum number of characters reserved for the line-number gutter. This is a dependency property. Default: `5` |
| `ShowMinimap` | `bool` | Gets or sets whether the minimap is displayed. This is a dependency property. Default: `false` |
| `ShowGlyphMargin` | `bool` | Gets or sets whether the glyph margin is displayed. This is a dependency property. Default: `false` |
| `EnableFolding` | `bool` | Gets or sets whether code folding is enabled. This is a dependency property. Default: `true` |
| `TabSize` | `int` | Gets or sets the tab size in spaces (1-64). This is a dependency property. Default: `4` |
| `InsertSpaces` | `bool` | Gets or sets whether spaces are inserted instead of tabs. This is a dependency property. Default: `true` |
| `DetectIndentation` | `bool` | Gets or sets whether indentation is detected from file content. This is a dependency property. Default: `true` |
| `AutoIndent` | `EditorAutoIndent` | Gets or sets the auto-indentation mode. This is a dependency property. Default: `EditorAutoIndent.Full` |
| `WordWrap` | `EditorWordWrap` | Gets or sets word the wrap mode. This is a dependency property. Default: `EditorWordWrap.Off` |
| `EnableScrollBeyondLastLine` | `bool` | Gets or sets whether scrolling beyond the last line is allowed. This is a dependency property. Default: `true` |
| `ScrollBeyondLastColumn` | `int` | Gets or sets the number of columns the editor can scroll beyond the last character. This is a dependency property. Default: `5` |
| `EnableSmoothScrolling` | `bool` | Gets or sets whether smooth scrolling is enabled. This is a dependency property. Default: `false` |
| `EnableStickyScroll` | `bool` | Gets or sets whether sticky scroll is enabled. This is a dependency property. Default: `true` |
| `EnableContextMenu` | `bool` | Gets or sets whether the context menu is enabled. This is a dependency property. Default: `true` |
| `EnableDragAndDrop` | `bool` | Gets or sets whether drag and drop is enabled. This is a dependency property. Default: `true` |
| `EnableMouseWheelZoom` | `bool` | Gets or sets whether zooming with <kbd>Ctrl</kbd>+<kbd>MouseWheel</kbd> is enabled. This is a dependency property. Default: `false` |
| `EnableQuickSuggestions` | `bool` | Gets or sets whether quick suggestions are enabled. This is a dependency property. Default: `true` |
| `EnableWordBasedSuggestions` | `bool` | Gets or sets whether word-based suggestions are enabled. This is a dependency property. Default: `true` |
| `EnableSuggestOnTriggerCharacters` | `bool` | Gets or sets whether suggestions appear automatically when a user types trigger characters. This is a dependency property. Default: `true` |
| `EnableParameterHints` | `bool` | Gets or sets whether parameter hints are displayed. This is a dependency property. Default: `true` |
| `ThemeName` | `string` | Gets or sets the Monaco Editor theme name. This is a dependency property. Default: `"vs"` |
| `MarkAsSavedCommand` | `ICommand` | Gets the command that resets the `IsModified` flag. |

## Methods

| Name | Description |
| ---- | ----------- |
| `GetAvailableLanguagesAsync(CancellationToken)` | Asynchronously retrieves a list of available programming language IDs. |
| `RegisterLanguage(LanguageDescriptor)` | Registers a custom programming language with a Monarch tokenizer. |
| `RegisterTheme(MonacoTheme)` | Registers a custom Monaco Editor theme. |
| `MarkAsSaved()` | Resets the `IsModified` property to `false`. |
| `Dispose()` | Releases WebView2 resources. |

## Events

| Name | Description |
| ---- | ----------- |
| `EditorInitialized` | Occurs when the Monaco Editor is fully initialized and ready for commands. |

## Enums

### EditorAutoIndent

| Member | Description |
| ------ | ----------- |
| `None` | No automatic indentation |
| `Keep` | Keep current indentation |
| `Brackets` | Indent based on brackets |
| `Advanced` | Advanced indentation |
| `Full` | Full automatic indentation (recommended) |

### EditorWordWrap

| Member | Description |
| ------ | ----------- |
| `Off` | No word wrapping |
| `On` | Word wrapping enabled |
21 changes: 21 additions & 0 deletions CS/CodeEditor/Docs/CodeEditorService.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# CodeEditorService

MVVM service that provides access to CodeEditor control functionality from ViewModels.

## Interface

```csharp
public interface ICodeEditorService {
void MarkAsSaved();
Task<IReadOnlyCollection<string>> GetLanguagesAsync(CancellationToken cancellationToken = default);
void RegisterLanguage(LanguageDescriptor language);
}
```

## Methods

| Name | Description |
| ---- | ----------- |
| `MarkAsSaved()` | Resets the `IsModified` flag of the associated `CodeEditor` to `false`. |
| `GetLanguagesAsync(CancellationToken)` | Asynchronously retrieves a collection of available programming language IDs from the associated `CodeEditor`. |
| `RegisterLanguage(LanguageDescriptor)` | Registers a custom programming language with a Monarch tokenizer in the associated `CodeEditor`. |
287 changes: 287 additions & 0 deletions CS/CodeEditor/Docs/Overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
## Introduction

This DevExpress WPF example displays code in a custom `CodeEditor` control. The control wraps the [Monaco Editor](https://microsoft.github.io/monaco-editor/), the editor used in Visual Studio Code. Monaco Editor supports modern code editing features such as syntax highlighting, region folding, a minimap, theming, and custom language support. Since **Monaco Editor** is a web control, the WPF wrapper uses [Microsoft WebView2](https://learn.microsoft.com/en-us/microsoft-edge/webview2/) to display it.





The `CodeEditor` implementation uses the following helper classes:

- `ThemeBehavior` - integrates Monaco with DevExpress themes.
- `CodeEditorService` - a lightweight service for scenarios where direct interaction with the control is not sufficient.

The control is designed as a **state-driven wrapper**: WPF owns the editor state through dependency properties, and Monaco Editor reflects that state and propagates changes back to WPF in an MVVM-friendly way.


## Prerequisites

- DevExpress WPF v26.1 (or newer compatible version)
- Monaco Editor v0.55.1 (the control is built and validated against this specific release)
- .NET version compatible with DevExpress WPF version you use: [WPF Controls → Prerequisites → .NET / .NET Core](https://docs.devexpress.com/WPF/8091/prerequisites#netnet-core)
- Windows with [Microsoft Edge WebView2 Runtime](https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution)

> The `CodeEditor` project must be compiled against the same DevExpress WPF version that is used in the consuming application.

## Quick Start

### 1. Add the `CodeEditor` project

Add the `CodeEditor` project to your solution and reference it from your WPF application:

- Add existing project → `CodeEditor`
- Add project reference from your WPF app
- Ensure DevExpress WPF and WebView2 dependencies are resolved

### 2. Declare the XML namespace

```xml
<Window
x:Class="YourApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ce="clr-namespace:CodeEditor;assembly=CodeEditor">

<Grid>
<ce:CodeEditor
Text="{Binding Code, Mode=TwoWay}"
EditorLanguage="csharp"
ThemeName="vs-dark" />
</Grid>

</Window>
```

### 3. Create a ViewModel

```csharp
using DevExpress.Mvvm;

public class MainViewModel : BindableBase
{
private string _code = @"using System;

class Program
{
static void Main()
{
Console.WriteLine(""Hello, Monaco!"");
}
}";

public string Code
{
get => _code;
set => SetProperty(ref _code, value);
}
}
```

Set the `DataContext` of the window to an instance of `MainViewModel`.

The editor content is automatically synchronized with the bound property.

> **Note:**
> `CodeEditor` does not automatically track DevExpress theme changes.
> To synchronize Monaco with the active DevExpress theme, use `ThemeBehavior`.

## Theming

`CodeEditor` supports built-in Monaco themes, DevExpress theme integration, and custom theme registration.

### Built-in Monaco Themes

You can use standard Monaco themes:

```csharp
editor.ThemeName = "vs";
editor.ThemeName = "vs-dark";
editor.ThemeName = "hc-black";
```



### DevExpress Theme Integration



To synchronize a Monaco Editor with the active DevExpress theme, attach a `ThemeBehavior`:

```xml
xmlns:ce="clr-namespace:CodeEditor;assembly=CodeEditor"
xmlns:cet="clr-namespace:CodeEditor.Theming;assembly=CodeEditor"
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"

...

<ce:CodeEditor
Text="{Binding Code}"
EditorLanguage="csharp">

<dxmvvm:Interaction.Behaviors>
<cet:ThemeBehavior ApplyDevExpressColors="True" />
</dxmvvm:Interaction.Behaviors>

</ce:CodeEditor>
```

`ThemeBehavior` synchronizes DevExpress themes and Monaco Editor appearance in the following manner:

- Detects the current DevExpress theme at startup and each time the theme changes
- Resolves Light / Dark / High Contrast base
- Registers and applies a corresponding Monaco theme
- Optionally maps DevExpress palette colors to Monaco color keys via the `ApplyDevExpressColors` property



### Custom Themes

You can register a custom Monaco theme programmatically:

```csharp
var theme = new MonacoTheme
{
Name = "my-theme",
Base = MonacoThemeBase.Dark,
Colors = new Dictionary<string, Color>
{
["editor.background"] = Color.FromRgb(30, 30, 30),
["editor.foreground"] = Color.FromRgb(220, 220, 220)
}
};

editor.RegisterTheme(theme);
editor.ThemeName = "my-theme";
```

For a complete list of commonly used Monaco theme color keys, see the official VS Code theme color reference (Monaco uses the same keys): [Theme Color](https://code.visualstudio.com/api/references/theme-color).

### Rules

In addition to UI colors, Monaco themes support token-level styling via `Rules`.

Rules define how the editor renders specific token types such as `keyword`, `comment`, `string`, and others.

You can use `Rules` to override the appearance of existing token types or to define styling for custom tokens introduced by a custom language definition.

```csharp
var theme = new MonacoTheme
{
Name = "my-theme",
Base = MonacoThemeBase.Dark,
Rules = new[]
{
new MonacoThemeRule
{
Token = "keyword",
Foreground = Color.FromRgb(86, 156, 214),
FontStyle = MonacoFontStyle.Bold
},
new MonacoThemeRule
{
Token = "comment",
Foreground = Color.FromRgb(106, 153, 85),
FontStyle = MonacoFontStyle.Italic
}
}
};
```

`Rules` is an init-only property and must be provided during theme initialization.

If you use `ThemeBehavior`, token styling can also be supplied through its `Rules` property, and it allows rule customization alongside DevExpress theme integration.

## Languages

`CodeEditor` supports both built-in Monaco languages and custom language registration.

### Built-in Languages

To use a built-in Monaco language, specify the `EditorLanguage` property:

```csharp
editor.EditorLanguage = "csharp";
editor.EditorLanguage = "javascript";
editor.EditorLanguage = "json";
```

The value must match a language ID supported by Monaco Editor.

### Custom Languages

You can register custom languages using `LanguageDescriptor`:

```csharp
var language = new LanguageDescriptor
{
Id = "mylang",

Monarch = @"{
tokenizer: {
root: [
[/\b(if|else|while)\b/, 'keyword'],
[/\/\/.*$/, 'comment'],
[/"".*?""/, 'string'],
[/\d+/, 'number']
]
}
}",

Configuration = @"{
comments: {
lineComment: '//'
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
]
}"
};

editor.RegisterLanguage(language);
editor.EditorLanguage = "mylang";
```

### Monarch Definition

The `Monarch` property defines syntax highlighting rules.

The property must contain a valid Monaco Monarch tokenizer definition specified as a JavaScript object literal.

All standard Monarch features are supported:

- Multiple states
- Nested states
- Regular expressions
- Rule objects
- Includes and state transitions

Refer to the following help topic for additional information: [Monarch Documentation](https://microsoft.github.io/monaco-editor/monarch.html)

### Language Configuration

The optional `Configuration` property defines editor behavior attributes such as:

- Line and block comments
- Brackets
- Auto-closing pairs
- Surrounding pairs

The configuration must also be provided as a JavaScript object literal.

For additional information, refer to the following help topic: [Language Configuration Guide](https://code.visualstudio.com/api/language-extensions/language-configuration-guide)

## See also

### API Reference

- [CodeEditor API Reference](CodeEditor.md)
- [CodeEditorService API Reference](CodeEditorService.md)
- [ThemeBehavior API Reference](ThemeBehavior.md)

### Monaco Resources

- [Monaco Editor](https://microsoft.github.io/monaco-editor/)
- [Monarch Documentation](https://microsoft.github.io/monaco-editor/monarch.html)
- [Monaco Playground – Custom Languages Example](https://microsoft.github.io/monaco-editor/playground.html?source=v0.55.1#example-extending-language-services-custom-languages)
Loading
Loading