【WPF跨平台开发Avalonia踩坑记】

WPF跨平台开发Avalonia踩坑记

  • 初识Avalonia
  • 准备工作
  • 创建第一个属于自己的Avalonia项目
    • Linux系统下运行Avalonia项目
  • 结语

【WPF跨平台开发Avalonia踩坑记】_第1张图片

初识Avalonia

由于公司想把当前的wpf项目整体移植到linux系统下,完全没接触过linux系统的我一开始是极其盲目的(包括现在也是很盲目),所以任何一个关键词在我这都是下一个目标。就在这时,我接触到了Avalonia框架。
不得不说,这个框架保留了大部分wpf风格和思维方式,但还是和原生的wpf有些许不同。在接下来的文章中,我会加以总结,可能整理的不够完善,但最终项目彻底完成后,我会进行梳理总结。

准备工作

开发工具:vs2022,VMWare
工欲善其事必先利其器,所以一个顺手的工具是必备的。
我自打接触wpf开发就一直用的vs,所以这次我也继续延续。由于是跨平台开发,很多东西都是没接触过的,所以我准备了虚拟机并安装了Ubuntu系统便于测试。
除此之外,使用Avalonia进行开发时还需要安装一个插件:Avalonia for Visual Studio 2022
安装方法如下:
首先打开vs2022,在管理扩展中输入Avalonia即可初夏如下界面:
【WPF跨平台开发Avalonia踩坑记】_第2张图片
选择第一个安装即可。
安装完毕后,重启vs2022,在创建新项目中找到Avalonia的模板即添加插件成功。
【WPF跨平台开发Avalonia踩坑记】_第3张图片
【WPF跨平台开发Avalonia踩坑记】_第4张图片至此初始准备工作已经结束,其他需要自己完善的比如linux系统的安装,依赖包引用等等,这里跳过,自行度娘基本上都可以解决。

创建第一个属于自己的Avalonia项目

打开vs2022,在创建新项目里面找到Avalonia关联的模板
【WPF跨平台开发Avalonia踩坑记】_第5张图片
选择Avalonia MVVM Application并点击下一步,填写好项目名称位置等后创建,整体的目录结构如下:
【WPF跨平台开发Avalonia踩坑记】_第6张图片
点击运行
【WPF跨平台开发Avalonia踩坑记】_第7张图片
至此创建项目结束。

Linux系统下运行Avalonia项目

很多小伙伴其实一开始关心的就是怎么能赶快在linux系统下试一试效果(别人不知道,反正我是特别想看下效果),那么就有了这一章的内容,操作非常简单。
首先我们已经有了可以在windows下运行的程序,这个时候,我们仅需要两行命令,即可生成Ubuntu的安装包。
具体如下:
首先找到工程文件(.csproj)所在文件夹
【WPF跨平台开发Avalonia踩坑记】_第8张图片
打开命令行并切换到当前目录
先输入 dotnet deb install 命令,用于下载 deb 工具。
【WPF跨平台开发Avalonia踩坑记】_第9张图片

然后执行 dotnet restore -r linux-x64 命令。
【WPF跨平台开发Avalonia踩坑记】_第10张图片
最后输入 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文件。
【WPF跨平台开发Avalonia踩坑记】_第11张图片
接下来就是在Linux系统上安装了(好激动啊)
我这边是通过spc命令把安装包发送过去的,借助工具也可,看怎么顺手怎么来。
【WPF跨平台开发Avalonia踩坑记】_第12张图片
发送过去后,大概是这么个样子。
现在运行安装
输入 sudo dpkg -i TestAvalonia.1.0.0.linux-x64.deb安装
【WPF跨平台开发Avalonia踩坑记】_第13张图片
这个样子就应该是安装好了。
然后我们打开用户/共享目录,找到我们的程序
【WPF跨平台开发Avalonia踩坑记】_第14张图片
执行./TestAvaloina理论上就可以打开刚才的界面。但是有的小伙伴会报这样的错误。
【WPF跨平台开发Avalonia踩坑记】_第15张图片
这是因为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系统上尝试。
【WPF跨平台开发Avalonia踩坑记】_第16张图片
这次非常完美的打开了我们的Avalonia程序。

结语

今天作为Avalonia框架学习的开篇,简单介绍了一下使用的开发工具及基本流程,实现了Windows,Linux系统的程序运行和简单的操作方法,接下来的文章将介绍Avalonia中与原生wpf不同的地方。

你可能感兴趣的:(Avalonia开发,wpf,linux)