Compare commits

...

3 Commits

Author SHA1 Message Date
b6db54b08e docs: 添加使用说明 2025-01-24 01:18:31 +08:00
44de222ed7 test: 测试 MVVM 模式与绑定 2025-01-24 01:17:06 +08:00
f6118e29f8 feat: 支持替换绑定内容 2025-01-24 01:13:52 +08:00
20 changed files with 456 additions and 5 deletions

View File

@@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Folder", "Solution
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{933A9C2F-8C23-4898-A266-780A841218DD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{933A9C2F-8C23-4898-A266-780A841218DD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestMvvm", "TestMvvm\TestMvvm.csproj", "{2558A760-25A2-4EA1-8F7B-960C54E1D898}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -24,5 +26,9 @@ Global
{933A9C2F-8C23-4898-A266-780A841218DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {933A9C2F-8C23-4898-A266-780A841218DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{933A9C2F-8C23-4898-A266-780A841218DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {933A9C2F-8C23-4898-A266-780A841218DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{933A9C2F-8C23-4898-A266-780A841218DD}.Release|Any CPU.Build.0 = Release|Any CPU {933A9C2F-8C23-4898-A266-780A841218DD}.Release|Any CPU.Build.0 = Release|Any CPU
{2558A760-25A2-4EA1-8F7B-960C54E1D898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2558A760-25A2-4EA1-8F7B-960C54E1D898}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2558A760-25A2-4EA1-8F7B-960C54E1D898}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2558A760-25A2-4EA1-8F7B-960C54E1D898}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@@ -6,5 +6,6 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATest_002EMainWindow_002Eg_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1beb137ab36b71a8961f85c524c65a73ff2463ed_003FTest_002EMainWindow_002Eg_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATest_002EMainWindow_002Eg_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1beb137ab36b71a8961f85c524c65a73ff2463ed_003FTest_002EMainWindow_002Eg_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6d2342c247c64eb7a9b3702e481e4443f83e00_003Fc7_003F71d96ba5_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6d2342c247c64eb7a9b3702e481e4443f83e00_003Fc7_003F71d96ba5_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4743f513c7ddc8411223a46f0ca426ed929391acebcff993721dff2f0c6b34_003FThrowHelper_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4743f513c7ddc8411223a46f0ca426ed929391acebcff993721dff2f0c6b34_003FThrowHelper_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=Test_002FAssets_002FResources/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=TestMvvm_002FAssets_002FResources/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=Test_002FAssets_002FResources/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -3,7 +3,7 @@ using Avalonia.Data.Converters;
namespace JackCraft.I18N.Converters; namespace JackCraft.I18N.Converters;
internal class I18NConverter(I18NBinding binding, object[] bindingValues) : IMultiValueConverter internal class I18NConverter(I18NBinding binding, dynamic[] bindingValues) : IMultiValueConverter
{ {
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture) public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{ {
@@ -12,8 +12,11 @@ internal class I18NConverter(I18NBinding binding, object[] bindingValues) : IMul
if (binding.KeyConverter.Convert(values[1], null!, null, culture) is not string key) if (binding.KeyConverter.Convert(values[1], null!, null, culture) is not string key)
return values[1]; return values[1];
var value = I18NConfig.Manager.Get<object>(key, values[0] as CultureInfo); var value = I18NConfig.Manager.Get<object>(key, values[0] as CultureInfo);
if (value is string str && bindingValues.Length > 0) if (value is not string str) return binding.ValueConverter.Convert(value, null!, null, culture);
if (bindingValues is string[] && bindingValues.Length > 0)
value = string.Format(str, bindingValues); value = string.Format(str, bindingValues);
else if (bindingValues.Length > 0)
value = string.Format(str, values.Skip(2).ToArray());
return binding.ValueConverter.Convert(value, null!, null, culture); return binding.ValueConverter.Convert(value, null!, null, culture);
} }
} }

View File

@@ -19,5 +19,7 @@ internal class I18NBinding : MultiBinding
ValueConverter = new I18NValueConverter(); ValueConverter = new I18NValueConverter();
Bindings.Add(new Binding { Source = I18NConfig.Manager, Path = nameof(I18NConfig.Manager.Culture) }); Bindings.Add(new Binding { Source = I18NConfig.Manager, Path = nameof(I18NConfig.Manager.Culture) });
Bindings.Add(new Binding { Source = key }); Bindings.Add(new Binding { Source = key });
foreach (var value in values)
Bindings.Add(value);
} }
} }

View File

@@ -2,12 +2,35 @@ using Avalonia.Markup.Xaml;
namespace JackCraft.I18N; namespace JackCraft.I18N;
public class I18NExtension(string key, params object[] values) : MarkupExtension public class I18NExtension(string key, params dynamic[] values) : MarkupExtension
{ {
public I18NExtension(string key) : this(key, []) public I18NExtension(string key) : this(key, [])
{ {
} }
public I18NExtension(string key, dynamic value1) : this(key, [value1])
{
}
public I18NExtension(string key, dynamic value1, dynamic value2) : this(key, [value1, value2])
{
}
public I18NExtension(string key, dynamic value1, dynamic value2, dynamic value3) : this(key,
[value1, value2, value3])
{
}
public I18NExtension(string key, dynamic value1, dynamic value2, dynamic value3, dynamic value4) : this(key,
[value1, value2, value3, value4])
{
}
public I18NExtension(string key, dynamic value1, dynamic value2, dynamic value3, dynamic value4, dynamic value5) :
this(key, [value1, value2, value3, value4, value5])
{
}
public override object ProvideValue(IServiceProvider serviceProvider) public override object ProvideValue(IServiceProvider serviceProvider)
{ {
return new I18NBinding(key, values); return new I18NBinding(key, values);

View File

@@ -1,3 +1,59 @@
# JackCraft.I18N # JackCraft.I18N
一个适用于 Avalonia 的自己觉得怎么好用怎么写的 I18N 库 一个适用于 Avalonia 的自己觉得怎么好用怎么写的 I18N 库
## 使用方法
### 安装
在 Avalonia 组件完全加载完成前的任意地方声明一个 `I18NManager`
```csharp
public class App : Application
{
// 例子: (不包含文件扩展名)
// Test.Resources
// TestAvaloniaProject.Assets.Languages.Resources
public static I18NManager I18NManager { get; } = new("程序命名空间.路径.语言资源文件名");
public override void Initialize()
...
}
```
### 使用
#### 切换语言/文化
```csharp
I18NManager.Culture = new CultureInfo("zh-cn");
```
#### 获取当前存在的语言/文化
```csharp
var totalCultures = I18NManager.GetCultures();
```
#### 从资源文件中获取特定类型的资源
```csharp
// 标准示例: var value = I18NManager.Get<类型>("资源名称", 文化信息, ...如果是字符串且需要格式化的话需要传入的格式化参数);
// 假设 Hello 的中文资源为 "你好, {0}",默认资源为 "Hello, {0}"Username 为 "User"
var value = I18NManager.Get<string>("Hello", new CultureInfo("zh-cn"), Username); // value = "你好, User"
var value = I18NManager.Get<string>("Hello", CultureInfo.InvariantCulture, Username); // value = "Hello, User"
```
#### Avalonia 中的使用
```xml
<Window xmlns="https://github.com/avaloniaui"
xmlns:i18N="clr-namespace:JackCraft.I18N;assembly=JackCraft.I18N">
<StackPanel>
<TextBlock Text="{i18N:I18N Test}" /> <!-- 输出: 测试 -->
<TextBlock Text="{i18N:I18N Hello, User}" /> <!-- 输出: 你好, User -->
<!-- ViewModel: public string User { get; set; } = "User"; -->
<TextBlock Text="{i18N:I18N Hello, {Binding User}}" /> <!-- 输出: 你好, User -->
</StackPanel>
</Window>
```

15
TestMvvm/App.axaml Normal file
View File

@@ -0,0 +1,15 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="TestMvvm.App"
xmlns:local="using:TestMvvm"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator />
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

46
TestMvvm/App.axaml.cs Normal file
View File

@@ -0,0 +1,46 @@
using System.Linq;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using JackCraft.I18N;
using TestMvvm.ViewModels;
using TestMvvm.Views;
namespace TestMvvm;
public class App : Application
{
public static I18NManager I18NManager { get; } = new("TestMvvm.Assets.Resources");
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
DisableAvaloniaDataAnnotationValidation();
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel()
};
}
base.OnFrameworkInitializationCompleted();
}
private void DisableAvaloniaDataAnnotationValidation()
{
// Get an array of plugins to remove
var dataValidationPluginsToRemove =
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove) BindingPlugins.DataValidators.Remove(plugin);
}
}

54
TestMvvm/Assets/Resources.Designer.cs generated Normal file
View File

@@ -0,0 +1,54 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace TestMvvm.Assets {
using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Resources.ResourceManager ResourceManager {
get {
if (object.Equals(null, resourceMan)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("TestMvvm.Assets.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
internal static string Test1 {
get {
return ResourceManager.GetString("Test1", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"
xmlns="">
<xsd:element name="root" msdata:IsDataSet="true">
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="Test1" xml:space="preserve">
<value>Test1: {0}</value>
</data>
</root>

View File

@@ -0,0 +1,21 @@
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="Test1" xml:space="preserve">
<value>测试1: {0}</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

26
TestMvvm/Program.cs Normal file
View File

@@ -0,0 +1,26 @@
using System;
using Avalonia;
namespace TestMvvm;
internal sealed class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args)
{
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
}

47
TestMvvm/TestMvvm.csproj Normal file
View File

@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<Folder Include="Models\"/>
<AvaloniaResource Include="Assets\**"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.3"/>
<PackageReference Include="Avalonia.Desktop" Version="11.2.3"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.3"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.3"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.3">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JackCraft.I18N\JackCraft.I18N.csproj"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Assets\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Assets\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
</Project>

27
TestMvvm/ViewLocator.cs Normal file
View File

@@ -0,0 +1,27 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using TestMvvm.ViewModels;
namespace TestMvvm;
public class ViewLocator : IDataTemplate
{
public Control? Build(object? param)
{
if (param is null)
return null;
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null) return (Control)Activator.CreateInstance(type)!;
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
}

View File

@@ -0,0 +1,29 @@
using System.Globalization;
using System.IO;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace TestMvvm.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
[ObservableProperty] public string? test = Path.GetRandomFileName();
[RelayCommand]
public void RandomText()
{
Test = Path.GetRandomFileName();
}
[RelayCommand]
public void ChangeToChinese()
{
App.I18NManager.Culture = new CultureInfo("zh-hans");
}
[RelayCommand]
public void ChangeToDefault()
{
App.I18NManager.Culture = CultureInfo.InvariantCulture;
}
}

View File

@@ -0,0 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace TestMvvm.ViewModels;
public class ViewModelBase : ObservableObject
{
}

View File

@@ -0,0 +1,30 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:TestMvvm.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i18N="clr-namespace:JackCraft.I18N;assembly=JackCraft.I18N"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="TestMvvm.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="TestMvvm">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel />
</Design.DataContext>
<StackPanel>
<TextBlock Text="{Binding Test}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Text="{i18N:I18N Test1, {Binding Test}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content="随机文本"
Command="{Binding RandomTextCommand}" />
<Button Content="切换中文"
Command="{Binding ChangeToChineseCommand}" />
<Button Content="切换默认"
Command="{Binding ChangeToDefaultCommand}" />
</StackPanel>
</Window>

View File

@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace TestMvvm.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}

18
TestMvvm/app.manifest Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="TestMvvm.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>