官网
底层架构区别
Avalonia:
WPF:
对比维度 | WPF | Avalonia |
---|---|---|
平台支持与依赖 | Windows系统,深度集成DirectX渲染和Win32 API,仅支持Windows平台 | 基于.NET Standard 2.0,通过抽象层兼容多平台(Windows/Linux/macOS/iOS/Android),使用Skia渲染引擎实现跨平台一致性 |
渲染引擎 | DirectX (Windows独占) | Skia |
样式系统 | 基于ResourceDictionary 和Style 对象,通过BasedOn 继承样式 | 样式选择器+伪类 ControlTheme 定义基础主题,Style 覆盖具体场景 |
数据绑定 | 强依赖ICommand | 支持直接绑定方法(无需ICommand 包装),保留ICommand 用于高级场景(如CanExecute ) |
字体 | 系统字体 | 嵌入字体(如思源黑体)以避免跨平台显示异常 |
实现细节区别
1.ControlTheme和Style的区别
Avalonia :样式通常定义在 Window.Styles
或全局资源字典中。Avalonia 使用 Selector
属性来指定样式将应用于哪些控件。 控件主题是在样式的基础上构建的,用于为控件创建可切换的主题。ControlTheme类似于WPF的Style
,通常用于为无外观的控件创建主题。
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme/>
</Application.Styles>
<Application.Resources>
<!-- 自定义按钮样式 -->
<ControlTheme x:Key="RoundedButtonTheme" TargetType="Button">
<Setter Property="Background" Value="#4CAF50"/> <!-- 绿色背景 -->
<Setter Property="Foreground" Value="White"/>
<Setter Property="Padding" Value="12,6"/>
<Setter Property="BorderBrush" Value="#388E3C"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Template">
<ControlTemplate>
...
</ControlTemplate>
</Setter>
</ControlTheme>
</Application.Resources>
</Application>
在选择步骤中,样式系统从控件开始沿着逻辑树向上搜索。这意味着在应用程序的最高级别(例如 App.axaml
文件)定义的样式可以在应用程序的任何地方使用,但仍然可以在控件更近的地方(例如在窗口或用户控件中)进行覆盖。
WPF : 通常在Window.Resources
。在 WPF 中是通过 TargetType
实现的。
<Window.Resources>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="Black"
BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Button Style="{StaticResource MyButtonStyle}" Content="Click Me"/>
2.依赖属性
WPF:DependencyProperty
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(string), typeof(AduCheckComboBox), new PropertyMetadata(string.Empty));
/// <summary>
/// 内容
/// </summary>
public string Content
{
get { return (string)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
Avalonia:
- 若属性需要被样式系统或控件模板影响和扩展,那么推荐使用
StyledProperty
。 AvaloniaProperty
则更适合那些不需要与样式直接交互,或者不需要继承性质的一般依赖属性。
//方法一:
public class MyControl : Avalonia.Controls.Control
{
public static readonly StyledProperty<int> MyPropertyProperty =
AvaloniaProperty.Register<MyControl, int>(nameof(MyProperty), defaultValue: 0, onChanged: OnMyPropertyChanged);
public int MyProperty
{
get { return GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
private static void OnMyPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
MyControl control = e.Sender as MyControl;
int newValue = (int)e.NewValue;
// Add your class handler logic here
// For example:
control.Classes.Set("my-class", newValue > 0);
}
}
//方法二
public MyControl()
{
MyPropertyProperty.Changed.AddClassHandler<MyControl>(MyPropertyPropertyChangedCallBack);
}
3.非模板控件的基类
WPF | Avalonia | |
非模板控件的基类 | UIElement | Control |
非模板控件的基类(自定义绘制控件) | FrameworkElement | Control |
控件 | Control | TemplatedControl |
依赖属性 | DependencyProperty | StyledProperty |
4.Grid写法
WPF:
<Grid>
<!-- 定义列 -->
<Grid.ColumnDefinitions>
<!-- 第一列自动调整大小以适应内容 -->
<ColumnDefinition Width="Auto" />
<!-- 第二列使用剩余空间 -->
<ColumnDefinition Width="*" />
<!-- 第三列固定宽度为32 -->
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<!-- 定义行 -->
<Grid.RowDefinitions>
<!-- 第一行使用剩余空间 -->
<RowDefinition Height="*" />
<!-- 第二行自动调整大小以适应内容 -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid>
Avalonia:
<Grid ColumnDefinitions="Auto,*,32" RowDefinitions="*,Auto">
5.隧道(也称为预览)事件
隧道(也称为预览)事件首先在最顶层的元素上触发,然后逐级向下传播到事件的原始源。这与冒泡事件相反,冒泡事件从事件的原始源开始,然后向上传播到包含它的元素。
WPF:通过单独的Preview
CLR事件处理程序公开的
示例:将使用PreviewKeyDown
事件,这是一个隧道事件,用于在键盘按键被按下时触发
若你在某个事件处理程序中设置了e.Handled = true;
(如注释所示),则事件将不会继续传播到更下层的元素。这可以用于在事件到达其原始源之前拦截并处理事件。
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("TextBox PreviewKeyDown: " + e.Key.ToString());
// 可以在这里处理键盘输入,或者通过设置e.Handled = true来阻止事件进一步传播
// e.Handled = true;
}
Avalonia::要订阅一个事件隧道传播事件,您需要使用AddHandler方法,并传递RoutingStrategies.Tunnel参数:
target.AddHandler(InputElement.KeyDownEvent, OnPreviewKeyDown, RoutingStrategies.Tunnel);
void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
// 处理程序代码
}