diff --git a/CS/CodeEditor/Docs/CodeEditor.md b/CS/CodeEditor/Docs/CodeEditor.md new file mode 100644 index 0000000..462c5e6 --- /dev/null +++ b/CS/CodeEditor/Docs/CodeEditor.md @@ -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 Ctrl+MouseWheel 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 | diff --git a/CS/CodeEditor/Docs/CodeEditorService.md b/CS/CodeEditor/Docs/CodeEditorService.md new file mode 100644 index 0000000..c4ea877 --- /dev/null +++ b/CS/CodeEditor/Docs/CodeEditorService.md @@ -0,0 +1,21 @@ +# CodeEditorService + +MVVM service that provides access to CodeEditor control functionality from ViewModels. + +## Interface + +```csharp +public interface ICodeEditorService { + void MarkAsSaved(); + Task> 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`. | diff --git a/CS/CodeEditor/Docs/Overview.md b/CS/CodeEditor/Docs/Overview.md new file mode 100644 index 0000000..d4b00b1 --- /dev/null +++ b/CS/CodeEditor/Docs/Overview.md @@ -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 + + + + + + + +``` + +### 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" + +... + + + + + + + + +``` + +`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 + { + ["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) \ No newline at end of file diff --git a/CS/CodeEditor/Docs/ThemeBehavior.md b/CS/CodeEditor/Docs/ThemeBehavior.md new file mode 100644 index 0000000..c1c22cc --- /dev/null +++ b/CS/CodeEditor/Docs/ThemeBehavior.md @@ -0,0 +1,19 @@ +# ThemeBehavior + +**Requires:** `CompatibilitySettings.UseLightweightThemes = true` + +Behavior that automatically synchronizes Monaco Editor theme with DevExpress LightweightThemes palette. + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ApplyDevExpressColors` | `bool` | Gets or sets whether DevExpress palette colors are applied to Monaco Editor UI elements (background, foreground, selection, line numbers, scrollbar, brackets). This is a dependency property. Default: `true` | +| `Rules` | `IReadOnlyList?` | Gets or sets optional syntax token style rules (keywords, comments, strings). This is a dependency property. | + +## Static Methods + +| Name | Description | +| ---- | ----------- | +| `CreateFromDXTheme(string, IReadOnlyList?, bool)` | Creates a `MonacoTheme` from a DevExpress theme name with optional rules and color mapping. | +| `GetColor(string)` | Retrieves a color from the current DevExpress theme palette by color key. Returns `null` if color is not found. | diff --git a/Readme.md b/Readme.md index 4bf7bbd..d536aff 100644 --- a/Readme.md +++ b/Readme.md @@ -322,6 +322,19 @@ The editor tracks unsaved changes. `IsModified` is a read-only dependency proper Command="{Binding SaveFileCommand}" IsEnabled="{Binding ElementName=CodeEditor, Path=IsModified}"/> ``` + +## Help Topics + +- [Overview](CS/CodeEditor/Docs/Overview.md) +- [CodeEditor API Reference](CS/CodeEditor/Docs/CodeEditor.md) +- [CodeEditorService API Reference](CS/CodeEditor/Docs/CodeEditorService.md) +- [ThemeBehavior API Reference](CS/CodeEditor/Docs/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) ## Does This Example Address Your Development Requirements/Objectives?