一、架构与设计模式
MVVM深度解耦
数据驱动界面:XAML通过声明式绑定(如{Binding Path})自动同步业务逻辑与UI状态,无需手动更新控件
例子:
MainWindow.xaml
<Window x:Class="DataBindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="员工信息" Height="300" Width="400">
<StackPanel Margin="15">
<TextBlock Text="员工姓名:"/>
<TextBox Text="{Binding EmployeeName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="当前薪资:" Margin="0,10,0,0"/>
<Slider Value="{Binding Salary, Mode=TwoWay}"
Minimum="3000" Maximum="30000" TickFrequency="1000"/>
<TextBlock Text="{Binding Salary, StringFormat='C2'}"/>
<CheckBox Content="是否经理" IsChecked="{Binding IsManager}"/>
<Button Content="提交" Command="{Binding SubmitCommand}" Margin="0,20"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Windows;
namespace DataBindingDemo {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = new EmployeeViewModel(); // 关键:设置数据上下文
}
}
public class EmployeeViewModel : INotifyPropertyChanged {
private string _employeeName = "张三";
private double _salary = 8000;
private bool _isManager;
public string EmployeeName {
get => _employeeName;
set { _employeeName = value; OnPropertyChanged(); }
}
public double Salary {
get => _salary;
set { _salary = value; OnPropertyChanged(); }
}
public bool IsManager {
get => _isManager;
set { _isManager = value; OnPropertyChanged(); }
}
public RelayCommand SubmitCommand => new RelayCommand(_ => {
MessageBox.Show($"已提交: {EmployeeName}, 薪资: {Salary:C2}, 经理: {IsManager}");
});
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class RelayCommand : System.Windows.Input.ICommand {
private readonly Action<object> _execute;
public RelayCommand(Action<object> execute) => _execute = execute;
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _execute(parameter);
public event EventHandler CanExecuteChanged;
}
}
命令模式集成:ICommand接口实现事件与逻辑分离(如异步操作封装至RelayCommand)
例子:
RelayCommandExample.xaml
<Window x:Class="CommandDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="命令模式" Height="200" Width="300">
<StackPanel VerticalAlignment="Center">
<Button Content="异步加载数据"
Command="{Binding LoadDataCommand}"
Padding="10,5"/>
<TextBlock Text="{Binding Status}"
Margin="0,10" HorizontalAlignment="Center"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
System.Windows;
using System.Threading.Tasks;
namespace CommandDemo {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = new MainViewModel();
}
}
public class MainViewModel {
public ICommand LoadDataCommand => new RelayCommand(async _ => {
Status = "加载中...";
await Task.Delay(2000); // 模拟耗时操作
Status = $"数据加载完成 {DateTime.Now:T}";
});
private string _status = "准备就绪";
public string Status {
get => _status;
set { _status = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class RelayCommand : ICommand {
private readonly Action<object> _execute;
public RelayCommand(Action<object> execute) => _execute = execute;
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _execute(parameter);
public event EventHandler CanExecuteChanged;
}
}
依赖注入支持:结合DI容器(如Microsoft.Extensions.DependencyInjection)管理ViewModel生命周期
例子:
App.xaml.cs
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
public partial class App : Application
{
public static IServiceProvider ServiceProvider { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
var services = new ServiceCollection();
services.AddTransient<MainViewModel>(); // 每次请求新建实例
services.AddSingleton<IDataService, MockDataService>(); // 单例服务
ServiceProvider = services.BuildServiceProvider();
var mainWindow = new MainWindow();
mainWindow.Show();
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = App.ServiceProvider.GetRequiredService<MainViewModel>();
}
}
MainViewModel.cs
public class MainViewModel
{
private readonly IDataService _dataService;
public MainViewModel(IDataService dataService)
{
_dataService = dataService; // 通过构造函数注入
LoadData();
}
private async void LoadData()
{
Data = await _dataService.GetDataAsync();
}
}
动态UI架构
控件模板化:通过重写ControlTemplate实现Material Design等复杂视觉效果(如浮动阴影、动态色彩)
例子:
MaterialButton.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button" x:Key="MaterialButton">
<Setter Property="Background" Value="#6200EE"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<!-- 阴影层 -->
<Border x:Name="Shadow"
CornerRadius="4"
Background="Transparent"
Margin="0,0,0,6">
<Border.Effect>
<DropShadowEffect BlurRadius="12"
ShadowDepth="3"
Color="#40000000"/>
</Border.Effect>
</Border>
<!-- 按钮主体 -->
<Border x:Name="Border"
CornerRadius="4"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<!-- 鼠标悬停效果 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="#3700B3"/>
<Setter TargetName="Shadow" Property="Margin" Value="0,0,0,8"/>
<Setter TargetName="Shadow" Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="16"
ShadowDepth="5"
Color="#60000000"/>
</Setter.Value>
</Setter>
</Trigger>
<!-- 按下效果 -->
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Background" Value="#03DAC6"/>
<Setter TargetName="Shadow" Property="Margin" Value="0,2,0,4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MainWindow.xaml
<Window x:Class="MaterialDesignDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Material Design" Height="200" Width="300">
<Window.Resources>
<ResourceDictionary Source="MaterialButton.xaml"/>
</Window.Resources>
<Grid>
<Button Style="{StaticResource MaterialButton}"
Content="悬浮按钮"
Width="120" Height="40"/>
</Grid>
</Window>
数据模板动态生成:DataTemplateSelector根据数据类型实时切换控件样式
例子:
MainWindow.xaml
<Window x:Class="TemplateSelectorDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TemplateSelectorDemo"
Title="模板选择器" Height="300" Width="400">
<Window.Resources>
<DataTemplate x:Key="TextTemplate">
<Border Background="LightBlue" Padding="10">
<TextBlock Text="{Binding Content}" FontSize="16"/>
</Border>
</DataTemplate>
<DataTemplate x:Key="ImageTemplate">
<Border Background="LightGreen" Padding="5">
<Image Source="{Binding Path}" Width="100" Height="100"/>
</Border>
</DataTemplate>
<local:CustomTemplateSelector x:Key="MySelector"
TextTemplate="{StaticResource TextTemplate}"
ImageTemplate="{StaticResource ImageTemplate}"/>
</Window.Resources>
<ListBox ItemsSource="{Binding Items}"
ItemTemplateSelector="{StaticResource MySelector}"/>
</Window>
CustomTemplateSelector.cs
using System.Windows;
using System.Windows.Controls;
namespace TemplateSelectorDemo {
public class CustomTemplateSelector : DataTemplateSelector {
public DataTemplate TextTemplate { get; set; }
public DataTemplate ImageTemplate { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container) {
return item is TextItem ? TextTemplate :
item is ImageItem ? ImageTemplate : null;
}
}
}
ViewModels.cs
TemplateSelectorDemo {
public abstract class BaseItem {}
public class TextItem : BaseItem {
public string Content { get; set; } = "文本内容";
}
public class ImageItem : BaseItem {
public string Path { get; set; } = "image.png";
}
public class MainViewModel {
public ObservableCollection<BaseItem> Items { get; } = new() {
new TextItem(),
new ImageItem(),
new TextItem()
};
}
}
二、高级UI表现力
声明式动画引擎
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyRect"
Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:2"/>
</Storyboard>
XAML原生支持时间轴动画与关键帧控制
视觉层级管理
视觉树操作:VisualTreeHelper动态遍历/修改UI元素层级结构
例子:
VisualTreeHelperDemo.xaml
<Window x:Class="VisualTreeDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="视觉树操作" Height="350" Width="500">
<StackPanel x:Name="MainPanel">
<Button Content="查找子元素" Click="FindChildren_Click"/>
<Button Content="修改样式" Click="ModifyStyle_Click"/>
<ListBox x:Name="TreeViewer" Height="200"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace VisualTreeDemo {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
MainPanel.Children.Add(new TextBlock { Text = "原始元素1" });
MainPanel.Children.Add(new TextBlock { Text = "原始元素2" });
}
private void FindChildren_Click(object sender, RoutedEventArgs e) {
TreeViewer.Items.Clear();
TraverseVisualTree(MainPanel, 0);
}
private void TraverseVisualTree(DependencyObject parent, int depth) {
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) {
var child = VisualTreeHelper.GetChild(parent, i);
TreeViewer.Items.Add(new string(' ', depth*4) + child.GetType().Name);
if (VisualTreeHelper.GetChildrenCount(child) > 0) {
TraverseVisualTree(child, depth + 1);
}
}
}
private void ModifyStyle_Click(object sender, RoutedEventArgs e) {
ApplyStyleToTextBlocks(MainPanel);
}
private void ApplyStyleToTextBlocks(DependencyObject parent) {
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) {
var child = VisualTreeHelper.GetChild(parent, i);
if (child is TextBlock tb) {
tb.Foreground = Brushes.Red;
tb.FontWeight = FontWeights.Bold;
}
ApplyStyleToTextBlocks(child);
}
}
}
}
混合渲染:集成Win2D或SkiaSharp实现GPU加速绘制(如复杂几何图形)
例子:
MainWindow.xaml
<Window x:Class="SkiaSharpDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF"
Title="SkiaSharp GPU绘制" Height="400" Width="600">
<Grid>
<skia:SKElement x:Name="SkiaCanvas"
PaintSurface="OnPaintSurface"/>
</Grid>
</Window>
MainWindow.xaml.cs
using SkiaSharp;
using System.Windows;
namespace SkiaSharpDemo {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void OnPaintSurface(object sender,
SKPaintSurfaceEventArgs e) {
var surface = e.Surface;
var canvas = surface.Canvas;
// 清空画布
canvas.Clear(SKColors.White);
// 创建渐变画笔
using var paint = new SKPaint {
Shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
new SKPoint(e.Info.Width, e.Info.Height),
new[] { SKColors.Blue, SKColors.Red },
new[] { 0f, 1f },
SKShaderTileMode.Clamp)
};
// 绘制复杂路径
using var path = new SKPath();
path.MoveTo(100, 100);
path.CubicTo(300, 50, 200, 200, 400, 150);
path.LineTo(400, 300);
path.ArcTo(200, 200, 0, SKPathArcSize.Large,
SKPathDirection.CounterClockwise, 100, 300);
path.Close();
canvas.DrawPath(path, paint);
}
}
}
App.xaml
<Application x:Class="SkiaSharpDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
</Application>
主题与样式系统
全局资源字典:ResourceDictionary统一管理颜色、字体等设计资产,支持运行时动态切换
例子:
App.xaml
<Application x:Class="ResourceDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/LightTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Themes/LightTheme.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="PrimaryColor">#FF42A5F5</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<FontFamily x:Key="MainFont">Segoe UI</FontFamily>
<Thickness x:Key="StandardPadding">10</Thickness>
</ResourceDictionary>
Themes/DarkTheme.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="PrimaryColor">#FF1565C0</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<FontFamily x:Key="MainFont">Consolas</FontFamily>
<Thickness x:Key="StandardPadding">10</Thickness>
</ResourceDictionary>
MainWindow.xaml
<Window x:Class="ResourceDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="资源字典演示" Height="300" Width="400">
<StackPanel>
<Button Content="切换深色主题" Click="DarkTheme_Click"
Background="{StaticResource PrimaryBrush}"
FontFamily="{StaticResource MainFont}"
Padding="{StaticResource StandardPadding}"/>
<Button Content="切换浅色主题" Click="LightTheme_Click"
Background="{StaticResource PrimaryBrush}"
FontFamily="{StaticResource MainFont}"
Padding="{StaticResource StandardPadding}"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Media;
namespace ResourceDemo {
public partial class MainWindow : Window {
public MainWindow() => InitializeComponent();
private void ChangeTheme(string themePath) {
var dict = new ResourceDictionary { Source = new Uri(themePath, UriKind.Relative) };
Application.Current.Resources.MergedDictionaries[0] = dict;
}
private void DarkTheme_Click(object sender, RoutedEventArgs e)
=> ChangeTheme("Themes/DarkTheme.xaml");
private void LightTheme_Click(object sender, RoutedEventArgs e)
=> ChangeTheme("Themes/LightTheme.xaml");
}
}
状态样式触发器:VisualStateManager根据控件状态(如Pressed、Disabled)自动切换样式
例子:
MainWindow.xaml
<Window x:Class="VisualStateDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="状态样式触发器" Height="300" Width="400">
<Grid>
<Button Content="交互按钮" Width="200" Height="60">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="LightBlue" Duration="0:0:0.2"/>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="LightGreen" Duration="0:0:0.2"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Orange" Duration="0:0:0.1"/>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX"
To="0.95" Duration="0:0:0.1"/>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY"
To="0.95" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="LightGray" Duration="0:0:0.2"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Button.RenderTransform>
<ScaleTransform/>
</Button.RenderTransform>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace VisualStateDemo {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
}
}
三、跨平台能力革新
单一代码库多端部署
.NET MAUI机制:XAML布局自适应不同平台(iOS/Android/Windows),共享核心业务逻辑
例子:
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiDemo.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image Source="dotnet_bot.png"
SemanticProperties.Description="MAUI Logo"
HeightRequest="200"
HorizontalOptions="Center"/>
<Label Text="Hello, World!"
Style="{StaticResource Headline}"
SemanticProperties.HeadingLevel="Level1"
HorizontalOptions="Center"/>
<Label Text="Welcome to .NET MAUI!"
Style="{StaticResource SubHeadline}"
SemanticProperties.HeadingLevel="Level2"
HorizontalOptions="Center"/>
<Button Text="Click Me"
Command="{Binding CounterCommand}"
HorizontalOptions="Center"
Style="{OnPlatform Android=PrimaryButton,
iOS=SecondaryButton,
WinUI=DefaultButton}"/>
<Label Text="{Binding CounterText}"
HorizontalOptions="Center"/>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
MainPage.xaml.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MauiDemo;
public partial class MainPage : ContentPage, INotifyPropertyChanged
{
private int _count = 0;
private string _counterText = "Click count: 0";
public string CounterText {
get => _counterText;
set {
_counterText = value;
OnPropertyChanged();
}
}
public Command CounterCommand { get; }
public MainPage()
{
InitializeComponent();
CounterCommand = new Command(OnCounterClicked);
BindingContext = this;
}
private void OnCounterClicked()
{
_count++;
CounterText = $"Click count: {_count}";
SemanticScreenReader.Announce(CounterText);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Platforms/Android/Resources/values/styles.xml
<resources>
<style name="PrimaryButton" parent="Widget.AppCompat.Button.Colored">
<item name="android:backgroundTint">#512BD4</item>
<item name="android:textColor">#FFFFFF</item>
</style>
</resources>
Platforms/iOS/Resources/SecondaryB
using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
namespace MauiDemo;
public static class SecondaryButtonStyle
{
public static Button ApplySecondaryStyle(this Button button)
{
button.BackgroundColor = Colors.White;
button.TextColor = Colors.Black;
button.On<iOS>().SetUseSafeArea(true);
return button;
}
}
响应式布局:FlexLayout与Grid结合自适应规则应对屏幕尺寸变化
例子:
ResponsivePage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveDemo.ResponsivePage">
<Grid RowDefinitions="Auto,*,Auto"
ColumnDefinitions="*,*">
<!-- 顶部标题栏 - 横屏时右移 -->
<Label Text="响应式布局演示"
Grid.ColumnSpan="{OnPlatform 2,
Default=1,
iOS=1,
Android=1,
WinUI=2}"
HorizontalOptions="Center"
Style="{StaticResource TitleStyle}"/>
<!-- 主内容区 - FlexLayout根据方向变化 -->
<FlexLayout Grid.Row="1"
Grid.ColumnSpan="2"
Direction="{OnPlatform Default=Row,
iOS=Row,
Android=Column,
WinUI=Row}"
Wrap="Wrap"
JustifyContent="SpaceEvenly"
AlignItems="Center">
<BoxView Color="LightBlue"
WidthRequest="{OnPlatform 150,
Default=100,
iOS=120}"
HeightRequest="100"/>
<BoxView Color="LightGreen"
WidthRequest="{OnPlatform 150,
Default=100,
iOS=120}"
HeightRequest="100"/>
<BoxView Color="LightPink"
WidthRequest="{OnPlatform 150,
Default=100,
iOS=120}"
HeightRequest="100"/>
</FlexLayout>
<!-- 底部按钮区 - 根据屏幕宽度调整布局 -->
<StackLayout Grid.Row="2"
Grid.ColumnSpan="2"
Orientation="{OnPlatform Default=Horizontal,
iOS=Horizontal,
Android=Vertical}">
<Button Text="确定"
WidthRequest="{OnPlatform 120,
Default=100,
iOS=110}"/>
<Button Text="取消"
WidthRequest="{OnPlatform 120,
Default=100,
iOS=110}"/>
</StackLayout>
</Grid>
</ContentPage>
ResponsivePage.xaml.cs
using Microsoft.Maui.Controls;
namespace ResponsiveDemo;
public partial class ResponsivePage : ContentPage
{
public ResponsivePage()
{
InitializeComponent();
// 监听尺寸变化
this.SizeChanged += (s,e) => {
bool isWide = this.Width > 600;
VisualStateManager.GoToState(
this,
isWide ? "WideLayout" : "NarrowLayout"
);
};
}
}
App.xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveDemo.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="TitleStyle" TargetType="Label">
<Setter Property="FontSize" Value="{OnPlatform 24, Default=20, iOS=22}"/>
<Setter Property="FontAttributes" Value="Bold"/>
</Style>
<VisualStateGroup x:Key="LayoutStates">
<VisualState x:Key="WideLayout">
<VisualState.Setters>
<Setter TargetName="mainGrid" Property="Grid.ColumnDefinitions" Value="*,*,*"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Key="NarrowLayout">
<VisualState.Setters>
<Setter TargetName="mainGrid" Property="Grid.ColumnDefinitions" Value="*"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</ResourceDictionary>
</Application.Resources>
</Application>
原生性能优化
渲染器定制:通过Handler机制重写平台原生控件行为(如Android下定制按钮阴影)
例子:
CustomButton.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomRendererDemo.CustomButton">
<Button x:Name="NativeButton"
Text="Custom Shadow Button"
BackgroundColor="#512BD4"
TextColor="White"/>
</ContentView>
CustomButton.xaml.cs
using Microsoft.Maui.Controls;
namespace CustomRendererDemo;
public partial class CustomButton : ContentView
{
public CustomButton()
{
InitializeComponent();
}
}
CustomButtonHandler.cs
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
namespace CustomRendererDemo;
public partial class CustomButtonHandler : ViewHandler<CustomButton, Android.Widget.Button>
{
protected override Android.Widget.Button CreatePlatformView()
{
var button = new Android.Widget.Button(Context);
return button;
}
protected override void ConnectHandler(Android.Widget.Button platformView)
{
base.ConnectHandler(platformView);
UpdateShadow();
}
void UpdateShadow()
{
if (PlatformView == null) return;
PlatformView.SetShadowLayer(
radius: 10f,
dx: 5f,
dy: 5f,
color: Android.Graphics.Color.Argb(100, 0, 0, 0));
}
}
MauiProgram.cs
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
namespace CustomRendererDemo;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureMauiHandlers(handlers => {
handlers.AddHandler<CustomButton, CustomButtonHandler>();
});
return builder.Build();
}
}
渲染管线接入:Avalonia框架支持Skia自定义绘制管线实现高性能渲染
例子:
CustomSkiaControl.cs
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Skia;
using SkiaSharp;
public class CustomSkiaControl : Control
{
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
InvalidateVisual();
}
public override void Render(DrawingContext context)
{
var skiaContext = context.GetFeature<ISkiaSharpApiLeaseFeature>();
using (var lease = skiaContext.Lease())
{
var canvas = lease.SkCanvas;
canvas.Clear(SKColors.White);
// 绘制渐变背景
using (var paint = new SKPaint())
{
var rect = new SKRect(0, 0, (float)Bounds.Width, (float)Bounds.Height);
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
new SKPoint((float)Bounds.Width, (float)Bounds.Height),
new[] { SKColors.Blue, SKColors.Green },
new[] { 0f, 1f },
SKShaderTileMode.Clamp);
canvas.DrawRect(rect, paint);
}
// 绘制文本
using (var paint = new SKPaint())
{
paint.Color = SKColors.Red;
paint.TextSize = 24;
paint.IsAntialias = true;
canvas.DrawText("SkiaSharp Rendering", 20, 40, paint);
}
}
}
}
MainWindow.xaml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YourNamespace"
Title="SkiaSharp Demo">
<local:CustomSkiaControl Width="400" Height="300"/>
</Window>
四、性能与可维护性
渲染优化技术
UI虚拟化:VirtualizingStackPanel应对万级数据列表,仅渲染可视区域元素
例子:
MainWindow.xaml
<Window x:Class="VirtualizationDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UI虚拟化演示" Height="450" Width="800">
<ListView ItemsSource="{Binding Items}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.CanContentScroll="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
Margin="5" FontSize="16"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Window>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
namespace VirtualizationDemo
{
public partial class MainWindow : Window
{
public ObservableCollection<DataItem> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<DataItem>();
// 生成10万条测试数据
for (int i = 0; i < 100000; i++)
{
Items.Add(new DataItem { Name = $"项目 {i + 1}" });
}
DataContext = this;
}
}
public class DataItem
{
public string Name { get; set; }
}
}
异步加载策略:PriorityBinding优先显示关键数据,后台加载次要内容
例子:
MainWindow.xaml
<Window x:Class="PriorityBindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="异步加载策略" Height="350" Width="500">
<StackPanel>
<TextBlock Margin="10" FontWeight="Bold" Text="商品详情"/>
<TextBlock Margin="10,5">
<TextBlock.Text>
<PriorityBinding>
<Binding Path="BasicInfo" IsAsync="False"/>
<Binding Path="LoadingText"/>
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Margin="10" FontWeight="Bold" Text="完整描述"/>
<TextBlock Margin="10,5">
<TextBlock.Text>
<PriorityBinding>
<Binding Path="FullDescription" IsAsync="True"/>
<Binding Path="LoadingText"/>
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
namespace PriorityBindingDemo
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _loadingText = "加载中...";
public string LoadingText {
get => _loadingText;
set => SetField(ref _loadingText, value);
}
private string _basicInfo;
public string BasicInfo {
get => _basicInfo ?? LoadBasicInfo();
set => SetField(ref _basicInfo, value);
}
private string _fullDescription;
public string FullDescription {
get => _fullDescription ?? Task.Run(LoadFullDescription).Result;
set => SetField(ref _fullDescription, value);
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private string LoadBasicInfo() {
// 模拟快速加载的关键数据
Task.Delay(300).Wait();
return "商品名称:智能手机(基础信息已加载)";
}
private async Task<string> LoadFullDescription() {
// 模拟耗时的详细数据加载
await Task.Delay(3000);
return "产品详情:\n- 6.5英寸AMOLED屏幕\n- 骁龙888处理器\n- 5000mAh电池\n(完整描述已加载)";
}
// INotifyPropertyChanged实现
public event PropertyChangedEventHandler PropertyChanged;
protected void SetField<T>(ref T field, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) {
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
开发效率工具链
实时热重载:修改XAML即时预览效果(注:需规避XamlC编译冲突)
Live Visual Tree调试:运行时动态检查/修改XAML属性