Prompt Playground 7月开发记录(2): Avalonia 应用开发
Prompt Playground 7月开发记录(2): Avalonia 应用开发
仅以此文记录开发过程中遇到的问题和个人的解决方案,如若有理解偏差或者更好的解决方案,欢迎指正。
客户端的开发的确不同于Web应用的开发。习惯了Web应用开发的模式之后,刚开始上手Avalonia应用的开发,就遇到了很多问题,其中比较多的还是UI相关的问题,还有就是状态管理的问题。
界面之上
给主菜单添加Icon
Icon的添加直接使用了Icons.Avalonia中的Material Design Icons.
其中提供了在菜单项上添加icon 的直接方法。
<MenuItem Header="About" i:MenuItem.Icon="fa-solid fa-circle-info" />
但是实际使用下来,发现只能给子菜单添加icon,主菜单无法添加。
于是就另辟蹊径,直接自定义主菜单的 Header。
<MenuItem>
<MenuItem.Header>
<i:Icon Value="mdi-menu"/>
</MenuItem.Header>
</MenuItem>
在菜单栏内展示状态和选项
菜单栏的定义往往直接<Menu DockPanel.Dock="Top">就可以了。
但是如果没有那么多的菜单项的话,就会显得很空,于是就想在菜单栏内添加一些状态信息和选项。
如何继续使用<Menu>标签的话,其他的组件也会有菜单的样式,于是就做一些简单的调整,整体使用 <WrapPanel> 包裹。
<DockPanel>
<WrapPanel DockPanel.Dock="Top">
<Menu>
<MenuItem>
<MenuItem.Header>
<i:Icon Value="mdi-menu"/>
</MenuItem.Header>
</MenuItem>
</Menu>
<TextBlock Text="Status"/>
</WrapPanel>
</DockPanel>
WrapPanel 和 StackPanel 类似,两者都是用来包裹其他组件的,WrapPanel默认是横向的,会换行,而StackPanel默认是纵向的。
实现简单的状态栏
状态栏和菜单栏类似,也是使用<DockPanel> + <StackPanel> ,只不过是放在了底部。
同时为StackPanel添加上一个默认的background,这样就可以看到状态栏了。
在StackPanel中添加一个<WrapPanel>,里面就可以放置更多的状态组件了。
<StackPanel DockPanel.Dock="Bottom" Background="LightGray">
<WrapPanel>
<TextBlock Text="{{ Binding Status }}"/>
</WrapPanel>
</StackPanel>
全局进度条
进度条本身只需要使用<ProgressBar>就可以了,但是为了突出一个全局的效果,就需要像菜单栏和状态栏一样,放在<DockPanel>中,例如固定在底部,另外再加上一个IsVisible的绑定,控制其显示和隐藏。
<DockPanel>
<ProgressBar DockPanel.Dock="Bottom" Value="{Binding Progress}"
IsVisible="{Binding Loading}"
/>
</DockPanel>
Tab栏上加按钮
Tab可以方便的做视图的切换,例如文件的切换等,但是如果Tab栏的内容不多的话,多少感觉有些空间的浪费,于是就想Tab栏右侧的位置是否将一些按钮放在那里。
在Avalonia的组件中没有找到直接类似的组件,大部分的布局控件并不会将组件堆叠在一起,所以最后使用了<Grid>来实现。
Grid 本身需要设置行列来确定组件的位置,但是如果不设置的话,组件的位置就可可能重叠。由此给操作按钮放在一个WrapPanel中,然后将WrapPanel放在Grid中,同时设置WrapPanel的HorizontalAlignment为Right,这样就可以将WrapPanel放在右侧了。
<Grid >
<TabControl TabStripPlacement="Top" >
<TabItem>
<TabItem.Header>
<i:Icon Value="mdi-file-edit-outline"/>
</TabItem.Header>
<ae:TextEditor
Classes="editor"
Document="{Binding Prompt}"
/>
</TabItem>
<TabItem>
<TabItem.Header>
<i:Icon Value="mdi-cogs"/>
</TabItem.Header>
<ae:TextEditor
Classes="editor"
Document="{Binding Config}"
SyntaxHighlighting="Json"
/>
</TabItem>
</TabControl>
<WrapPanel HorizontalAlignment="Right">
<Button IsVisible="False" Click="OnGenerateButtonClick" HotKey="Ctrl+Enter"/><!-- 一个隐藏的按钮添加额外的快捷键 -->
<Button Classes="MainBtn" Click="OnGenerateButtonClick" i:Attached.Icon="mdi-play" HotKey="Ctrl+G" IsVisible="{Binding !IsGenerating}"/>
<Button Classes="MainBtn" Click="OnCancelButtonClick" i:Attached.Icon="mdi-cancel" HotKey="Ctrl+C" IsVisible="{Binding IsGenerating}"/>
<Button Classes="MainBtn" Click="OnSaveButtonClick" i:Attached.Icon="mdi-content-save-outline" HotKey="Ctrl+S" IsEnabled="{Binding IsChanged}"/>
</WrapPanel>
</Grid>
类似的,文本框上加状态信息等,也都是使用Grid定位即可实现。
界面之下
配置文件的加载和保存
为了便于长期使用,需要将配置文件保存到本地,同时在启动时加载配置文件。
需要确定两个问题:
- 配置文件保存在哪里?
- 配置何时加载?
至于配置文件的格式就不多讨论,常用的json格式也好,ini格式也好,只要便于读取和保存就行。
如果将配置文件保存在应用的目录下,可能会随着应用更新或者迁移导致配置文件丢失,因此最好将配置文件保存在用户目录下或者操作系统为应用提供的目录下。
例如使用以下方法就可以获取用户目录下配置文件的地址了。
const string _configFileName = "config.json";
var profile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var configPath = Path.Combine(profile, _configFileName);
配置作为全局的属性,需要在应用启动的时候加载,于是就可以将配置放在MainViewModel中,在初始化的时候加载。
public partial class MainViewModel : ViewModelBase
{
public ConfigViewModel Config { get; set; } = ConfigViewModel.Load();
}
public partial class ConfigViewModel : ViewModelBasse {
public static ConfigViewModel Load(){
// 读取配置文件
}
}
不同窗口之间的数据同步
不同窗口之间的数据传输重要还是使用ViewModel。
在Avalonia中,窗口的数据对象均为 DataContext,因此就可以通过设置DataContext来传递数据。
例如在主界面打开配置窗口的时候,将配置窗口的DataContext设置为MainViewModel中的Config,这样就可以在配置窗口中修改配置,同时主界面也能够获取到修改后的配置。
private void OnConfigClick(object sender, RoutedEventArgs e)
{
var configWindow = new ConfigWindow()
{
DataContext = model.Config,
ShowInTaskbar = false,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
configWindow.ShowDialog(mainWindow);
}
界面之外
生成安装包
为了便于应用的分享和使用,就需要为应用生成安装包。
当然编译后打包的文件也可以直接分享,但是这样的话,用户就需要自己去下载覆盖更新,而且也不方便应用管理。同时如果支持自动更新的话,也不方便更新。
经过探索,找到了InnoSetup这个工具,可以将编译后的文件打包成安装包,同时还可以自定义安装界面,非常方便。
只需要为构建后的应用添加一个脚本文件,然后使用InnoSetup打包就可以了。InnoSetup 支持向导式的脚本生成,对于没有使用经验的人来说,也是相当方便了。
参与
Prompt Playground目前在Github上开源,使用.NET 7 + AvaloniaUI 进行开发,支持跨平台,欢迎大家参与。
如果有任何问题或者建议,也欢迎提issue或者PR。
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
Prompt Playground 7月开发记录(2): Avalonia 应用开发的更多相关文章
- 《CSOL大灾变》开发记录——武器购买逻辑开发
上次完成了武器购买界面设计,这次来完成武器购买逻辑与武器选择逻辑. 武器购买逻辑分为两个部分,一个部分是GUI部分的逻辑,也就是购买菜单,一个是武器游戏数据更新的逻辑,也就是实际中玩家获取武器的逻辑开 ...
- Anytime项目开发记录0
Anytime,中文名:我很忙. 开发者:孤独的猫咪神. 这个项目会持续更新,直到我决定不再维护这个APP. 2014年3月10日:近日有事,暂时断更.希望可以会尽快完事. 2014年3月27日:很抱 ...
- CozyRSS开发记录22-界面退化
CozyRSS开发记录22-界面退化 1.问题1-HtmlTextBlock 找的这个HtmlTextBlock有很严重的bug,有时候显示不完全,有时候直接就崩了.然后看了下代码,完全是学生仔水平写 ...
- CozyRSS开发记录21-默认RSS源列表
CozyRSS开发记录21-默认RSS源列表 1.默认列表 在第一次使用CozyRSS的情况下,我们让它内置五个RSS源吧: 2.响应RSS源的更新 先不处理RSS源列表项的点击,响应下下拉菜单里的更 ...
- CozyRSS开发记录20-CanResizeWithGrip
CozyRSS开发记录20-CanResizeWithGrip 1.窗口样式 首先,WindowStyle有四种: 然后,对于窗口缩放的ResizeMode,也有四种,CanResize和CanRes ...
- CozyRSS开发记录19-窗口标题栏交互
CozyRSS开发记录19-窗口标题栏交互 1.谈谈对mvvm解耦的看法 在使用mvvm时,如何操作窗口,这是一个问题.这个问题的关键点是:mvvm是把view和viewmodel解耦了的,很多写法一 ...
- CozyRSS开发记录18-番外之Atom1.0的支持
CozyRSS开发记录18-番外之Atom1.0的支持 1.对CozyRSS.Syndication批判一番 由于我工作的主要开发语言是c++,所以会看到我的c#代码写得非常朴素,很多语法糖都没有用上 ...
- CozyRSS开发记录17-Html2Xaml
CozyRSS开发记录17-Html2Xaml 1.RssContentView还需要优化 上回做了RssContentView的显示,但是对于rss返回的描述(摘要),连换行的没有,更别说里面还有h ...
- CozyRSS开发记录16-RssContentView显示
CozyRSS开发记录16-RssContentView显示 1.RssContentView的布局和绑定 继续参照原型图来写xaml: 然后在RSSContentFrameViewModel里提供绑 ...
- CozyRSS开发记录15-获取和显示RSS内容
CozyRSS开发记录15-获取和显示RSS内容 1.内容列表 我们先给RSSContentFrame增加一个ViewModel,里面和RSS源列表一样,提供一个ObservableCollectio ...
随机推荐
- 在nuxt下引入外部js
最近在踩nuxt.js的坑,遇到了许多问题,在这里记录一下,方便以后查阅. 1.如何引入外部js 如果是插件,可以在package.json中写明名字和版本,在nuxt.config.js中注册,然后 ...
- S5PV210 | 裸机汇编LED流水灯实验
S5PV210 | 裸机汇编LED流水灯实验 目录 S5PV210 | 裸机汇编LED流水灯实验 开发板: 1.原理图 2.Datasheet相关 3.代码 3-1.代码实现(流水灯,仅作演示) 3- ...
- 【Linux】(小白向)详解VirtualBox网络配置-配置Linux网络
本文时间 2023-05-18 作者:sugerqube漆瓷 本文面向新手,重在理解会舍弃不少精密的理论,还请大佬们见谅. 本文目标:成功使用ssh工具登录linux,同时linux能连接外网. 网络 ...
- Odoo 13之十三 :开发之创建网站前端功能
Odoo 13开发之创建网站前端功能 Odoo 起初是一个后台系统,但很快就有了前端界面的需求.早期基于后台界面的门户界面不够灵活并且对移动端不友好.为解决这一问题,Odoo 引入了新的网站功能,为系 ...
- GitHub 的项目徽章
GitHub 项目的 README.md 中可以添加徽章(Badge)对项目进行标记和说明,这些好看的小图标不仅简洁美观,而且还包含了清晰易读的信息. GitHub 项目的徽标可以参考 https:/ ...
- JavaWeb编程面试题——Spring Framework
引言 面试题==知识点,这里所记录的面试题并不针对于面试者,而是将这些面试题作为技能知识点来看待.不以刷题进大厂为目的,而是以学习为目的.这里的知识点会持续更新,目录也会随时进行调整. 关注公众号:编 ...
- STL-set(ACM)
1.set只能insert().erase(),没有push()等操作 2.插入的元素自动排序按从小到大的顺序排 3.不会插入相同的元素,已经插入了6,之后就不会再插入了 4.时间复杂度为 O(log ...
- Linux多线程(8.3 线程同步与互斥)
3. 线程的同步与互斥 为什么需要同步与互斥 一个进程运行时,数据存储在内存中.如果一个数据要进行运算,必须先将数据拷贝到寄存器中.比如要对栈上的一个int i进行"++"操作 ...
- AI与健康管理:趋势与未来
目录 引言 随着人工智能技术的不断发展,健康管理也逐渐成为了一个新的研究领域.AI技术可以为健康管理提供智能化.个性化.高效的支持,使得健康管理更加人性化和科学.本文将介绍AI与健康管理的技术原理.实 ...
- Python运维开发之路《数据类型》
一. python数据类型 python的五大基本数据类型,数字.字符串.列表.元组.字典;其他数据类型,类型type.Null.文件.集合.函数/方法.类.模块. 1.数字 1 ①整型 2 十进制转 ...