您现在的位置是:网站首页> .NET Core

Avalonia .NET 的跨平台 UI 框架及其他GTK学习资料收集

摘要

Avalonia .NET 的跨平台 UI 框架及其他GTK学习资料收集

点击进入Avalonia主页

最有效办法是点击查看微软MAUI学习资料


1.jpg


dotnet命令行发布应用程序

***Avalonia.NET编译Android配置签名***

MAUI配置发布Android IOS等个平台独特的文件配置

Avalonia生成的程序位置

dotnet publish 发布win-x64程序,用这个命令行设置图标,给个例子

C#异常步函数await async

****************************************************************************************************

Avalonia常用的界面布局组件介绍和例子代码

Avalonia如何不采用数据绑定方式,手动更新界面,手动对组件赋值和获得值,给几个典型组件的代码例子

Avalonia数据列表组件不采用数据绑定方式

Avalonia常用的界面组件介绍和例子代码数据采用非绑定方式的

****************************************************************************************************

WPF应用转Avalonia

Avalonia点击查看库文档

vs2022的Avalonia扩展安装

Avalonia入门及学习

Avalonia 中,你可以通过几种方式向新窗口传递参数并获取返回值

基于Material Design风格开源的Avalonia UI控件库

Avalonia .NET实现NFC的读写的详细例子

Avalonia UI  报错

开源GTKSystem.Windows.Forms框架

Avalonia如何不采用数据绑定方式,手动更新界面,手动对组件赋值和获得值,给几个典型组件的代码例子

Avalonia数据列表组件不采用数据绑定方式

Avalonia常用的界面组件介绍和例子代码数据采用非绑定方式的

Avalonia常用的界面布局组件介绍和例子代码



Avalonia .NET编译Android配置签名

1.png

对Android项目右键配置项目属性点Android项,划到包签名



Avalonia生成的程序位置

1.png




Avalonia 中,你可以通过几种方式向新窗口传递参数并获取返回值

1.首先,创建一个新的窗口类,比如 SecondWindow.axaml 和 SecondWindow.axaml.cs。


2.在 SecondWindow.axaml.cs 中,添加构造函数和属性来接收参数,并添加一个方法来返回结果:

public partial class SecondWindow : Window

{

    public string InputParameter { get; set; }

    public string Result { get; private set; }


    public SecondWindow()

    {

        InitializeComponent();

    }


    public SecondWindow(string inputParameter) : this()

    {

        InputParameter = inputParameter;

    }


    private void OnOKButtonClick(object sender, RoutedEventArgs e)

    {

        Result = "处理完成";

        Close();

    }

}


3.在主窗口 MainWindow.axaml.cs 中,添加一个方法来打开新窗口并等待结果:

private async void OpenSecondWindowButton_Click(object sender, RoutedEventArgs e)

{

    var secondWindow = new SecondWindow("这是传入的参数");

    var result = await ShowDialogAsync(secondWindow);


    if (result == true)

    {

        // 获取返回值

        string returnValue = secondWindow.Result;

        // 处理返回值

        await MessageBoxManager.GetMessageBoxStandardWindow("结果", $"返回值: {returnValue}").ShowAsync();

    }

}


private async Task<bool?> ShowDialogAsync(Window window)

{

    var tcs = new TaskCompletionSource<bool?>();

    window.Closed += (s, e) => tcs.TrySetResult(window.DialogResult);

    await window.ShowDialog(this);

    return await tcs.Task;

}


4.在 SecondWindow.axaml 中,添加一个按钮来关闭窗口并设置结果:

<Window xmlns="https://github.com/avaloniaui"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        x:Class="YourNamespace.SecondWindow"

        Title="Second Window">

    <StackPanel>

        <TextBlock Text="{Binding InputParameter}" />

        <Button Content="确定" Click="OnOKButtonClick" />

    </StackPanel>

</Window>


5.在主窗口 MainWindow.axaml 中,添加一个按钮来打开新窗口:

<Window xmlns="https://github.com/avaloniaui"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        x:Class="YourNamespace.MainWindow"

        Title="Main Window">

    <Button Content="打开新窗口" Click="OpenSecondWindowButton_Click" />

</Window>


这个例子展示了如何:

1.创建一个新窗口并传递参数(通过构造函数)。

2.在新窗口中处理参数并设置返回值。

3.在主窗口中等待新窗口关闭并获取返回值。

4.使用 TaskCompletionSource 来异步等待窗口关闭。

通过这种方式,你可以方便地在 Avalonia 应用程序中实现窗口间的参数传递和返回值获取。




基于Material Design风格开源的Avalonia UI控件库

点击查看原文



Avalonia .NET实现NFC的读写的详细例子

你可以在Avalonia应用中集成NFC功能,通过使用其他.NET库或原生API来实现。以下是一个概念性的例子,说明如何在Avalonia应用中集成NFC功能:

首先,你需要一个NFC库。在.NET生态系统中,没有一个广泛使用的跨平台NFC库。你可能需要根据目标平台选择不同的实现方式。

对于Windows,你可以使用Windows.Devices.Nfc命名空间。

对于Android,你可以使用Xamarin.Android的NFC API。

对于iOS,你可以使用CoreNFC框架。

下面是一个概念性的例子,展示了如何在Avalonia应用中集成NFC读取功能(以Windows平台为例):

using Avalonia;

using Avalonia.Controls;

using Avalonia.Markup.Xaml;

using Windows.Devices.Nfc;

using Windows.Devices.Enumeration;


public class MainWindow : Window

{

    private NfcReader _nfcReader;


    public MainWindow()

    {

        InitializeComponent();

        InitializeNfcReader();

    }


    private async void InitializeNfcReader()

    {

        var selector = NfcReader.GetDeviceSelector();

        var devices = await DeviceInformation.FindAllAsync(selector);

        if (devices.Count > 0)

        {

            _nfcReader = await NfcReader.FromIdAsync(devices[0].Id);

            _nfcReader.TagRead += OnTagRead;

        }

    }


    private void OnTagRead(NfcReader sender, NfcTagEventArgs args)

    {

        var tag = args.Tag;

        // 处理NFC标签数据

        Dispatcher.UIThread.InvokeAsync(() =>

        {

            // 更新UI

        });

    }


    // XAML初始化代码

    private void InitializeComponent()

    {

        AvaloniaXamlLoader.Load(this);

    }

}

这个例子展示了如何初始化NFC读取器并处理标签读取事件。你需要根据实际需求修改UI更新逻辑。


对于写入NFC标签,过程类似,但你需要使用NfcWriter类。例如:

private async Task WriteToTag(NdefMessage message)

{

    var writer = new NfcWriter();

    await writer.WriteNdefMessageAsync(message);

}

请注意,这只是一个概念性的例子。实际实现可能会更复杂,需要考虑错误处理、不同平台的兼容性等因素。此外,由于NFC操作涉及硬件访问,你可能需要请求适当的权限。


在跨平台场景中,你可能需要为每个平台创建单独的NFC实现,然后通过依赖注入或其他方式在Avalonia应用中使用



Avalonia UI  报错

报错:System.InvalidOperationException:“Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date.

解决办法:

原因

未生成开发证书:

.NET 开发环境下默认需要 HTTPS 证书,若未生成或证书缺失,启动时会报此错误。


证书过期:

.NET 开发证书有效期为 1 年,过期后需重新生成。


项目未正确配置 HTTPS:

若项目中启用了 HTTPS 但未指定证书路径,可能导致此问题。


解决方案

1. 生成并信任开发证书

Windows/macOS/Linux

在终端执行以下命令:

# 检查证书状态

dotnet dev-certs https --check


# 清理旧证书(如有必要)

dotnet dev-certs https --clean


# 生成新证书

dotnet dev-certs https --trust



dotnet publish 发布win-x64程序,用这个命令行设置图标,给个例子

使用 dotnet publish 命令发布 Win-x64 程序时,希望通过命令行设置可执行文件的图标。虽然 dotnet publish 本身不直接支持通过命令行参数来设置图标,但您可以结合修改项目文件(.csproj)的方式来实现这一点。

下面是详细的步骤和示例:

步骤

准备图标文件: 确保你有一个 ICO 格式的图标文件(例如 appicon.ico),并将它放在你的项目目录中或其他合适的位置。

修改项目文件 (.csproj): 编辑你的 .csproj 文件,添加必要的元素来指定图标。

使用 dotnet publish 命令发布应用程序: 运行 dotnet publish 命令来发布你的应用程序。

示例

假设你有一个名为 MyApp.csproj 的控制台应用程序,并且你想将 appicon.ico 设置为其可执行文件的图标。

修改 .csproj 文件

首先,打开你的 .csproj 文件并在适当位置添加以下内容:

<Project Sdk="Microsoft.NET.Sdk">


    <PropertyGroup>

        <OutputType>Exe</OutputType>

        <TargetFramework>net7.0-windows</TargetFramework>

        

        <!-- 添加这些行来指定图标 -->

        <ApplicationIcon>path/to/appicon.ico</ApplicationIcon>

        <WinExeIconFromBuildAsResource>false</WinExeIconFromBuildAsResource>

    </PropertyGroup>


    <!-- ...其他配置... -->


</Project>

在这个示例中:

<ApplicationIcon> 指定了图标的相对路径。

<WinExeIconFromBuildAsResource> 设置为 false 表明不要将图标作为资源嵌入到可执行文件中。

如果 appicon.ico 放在项目的根目录下,可以直接写成:

<ApplicationIcon>appicon.ico</ApplicationIcon>

如果图标不在项目根目录下,请提供完整路径或相对于 .csproj 文件的路径。


使用 dotnet publish 命令发布应用程序

接下来,使用 dotnet publish 命令来发布你的应用程序:

# 使用 release 构建模式并且针对 windows x64 平台发布

dotnet publish -c Release -r win-x64 --self-contained false /p:PublishSingleFile=true /p:PublishTrimmed=true


这条命令做了以下几件事:

-c Release: 使用 Release 构建模式。

-r win-x64: 针对 Windows 64-bit 平台发布。

--self-contained false: 不包含 .NET Runtime,减少最终生成文件大小。

/p:PublishSingleFile=true: 将所有依赖项合并到一个单一的可执行文件中。

/p:PublishTrimmed=true: 启用 IL Trimming,去除不需要的部分以减小文件大小。


完整示例

假设你的项目结构如下:

MyApp/

├── MyApp.csproj

└── appicon.ico

MyApp.csproj 内容

<Project Sdk="Microsoft.NET.Sdk">


    <PropertyGroup>

        <OutputType>Exe</OutputType>

        <TargetFramework>net7.0-windows</TargetFramework>

        <ApplicationIcon>appicon.ico</ApplicationIcon>

        <WinExeIconFromBuildAsResource>false</WinExeIconFromBuildAsResource>

    </PropertyGroup>


</Project>

发布命令

dotnet publish -c Release -r win-x64 --self-contained false /p:PublishSingleFile=true /p:PublishTrimmed=true



vs2022的Avalonia扩展安装

vs2022的Avalonia扩展安装点击查看原文

// 安装模板

dotnet new install Avalonia.Templates

// 验证

 dotnet new list

1.jpg

2.jpg

Avalonia for Visual Studio 2022.rar



Avalonia入门及学习

Avalonia入门及学习点击查看原文



开源GTKSystem.Windows.Forms框架

点击查看原文

开发项目时,Visual Studio可以使用C#的原生WinForms表单窗体设计器,保持与原生WinForms相同的属性、方法和事件,无需额外学习。通过一次编译,可以实现跨平台运行,便于开发跨平台WinForms软件,以及将现有的C# WinForms软件升级为跨平台软件。

使用GTK3.24.24.95作为表单UI重写C#的System.Windows.Forms组件,在应用时,兼容原生C#程序组件。


Visual Studio插件安装

从NuGet上安装GTKSystem.Windows.FormsDesigner类库,此类库可以在编译工程时修正窗体设计器。

下载本插件工具,关闭Visual Studio 2022,直接双击GTKWinformVSIXProject.vsix文件安装(本框架下的工程,Studio没有添加Form模板项,需要安装此插件)。


插件会安装两个功能:

1、新建项的Form窗体模板、用户控件模板。

2、工程右键菜单。

1.jpg



Avalonia如何不采用数据绑定方式,手动更新界面,手动对组件赋值和获得值,给几个典型组件的代码例子

几种典型控件的手动操作方式:

获取控件引用:使用FindControl<T>()方法通过 Name 属性获取控件实例

1.TextBox 文本框

设置值:_inputTextBox.Text = "初始文本";

获取值:string value = _inputTextBox.Text;

2.Button 按钮

设置文本:通过 XAML 的 Content 属性

注册点击事件:_submitButton.Click += (sender, e) => { ... };

3.TextBlock 文本块

设置显示文本:_displayTextBlock.Text = "等待输入...";

4.CheckBox 复选框

检查状态:_agreeCheckBox.IsChecked

注册状态改变事件:Checked和Unchecked

5.ComboBox 下拉列表

添加项:_optionsComboBox.Items.Add("选项1");

设置选中项:_optionsComboBox.SelectedIndex = 0;

获取选中项:_optionsComboBox.SelectedItem

注册选择改变事件:SelectionChanged

6.Slider 滑块

设置值:_volumeSlider.Value = 50;

获取值:_volumeSlider.Value

注册值改变事件:ValueChanged

这种方式完全不依赖数据绑定,所有 UI 更新和值获取都是通过直接操作控件实例完成的,适合一些简单场景或需要更直接控制 UI 的情况。


MainWindow.xaml

<Window xmlns="https://github.com/avaloniaui"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        x:Class="ManualUIUpdateExample.MainWindow"

        Title="手动更新界面示例">

    <StackPanel Margin="20" Spacing="10">

        <!-- 文本框 -->

        <TextBox Name="InputTextBox" Width="200" />

        

        <!-- 按钮 -->

        <Button Name="SubmitButton" Content="点击我" Width="100" />

        

        <!-- 文本块 -->

        <TextBlock Name="DisplayTextBlock" />

        

        <!-- 复选框 -->

        <CheckBox Name="AgreeCheckBox" Content="同意条款" />

        

        <!-- 下拉列表 -->

        <ComboBox Name="OptionsComboBox" Width="200" />

        

        <!-- 滑块 -->

        <Slider Name="VolumeSlider" Width="200" Minimum="0" Maximum="100" />

        

        <!-- 滑块值显示 -->

        <TextBlock Name="SliderValueTextBlock" />

    </StackPanel>

</Window>


MainWindow.xaml.cs

using Avalonia;

using Avalonia.Controls;

using Avalonia.Markup.Xaml;


namespace ManualUIUpdateExample

{

    public partial class MainWindow : Window

    {

        // 声明控件变量

        private TextBox _inputTextBox;

        private Button _submitButton;

        private TextBlock _displayTextBlock;

        private CheckBox _agreeCheckBox;

        private ComboBox _optionsComboBox;

        private Slider _volumeSlider;

        private TextBlock _sliderValueTextBlock;


        public MainWindow()

        {

            InitializeComponent();

#if DEBUG

            this.AttachDevTools();

#endif

            // 初始化控件引用

            InitializeControls();

            

            // 设置初始值

            SetInitialValues();

            

            // 注册事件

            RegisterEvents();

        }


        private void InitializeComponent()

        {

            AvaloniaXamlLoader.Load(this);

        }


        private void InitializeControls()

        {

            // 获取控件引用

            _inputTextBox = this.FindControl<TextBox>("InputTextBox");

            _submitButton = this.FindControl<Button>("SubmitButton");

            _displayTextBlock = this.FindControl<TextBlock>("DisplayTextBlock");

            _agreeCheckBox = this.FindControl<CheckBox>("AgreeCheckBox");

            _optionsComboBox = this.FindControl<ComboBox>("OptionsComboBox");

            _volumeSlider = this.FindControl<Slider>("VolumeSlider");

            _sliderValueTextBlock = this.FindControl<TextBlock>("SliderValueTextBlock");

        }


        private void SetInitialValues()

        {

            // 手动设置控件初始值

            _inputTextBox.Text = "初始文本";

            _displayTextBlock.Text = "等待输入...";

            

            // 向下拉列表添加项

            _optionsComboBox.Items.Add("选项1");

            _optionsComboBox.Items.Add("选项2");

            _optionsComboBox.Items.Add("选项3");

            

            // 设置默认选中项

            _optionsComboBox.SelectedIndex = 0;

            

            // 设置滑块初始值

            _volumeSlider.Value = 50;

            _sliderValueTextBlock.Text = $"滑块值: {_volumeSlider.Value}";

        }


        private void RegisterEvents()

        {

            // 按钮点击事件

            _submitButton.Click += (sender, e) => 

            {

                // 获取文本框的值并设置到文本块

                _displayTextBlock.Text = $"你输入了: {_inputTextBox.Text}";

            };

            

            // 复选框状态改变事件

            _agreeCheckBox.Checked += (sender, e) => 

            {

                _displayTextBlock.Text = "已同意条款";

            };

            

            _agreeCheckBox.Unchecked += (sender, e) => 

            {

                _displayTextBlock.Text = "未同意条款";

            };

            

            // 下拉列表选择改变事件

            _optionsComboBox.SelectionChanged += (sender, e) => 

            {

                if (_optionsComboBox.SelectedItem != null)

                {

                    _displayTextBlock.Text = $"选中了: {_optionsComboBox.SelectedItem}";

                }

            };

            

            // 滑块值改变事件

            _volumeSlider.ValueChanged += (sender, e) => 

            {

                // 实时更新滑块值显示

                _sliderValueTextBlock.Text = $"滑块值: {e.NewValue}";

            };

        }

    }

}



Avalonia数据列表组件不采用数据绑定方式

MainWindow.xaml


<Window xmlns="https://github.com/avaloniaui"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"

        x:Class="ListBoxWithXamlTemplate.MainWindow"

        Title="带XAML模板的列表示例">

    <StackPanel Margin="20" Spacing="15">

        <TextBlock FontSize="20" FontWeight="Bold">带图片和文本的列表</TextBlock>

        

        <!-- 输入区域 -->

        <StackPanel Spacing="8">

            <TextBox x:Name="ImagePathInput" PlaceholderText="图片路径 (URL或本地路径)"/>

            <TextBox x:Name="TitleInput" PlaceholderText="标题"/>

            <TextBox x:Name="DescriptionInput" PlaceholderText="描述" AcceptsReturn="True" Height="80"/>

            

            <StackPanel Orientation="Horizontal" Spacing="10">

                <Button x:Name="AddButton" Content="添加" Width="80" Classes="btn-primary"/>

                <Button x:Name="EditButton" Content="编辑" Width="80" Classes="btn-secondary"/>

                <Button x:Name="DeleteButton" Content="删除" Width="80" Classes="btn-danger"/>

            </StackPanel>

        </StackPanel>

        

        <!-- 列表区域 -->

        <ListBox x:Name="DataListBox" Height="300" Margin="0 10">

            <!-- 列表项模板定义在XAML中 -->

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <StackPanel Orientation="Horizontal" Spacing="12" Margin="5" Padding="5" 

                                Background="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}, 

                                Converter={x:Static BoolToBrushConverter.Instance}}">

                        <!-- 图片控件 -->

                        <Image x:Name="ItemImage" Width="100" Height="100" Stretch="UniformToFill" 

                               Margin="0 5"/>

                        

                        <!-- 文本内容容器 -->

                        <StackPanel VerticalAlignment="Center" Spacing="5" Margin="5 0">

                            <TextBlock x:Name="ItemTitle" FontSize="16" FontWeight="Bold"/>

                            <TextBlock x:Name="ItemDescription" FontSize="12" Foreground="Gray" 

                                       MaxWidth="500" TextWrapping="Wrap"/>

                        </StackPanel>

                    </StackPanel>

                </DataTemplate>

            </ListBox.ItemTemplate>

        </ListBox>

        

        <!-- 状态信息 -->

        <TextBlock x:Name="StatusText" FontSize="12" Foreground="DarkSlateBlue"/>

    </StackPanel>

</Window>



MainWindow.xaml.cs


using Avalonia;

using Avalonia.Controls;

using Avalonia.Data.Converters;

using Avalonia.Media;

using Avalonia.Media.Imaging;

using Avalonia.Platform;

using System;

using System.Collections.Generic;

using System.Globalization;

using System.IO;


namespace ListBoxWithXamlTemplate

{

    // 辅助转换器:根据选中状态改变背景色

    public class BoolToBrushConverter : IValueConverter

    {

        public static readonly BoolToBrushConverter Instance = new BoolToBrushConverter();


        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

        {

            if (value is bool isSelected && isSelected)

                return new SolidColorBrush(Color.FromArgb(20, 0, 120, 215));

            return Brushes.Transparent;

        }


        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

        {

            throw new NotImplementedException();

        }

    }


    // 数据项模型类

    public class ListItem

    {

        public string Id { get; }

        public string ImagePath { get; set; }

        public string Title { get; set; }

        public string Description { get; set; }

        public Bitmap Image { get; set; }


        public ListItem(string imagePath, string title, string description)

        {

            Id = Guid.NewGuid().ToString();

            ImagePath = imagePath;

            Title = title;

            Description = description;

            LoadImage();

        }


        // 加载图片

        public void LoadImage()

        {

            try

            {

                if (!string.IsNullOrEmpty(ImagePath))

                {

                    if (ImagePath.StartsWith("http"))

                    {

                        Image = new Bitmap(new Uri(ImagePath));

                    }

                    else if (File.Exists(ImagePath))

                    {

                        Image = new Bitmap(ImagePath);

                    }

                    else

                    {

                        Image = GetDefaultImage();

                    }

                }

                else

                {

                    Image = GetDefaultImage();

                }

            }

            catch

            {

                Image = GetDefaultImage();

            }

        }


        // 获取默认图片

        private Bitmap GetDefaultImage()

        {

            return new Bitmap(AssetLoader.Open(new Uri("resm:Avalonia.Controls.Resources.DefaultIcon.png")));

        }

    }


    public partial class MainWindow : Window

    {

        // 控件引用

        private TextBox _imagePathInput;

        private TextBox _titleInput;

        private TextBox _descriptionInput;

        private Button _addButton;

        private Button _editButton;

        private Button _deleteButton;

        private ListBox _dataListBox;

        private TextBlock _statusText;

        

        // 数据集合

        private List<ListItem> _dataItems = new List<ListItem>();


        public MainWindow()

        {

            InitializeComponent();

#if DEBUG

            this.AttachDevTools();

#endif

            

            // 初始化控件引用

            InitializeControls();

            

            // 注册事件处理

            RegisterEvents();

            

            // 设置初始数据

            SetInitialData();

        }


        private void InitializeComponent()

        {

            AvaloniaXamlLoader.Load(this);

        }


        private void InitializeControls()

        {

            // 获取所有控件的引用

            _imagePathInput = this.FindControl<TextBox>("ImagePathInput");

            _titleInput = this.FindControl<TextBox>("TitleInput");

            _descriptionInput = this.FindControl<TextBox>("DescriptionInput");

            _addButton = this.FindControl<Button>("AddButton");

            _editButton = this.FindControl<Button>("EditButton");

            _deleteButton = this.FindControl<Button>("DeleteButton");

            _dataListBox = this.FindControl<ListBox>("DataListBox");

            _statusText = this.FindControl<TextBlock>("StatusText");

        }


        private void RegisterEvents()

        {

            _addButton.Click += AddButton_Click;

            _editButton.Click += EditButton_Click;

            _deleteButton.Click += DeleteButton_Click;

            _dataListBox.SelectionChanged += DataListBox_SelectionChanged;

            _dataListBox.ItemPrepared += DataListBox_ItemPrepared;

        }


        private void SetInitialData()

        {

            // 添加初始数据

            _dataItems.Add(new ListItem(

                "https://picsum.photos/id/237/100/100", 

                "小狗", 

                "一只可爱的小狗,正在草地上玩耍"));

            

            _dataItems.Add(new ListItem(

                "https://picsum.photos/id/10/100/100", 

                "山脉风景", 

                "壮丽的山脉景观,蓝天白云相映成趣"));

            

            _dataItems.Add(new ListItem(

                "https://picsum.photos/id/96/100/100", 

                "花朵", 

                "鲜艳的花朵,展现了大自然的美丽"));

            

            UpdateListDisplay();

            UpdateStatusText($"初始数据加载完成,共 {_dataItems.Count} 项");

        }


        /// <summary>

        /// 当列表项准备好时手动设置UI元素的值

        /// </summary>

        private void DataListBox_ItemPrepared(object sender, ItemPreparedEventArgs e)

        {

            // 获取数据项

            var listItem = e.Item as ListItem;

            if (listItem == null) return;

            

            // 获取模板中的UI元素

            var image = e.Container.FindControl<Image>("ItemImage");

            var title = e.Container.FindControl<TextBlock>("ItemTitle");

            var description = e.Container.FindControl<TextBlock>("ItemDescription");

            

            // 手动设置UI元素的值

            if (image != null) image.Source = listItem.Image;

            if (title != null) title.Text = listItem.Title;

            if (description != null) description.Text = listItem.Description;

        }


        /// <summary>

        /// 手动更新列表显示

        /// </summary>

        private void UpdateListDisplay()

        {

            // 保存当前选中项ID

            var selectedItem = _dataListBox.SelectedItem as ListItem;

            string selectedId = selectedItem?.Id;

            

            // 清空并重新填充列表

            _dataListBox.Items.Clear();

            foreach (var item in _dataItems)

            {

                _dataListBox.Items.Add(item);

            }

            

            // 恢复选中状态

            if (!string.IsNullOrEmpty(selectedId))

            {

                var itemToSelect = _dataItems.Find(i => i.Id == selectedId);

                if (itemToSelect != null)

                {

                    _dataListBox.SelectedItem = itemToSelect;

                }

            }

        }


        private void AddButton_Click(object sender, EventArgs e)

        {

            if (string.IsNullOrWhiteSpace(_titleInput.Text))

            {

                UpdateStatusText("请输入标题");

                return;

            }

            

            var newItem = new ListItem(

                _imagePathInput.Text,

                _titleInput.Text.Trim(),

                _descriptionInput.Text?.Trim() ?? ""

            );

            

            _dataItems.Add(newItem);

            UpdateListDisplay();

            ClearInputFields();

            UpdateStatusText($"已添加: {newItem.Title}");

            

            // 选中新添加的项

            if (_dataListBox.Items.Count > 0)

            {

                _dataListBox.SelectedIndex = _dataListBox.Items.Count - 1;

            }

        }


        private void EditButton_Click(object sender, EventArgs e)

        {

            var selectedItem = _dataListBox.SelectedItem as ListItem;

            if (selectedItem == null)

            {

                UpdateStatusText("请先选择要编辑的项");

                return;

            }

            

            if (string.IsNullOrWhiteSpace(_titleInput.Text))

            {

                UpdateStatusText("请输入标题");

                return;

            }

            

            string oldTitle = selectedItem.Title;

            

            // 更新数据

            selectedItem.ImagePath = _imagePathInput.Text;

            selectedItem.Title = _titleInput.Text.Trim();

            selectedItem.Description = _descriptionInput.Text?.Trim() ?? "";

            selectedItem.LoadImage(); // 重新加载图片

            

            UpdateListDisplay();

            UpdateStatusText($"已更新: {oldTitle} -> {selectedItem.Title}");

        }


        private void DeleteButton_Click(object sender, EventArgs e)

        {

            var selectedItem = _dataListBox.SelectedItem as ListItem;

            if (selectedItem == null)

            {

                UpdateStatusText("请先选择要删除的项");

                return;

            }

            

            string deletedTitle = selectedItem.Title;

            _dataItems.Remove(selectedItem);

            UpdateListDisplay();

            ClearInputFields();

            UpdateStatusText($"已删除: {deletedTitle}");

        }


        private void DataListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)

        {

            var selectedItem = _dataListBox.SelectedItem as ListItem;

            if (selectedItem != null)

            {

                _imagePathInput.Text = selectedItem.ImagePath;

                _titleInput.Text = selectedItem.Title;

                _descriptionInput.Text = selectedItem.Description;

                UpdateStatusText($"已选择: {selectedItem.Title}");

            }

        }


        private void ClearInputFields()

        {

            _imagePathInput.Text = string.Empty;

            _titleInput.Text = string.Empty;

            _descriptionInput.Text = string.Empty;

        }


        private void UpdateStatusText(string message)

        {

            _statusText.Text = $"状态: {message}";

        }

    }

}




1.XAML 模板定义:

在 XAML 中通过ListBox.ItemTemplate定义了列表项的 UI 结构

模板包含 Image 和两个 TextBlock,分别显示图片、标题和描述

使用转换器实现了选中项的背景色变化效果

2.手动数据绑定:

通过ItemPrepared事件手动将数据项的值设置到 UI 元素上

不使用 Avalonia 的数据绑定表达式,完全手动控制值的传递

3.核心实现逻辑:

当列表项准备好显示时,DataListBox_ItemPrepared事件被触发

在事件处理中,找到模板中的 UI 元素并手动设置其属性

数据更新后通过UpdateListDisplay方法刷新整个列表

4.保持原有功能:

保留了完整的添加、编辑、删除功能

维持了选中状态记忆和输入框自动填充功能

这种方式结合了 XAML 的 UI 定义优势和手动控制数据的灵活性,既便于设计复杂的 UI 结构,又能精确控制数据流向和 UI 更新时机。对于需要精细控制 UI 渲染过程的场景非常适用。



Avalonia常用的界面组件介绍和例子代码数据采用非绑定方式的

Avalonia 是一个跨平台的.NET UI 框架,提供了丰富的界面组件。以下是一些常用组件的介绍和非绑定方式的示例代码:

1. Button(按钮)

按钮是最常用的交互组件,用于触发操作。

XAML 示例:

xml

<Button Name="MyButton" Content="点击我" Width="100" Height="30" />

C# 代码(事件处理):

csharp

public MainWindow()

{

    InitializeComponent();

    var button = this.FindControl<Button>("MyButton");

    button.Click += (sender, e) => 

    {

        button.Content = "已点击";

    };

}

2. TextBlock(文本块)

用于显示文本内容,不可编辑。

XAML 示例:

xml

<TextBlock Name="MyTextBlock" Text="初始文本" FontSize="16" Foreground="Blue" />

C# 代码:

csharp

var textBlock = this.FindControl<TextBlock>("MyTextBlock");

textBlock.Text = "更新后的文本";

textBlock.Foreground = Brushes.Red;

3. TextBox(文本框)

用于输入和编辑文本。

XAML 示例:

xml

<TextBox Name="MyTextBox" Width="200" Height="30" Text="默认文本" />

C# 代码:

csharp

var textBox = this.FindControl<TextBox>("MyTextBox");

// 设置文本

textBox.Text = "通过代码设置的文本";

// 获取文本

string inputText = textBox.Text;

4. CheckBox(复选框)

用于表示布尔值选项,可选中或取消选中。

XAML 示例:

xml

<CheckBox Name="MyCheckBox" Content="同意条款" />

C# 代码:

csharp

var checkBox = this.FindControl<CheckBox>("MyCheckBox");

// 设置选中状态

checkBox.IsChecked = true;

// 获取选中状态

bool? isChecked = checkBox.IsChecked;


checkBox.Checked += (sender, e) => 

{

    // 处理选中事件

};

5. RadioButton(单选按钮)

用于从多个选项中选择一个,通常成组使用。

XAML 示例:

xml

<StackPanel>

    <RadioButton Name="Radio1" GroupName="Options" Content="选项1" />

    <RadioButton Name="Radio2" GroupName="Options" Content="选项2" />

</StackPanel>

C# 代码:

csharp

var radio1 = this.FindControl<RadioButton>("Radio1");

var radio2 = this.FindControl<RadioButton>("Radio2");


radio1.IsChecked = true; // 设置为选中


radio1.Checked += (sender, e) => 

{

    // 处理选中事件

};

6. ComboBox(下拉列表)

用于从预定义列表中选择一项。

XAML 示例:

xml

<ComboBox Name="MyComboBox" Width="150" Height="30" />

C# 代码:

csharp

var comboBox = this.FindControl<ComboBox>("MyComboBox");


// 添加选项

comboBox.Items.Add("选项1");

comboBox.Items.Add("选项2");

comboBox.Items.Add("选项3");


// 设置默认选中项

comboBox.SelectedIndex = 0;


// 处理选择变化事件

comboBox.SelectionChanged += (sender, e) => 

{

    var selectedItem = comboBox.SelectedItem;

};

7. Slider(滑块)

用于从一定范围内选择数值。

XAML 示例:

xml

<Slider Name="MySlider" Width="200" Minimum="0" Maximum="100" />

C# 代码:

csharp

var slider = this.FindControl<Slider>("MySlider");

slider.Value = 50; // 设置初始值


slider.ValueChanged += (sender, e) => 

{

    double value = slider.Value;

};

8. ProgressBar(进度条)

用于显示操作的进度。

XAML 示例:

xml

<ProgressBar Name="MyProgressBar" Width="200" Height="20" />

C# 代码:

csharp

var progressBar = this.FindControl<ProgressBar>("MyProgressBar");

progressBar.Maximum = 100;

progressBar.Value = 30; // 设置当前进度


// 模拟进度更新

var timer = new DispatcherTimer

{

    Interval = TimeSpan.FromSeconds(0.5)

};

timer.Tick += (sender, e) =>

{

    progressBar.Value += 5;

    if (progressBar.Value >= progressBar.Maximum)

    {

        timer.Stop();

    }

};

timer.Start();

这些示例展示了 Avalonia 中常用组件的基本用法,采用了非绑定方式,直接通过代码操作组件的属性和事件。在实际应用中,你可以根据需要组合这些组件,创建复杂的用户界面。



Avalonia常用的界面布局组件介绍和例子代码

在 Avalonia 中,布局组件用于控制界面元素的排列方式,是构建复杂 UI 的基础。以下是常用的布局组件介绍及示例代码:

1. StackPanel(栈式布局)

作用:按垂直或水平方向线性排列子元素,适合简单的列表式布局。特点:子元素会按顺序排列,不会自动换行(垂直方向高度自适应,水平方向宽度自适应)。

XAML 示例:

xml

<!-- 垂直排列(默认) -->

<StackPanel Margin="10" Spacing="8">

    <Button Content="按钮1" Width="100"/>

    <Button Content="按钮2" Width="100"/>

    <Button Content="按钮3" Width="100"/>

</StackPanel>


<!-- 水平排列 -->

<StackPanel Margin="10" Orientation="Horizontal" Spacing="8">

    <Button Content="按钮A" Height="30"/>

    <Button Content="按钮B" Height="30"/>

    <Button Content="按钮C" Height="30"/>

</StackPanel>

C# 动态添加元素:

csharp

var stackPanel = this.FindControl<StackPanel>("MyStackPanel");

// 添加新按钮

var newButton = new Button { Content = "动态添加的按钮", Width = 120 };

stackPanel.Children.Add(newButton);

2. Grid(网格布局)

作用:通过行和列定义分割区域,适合复杂的结构化布局(如表单、多区域界面)。特点:子元素可指定占用的行 / 列范围,支持比例或固定尺寸定义。

XAML 示例:

xml

<Grid Margin="10">

    <!-- 定义3行:第1行高50,第2行高*(剩余空间),第3行高2*(剩余空间的2倍) -->

    <Grid.RowDefinitions>

        <RowDefinition Height="50"/>

        <RowDefinition Height="*"/>

        <RowDefinition Height="2*"/>

    </Grid.RowDefinitions>

    

    <!-- 定义2列:第1列宽100,第2列宽*(剩余空间) -->

    <Grid.ColumnDefinitions>

        <ColumnDefinition Width="100"/>

        <ColumnDefinition Width="*"/>

    </Grid.ColumnDefinitions>


    <!-- 子元素指定位置 -->

    <TextBlock Grid.Row="0" Grid.Column="0" Text="标题" FontSize="18"/>

    <Button Grid.Row="0" Grid.Column="1" Content="操作" HorizontalAlignment="Right"/>

    <TextBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" HintText="跨两列的输入框"/>

    <TextBlock Grid.Row="2" Grid.Column="0" Text="说明" VerticalAlignment="Top"/>

    <TextBlock Grid.Row="2" Grid.Column="1" Text="网格布局适合复杂界面分区" Margin="5"/>

</Grid>

3. WrapPanel(自动换行布局)

作用:按水平或垂直方向排列子元素,当空间不足时自动换行(或换列)。特点:适合展示数量不固定的元素(如标签、图片列表)。

XAML 示例:

xml

<WrapPanel Margin="10" Orientation="Horizontal" Spacing="5" ItemWidth="80" ItemHeight="30">

    <Button Content="标签1"/>

    <Button Content="标签2"/>

    <Button Content="标签3"/>

    <Button Content="标签4"/>

    <Button Content="标签5"/>

    <Button Content="标签6"/>

    <Button Content="标签7"/>

</WrapPanel>

Orientation:排列方向(Horizontal/Vertical)

Spacing:元素间距

ItemWidth/ItemHeight:强制子元素统一尺寸

4. DockPanel(停靠布局)

作用:按上、下、左、右方向停靠子元素,剩余空间由最后一个元素填充。特点:适合工具栏、状态栏等需要固定在边缘的元素。

XAML 示例:

xml

<DockPanel Margin="10" LastChildFill="True">

    <!-- 顶部停靠(工具栏) -->

    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Height="40" Background="LightGray">

        <Button Content="文件" Margin="5"/>

        <Button Content="编辑" Margin="5"/>

    </StackPanel>

    

    <!-- 左侧停靠(导航栏) -->

    <StackPanel DockPanel.Dock="Left" Width="100" Background="LightBlue">

        <Button Content="首页" Margin="5"/>

        <Button Content="设置" Margin="5"/>

    </StackPanel>

    

    <!-- 底部停靠(状态栏) -->

    <TextBlock DockPanel.Dock="Bottom" Text="状态栏" Height="25" Background="LightGray" HorizontalAlignment="Stretch" TextAlignment="Center"/>

    

    <!-- 最后一个元素填充剩余空间(内容区) -->

    <TextBlock Text="主内容区域" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="16"/>

</DockPanel>

DockPanel.Dock:子元素的停靠方向(Top/Bottom/Left/Right)

LastChildFill:是否让最后一个子元素填充剩余空间(默认 True)

5. ScrollViewer(滚动布局)

作用:当内容超出显示区域时,提供滚动条。特点:适合展示长文本、大图片或内容动态增长的场景。

XAML 示例:

xml

<ScrollViewer Margin="10" Height="200" Width="300" VerticalScrollBarVisibility="Auto">

    <StackPanel Spacing="10">

        <TextBlock Text="长文本内容..." FontSize="14" TextWrapping="Wrap"/>

        <Button Content="按钮1"/>

        <Button Content="按钮2"/>

        <Button Content="按钮3"/>

        <Button Content="按钮4"/>

        <Button Content="按钮5"/>

        <Button Content="按钮6"/>

    </StackPanel>

</ScrollViewer>

VerticalScrollBarVisibility/HorizontalScrollBarVisibility:滚动条显示策略(Auto/Visible/Hidden/Disabled)

6. Border(边框布局)

作用:为子元素添加边框、背景或圆角,本身不控制布局,但可嵌套其他布局。特点:常用于美化元素外观(如卡片、分组区域)。

XAML 示例:

xml

<Border Margin="10" 

        BorderThickness="2" 

        BorderBrush="Blue" 

        Background="AliceBlue" 

        CornerRadius="5" 

        Padding="10">

    <StackPanel>

        <TextBlock Text="带边框的区域" FontSize="16"/>

        <TextBlock Text="这是Border组件的示例" Margin="5 0"/>

    </StackPanel>

</Border>

7. Canvas(画布布局)

作用:通过坐标(Left/Top)精确定位子元素,适合自定义绘图或绝对定位场景。特点:子元素位置不受其他元素影响,需手动控制布局(不推荐复杂 UI)。

XAML 示例:

xml

<Canvas Margin="10" Height="300" Width="300" Background="LightGray">

    <Button Canvas.Left="50" Canvas.Top="30" Content="位置(50,30)"/>

    <Button Canvas.Left="150" Canvas.Top="100" Content="位置(150,100)"/>

    <TextBlock Canvas.Left="80" Canvas.Top="200" Text="画布布局示例"/>

</Canvas>

布局组件的选择建议:

简单线性排列:优先用 StackPanel

复杂结构化布局:用 Grid(最常用)

动态数量元素(需换行):用 WrapPanel

边缘固定元素(工具栏 / 状态栏):用 DockPanel

内容过长:用 ScrollViewer 包裹

美化边框:用 Border 嵌套其他布局

通过组合这些布局组件,可以构建出灵活且美观的 Avalonia 界面。




















Top