WPF跨平台开发Avalonia踩坑记
初识Avalonia
由于公司想把当前的wpf项目整体移植到linux系统下,完全没接触过linux系统的我一开始是极其盲目的(包括现在也是很盲目),所以任何一个关键词在我这都是下一个目标。就在这时,我接触到了Avalonia框架。
不得不说,这个框架保留了大部分wpf风格和思维方式,但还是和原生的wpf有些许不同。在接下来的文章中,我会加以总结,可能整理的不够完善,但最终项目彻底完成后,我会进行梳理总结。
准备工作
开发工具:vs2022,VMWare
工欲善其事必先利其器,所以一个顺手的工具是必备的。
我自打接触wpf开发就一直用的vs,所以这次我也继续延续。由于是跨平台开发,很多东西都是没接触过的,所以我准备了虚拟机并安装了Ubuntu系统便于测试。
除此之外,使用Avalonia进行开发时还需要安装一个插件:Avalonia for Visual Studio 2022。
安装方法如下:
首先打开vs2022,在管理扩展中输入Avalonia即可初夏如下界面:
选择第一个安装即可。
安装完毕后,重启vs2022,在创建新项目中找到Avalonia的模板即添加插件成功。
至此初始准备工作已经结束,其他需要自己完善的比如linux系统的安装,依赖包引用等等,这里跳过,自行度娘基本上都可以解决。
创建第一个属于自己的Avalonia项目
打开vs2022,在创建新项目里面找到Avalonia关联的模板
选择Avalonia MVVM Application并点击下一步,填写好项目名称位置等后创建,整体的目录结构如下:
点击运行
至此创建项目结束。
Linux系统下运行Avalonia项目
很多小伙伴其实一开始关心的就是怎么能赶快在linux系统下试一试效果(别人不知道,反正我是特别想看下效果),那么就有了这一章的内容,操作非常简单。
首先我们已经有了可以在windows下运行的程序,这个时候,我们仅需要两行命令,即可生成Ubuntu的安装包。
具体如下:
首先找到工程文件(.csproj)所在文件夹
打开命令行并切换到当前目录
先输入 dotnet deb install 命令,用于下载 deb 工具。
然后执行 dotnet restore -r linux-x64 命令。
最后输入 dotnet msbuild TestAvalonia.csproj /t:CreateDeb /p:TargetFramework=net6.0 /p:RuntimeIdentifier=linux-x64 /p:Configuration=Release ,执行即可。
TestAvalonia.csproj 为项目工程文件,自行替换成自己项目的即可。
打开Release/net6.0/linux-x64文件夹找到刚刚生成的.deb文件。
接下来就是在Linux系统上安装了(好激动啊)
我这边是通过spc命令把安装包发送过去的,借助工具也可,看怎么顺手怎么来。
发送过去后,大概是这么个样子。
现在运行安装
输入 sudo dpkg -i TestAvalonia.1.0.0.linux-x64.deb安装
这个样子就应该是安装好了。
然后我们打开用户/共享目录,找到我们的程序
执行./TestAvaloina理论上就可以打开刚才的界面。但是有的小伙伴会报这样的错误。
这是因为Avalonia需要指定一个默认的字体才可以正常显示。所以我们需要在改造一下之前的代码。
在项目中添加一个CustomFontManagerImpl类,并实现IFontManagerImpl接口,具体代码如下,可直接使用,使用前需要修改
private readonly Typeface _defaultTypeface =
new Typeface("resm:TestAvalonia.Assets.Fonts.msyh#微软雅黑");
中的命名空间(TestAvalonia)
整体代码如下:
public class CustomFontManagerImpl : IFontManagerImpl
{
private readonly Typeface[] _customTypefaces;
private readonly string _defaultFamilyName;
//Load font resources in the project, you can load multiple font resources
private readonly Typeface _defaultTypeface =
new Typeface("resm:TestLinuxControl.Assets.Fonts.msyh#微软雅黑");
public CustomFontManagerImpl()
{
_customTypefaces = new[] { _defaultTypeface };
_defaultFamilyName = _defaultTypeface.FontFamily.FamilyNames.PrimaryFamilyName;
}
public string GetDefaultFontFamilyName()
{
return _defaultFamilyName;
}
public IEnumerable<string> GetInstalledFontFamilyNames(bool checkForUpdates = false)
{
return _customTypefaces.Select(x => x.FontFamily.Name);
}
private readonly string[] _bcp47 = { CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName };
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
CultureInfo culture, out Typeface typeface)
{
foreach (var customTypeface in _customTypefaces)
{
if (customTypeface.GlyphTypeface.GetGlyph((uint)codepoint) == 0)
{
continue;
}
typeface = new Typeface(customTypeface.FontFamily.Name, fontStyle, fontWeight);
return true;
}
var fallback = SKFontManager.Default.MatchCharacter(fontFamily?.Name, (SKFontStyleWeight)fontWeight,
SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle, _bcp47, codepoint);
typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight);
return true;
}
public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
{
SKTypeface skTypeface;
switch (typeface.FontFamily.Name)
{
case FontFamily.DefaultFontFamilyName:
case "微软雅黑": //font family name
skTypeface = SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name); break;
default:
skTypeface = SKTypeface.FromFamilyName(typeface.FontFamily.Name,
(SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)typeface.Style);
break;
}
//解决linux系统下skTypeface是null
if (skTypeface == null)
{
skTypeface = SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name);
}
//如果是centos7之类的使用linux里面的字体
if (skTypeface == null)
{
skTypeface = SKTypeface.FromFamilyName("WenQuanYi Micro Hei");
}
return new GlyphTypefaceImpl(skTypeface);
}
}
然后到App.axaml.cs中重写RegisterServices方法即可。
public override void RegisterServices()
{
AvaloniaLocator.CurrentMutable.Bind<IFontManagerImpl>().ToConstant(new CustomFontManagerImpl());
base.RegisterServices();
}
现在,我们再次通过命令行生成.deb安装包,发送到Linux系统上尝试。
这次非常完美的打开了我们的Avalonia程序。
结语
今天作为Avalonia框架学习的开篇,简单介绍了一下使用的开发工具及基本流程,实现了Windows,Linux系统的程序运行和简单的操作方法,接下来的文章将介绍Avalonia中与原生wpf不同的地方。