Next we will explain how to use Blazor in WPF and use Blazor to do some file editing operations. Here are the things you need to use
-
WPF
-
Blazor
-
Masa Blazor
-
Monaco
Install Masa Blazor template
Use the CMD
command to install the template
dotnet new install MASA.Template
New Masa Blazor WPF App
-
Find the template as shown in the picture and click Next
-
Next, create a new project name
FileEditor
Add Monaco
-
Open
wwwroot/index.html
, and reference Monaco’s dependencies, and add the following dependencies to the end of the body.
<script> var require = { paths: { 'vs': 'https://cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs' } }; </script> <script src="//i2.wp.com/cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/loader.js"></script> <script src="//i2.wp.com/cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/editor/editor.main.nls.js"></script> <script src="//i2.wp.com/cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/editor/editor.main.js"></script></ pre> <ol><li><p>Create a new <code>Pages/Index.razor.cs</code> file</p></li></ol> <pre>using System.IO; using System.Text; using Masa.Blazor; using Masa.Blazor.Presets; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; namespace FileEditor.Pages; public partial class Index : IDisposable { /// <summary> /// Text content /// </summary> private string value; private MMonacoEditor _editor; private DotNetObjectReference<Index>? _objRef; /// <summary> /// Define the initial configuration of Monaco /// </summary> private object options = new { language = "md", // Set syntax automaticLayout = true, // Height adaptive theme = "vs-dark", // theme }; private string fullName; protected override void OnInitialized() { _objRef = DotNetObjectReference.Create(this); } /// <summary> ///Specific file path /// </summary> [Parameter] [CascadingParameter(Name = nameof(FullName))] public string FullName { get => fullName; set { fullName = value; UpdateValue(); } } /// <summary> /// Monaco initialization event /// </summary> private async Task InitMonaco() { // Monitor CTRL + S 2097 = CTRL + S shortcut key // Call Monaco's Command, pass the current object, and specify to call the specified method of the signed object when the shortcut key is triggered. await _editor.AddCommandAsync(2097, _objRef, nameof(SaveValue)); } /// <summary> /// Update value /// </summary> private void UpdateValue() { if (string.IsNullOrEmpty(fullName)) { return; } var info = new FileInfo(fullName); if (!info.Exists) return; using var fileStream = info.OpenText(); value = fileStream.ReadToEnd(); } /// <summary> /// Update file content /// </summary> [JSInvokable] public async Task SaveValue() { try { await using var fileStream = File.OpenWrite(fullName); fileStream.Position = 0; await fileStream.WriteAsync(Encoding.UTF8.GetBytes(value)); fileStream.Close(); } catch (Exception e) { await PopupService.EnqueueSnackbarAsync(new SnackbarOptions() { Title = "Error saving file", Content = e.Message }); } } public void Dispose() { _editor.Dispose(); _objRef?.Dispose(); } }
In the Index.razor.cs
file, we implement the set that intercepts FullName. When it is set, it means that the superior component has selected the file and passed the parameters to the current component through CascadingParameter
.
And update the current Value,
Open Index.razor
@page "/" @inject IPopupService PopupService <MMonacoEditor InitCompleteHandle="async () => await InitMonaco()" @bind-Value="value" Height="@("100%")" EditorOptions="options" @ref="_editor"> </MMonacoEditor>
We have bound some methods and parameters of cs, and bind-value the value of value
. We have updated the value
cs file. >The displayed value of the UI is automatically updated.
Then we open the Shared/MainLayout.razor
file and add the open file selector to select the file.
@using Microsoft.Win32 @inherits LayoutComponentBase <MApp> <MAppBar App> <MAppBarNavIcon @onclick="() => _drawer = !_drawer"></MAppBarNavIcon> <MToolbarTitle>FileEditor</MToolbarTitle> <MButton OnClick="OpenFile">Open file</MButton> <MSpacer></MSpacer> <MButton Text Color="primary" Target="_blank" Href="https://docs.masastack.com/blazor/introduction/why-masa-blazor">About</MButton> </MAppBar> <MNavigationDrawer App @bind-Value="_drawer"> <MList Nav Routable> <MListItem Href="/" ActiveClass="primary--text"> <MListItemIcon> <MIcon>mdi-home</MIcon> </MListItemIcon> <MListItemContent> <MListItemTitle>Home</MListItemTitle> </MListItemContent> </MListItem> <MListItem Href="/counter" ActiveClass="primary--text"> <MListItemIcon> <MIcon>mdi-plus</MIcon> </MListItemIcon> <MListItemContent> <MListItemTitle>Counter</MListItemTitle> </MListItemContent> </MListItem> <MListItem Href="/fetchdata" ActiveClass="primary--text"> <MListItemIcon> <MIcon>mdi-list-box</MIcon> </MListItemIcon> <MListItemContent> <MListItemTitle>Fetch data</MListItemTitle> </MListItemContent> </MListItem> </MList> </MNavigationDrawer> <MMain> <MContainer Fluid Style="height: 100%"> <CascadingValue Value="fullName" Name="FullName"> <MErrorHandler> @Body </MErrorHandler> </CascadingValue> </MContainer> </MMain> </MApp> @code { private bool? _drawer; private string fullName; private void OpenFile() { var openFileDialog = new OpenFileDialog(); openFileDialog.Title = "Please select your file"; openFileDialog.Filter = "Text file (*.txt, *.md)|*.txt;*.md"; bool? result = openFileDialog.ShowDialog(); if (result == true) { fullName = openFileDialog.FileName; } } }
Here we will use Microsoft.Win32.OpenFileDialog
to open the file selector and specify the type of selected file.
If the current file selector returns true, the value of fullName
and fullName
will be passed to
.CascadingValue
component. All subcomponents within
Let’s take a look at the actual use effect.