IRegionManager
和IContainerExtension
IRegionManager
是 Prism 框架中用于管理 UI 区域(Regions)的核心接口,它实现了模块化应用中视图(Views)的动态加载、导航和生命周期管理。
IContainerExtension
是依赖注入(DI)容器的抽象接口,而Resolve<T>()
方法用于从容器中解析指定类型的实例
public class u1: UserControl
{IRegionManager _regionManager;IContainerExtension _container;public u1(IContainerExtension container, IRegionManager regionManager){_regionManager = regionManager;_container = container;//从容器中解析ListView类型的实例。如果ListView已注册为单例,则返回单例实例;否则返回新实例ListView ListView11 =_container.Resolve<ListView>();//获取中心显示区域IRegion region= _regionManager.Regions["ContentRegion"];//为中心显示区域添加视图(ListView11),并为视图分配一个名称“ListView1”region.Add(ListView11 , "ListView1");//将指定视图(ListView11)设置为区域(region)中的活动视图region.Activate(ListView11);}}
u1的xaml中有:
<ContentControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion" />
......
<dx:DXTabItem Header="名称">
<local:ListView/>//将ListView
视图嵌入到DXTabItem
中,作为选项卡页的内容。</dx:DXTabItem>
其中ListView是自定义的另一个用户控件。
在 Prism 框架中,结合第三方控件库(如 DevExpress 的 DXTabItem
)时,可以通过 XAML 直接定义视图(如 ListView
)并将其嵌入到选项卡控件中。
region.Activate(view)
方法用于将指定视图(view
)设置为区域(IRegion
)中的活动视图。
单选框的动态绑定
<StackPanel Margin="20">
<TextBlock Text="组合单选框" FontWeight="Bold"/>
<DockPanel x:Name="GroupRadioButton">
<StackPanel DockPanel.Dock="Left">
<ItemsControl ItemsSource="{Binding RadioButtons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding SecurityId}"
IsChecked="{Binding IsSelected, Mode=TwoWay}"
GroupName="RadioButtons"
Command="{Binding DataContext.RadioCheckCommand,
RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding}">
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel><StackPanel DockPanel.Dock="Right"
Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="{Binding SelectedRadioButton.SecurityId, StringFormat='结果:{0}'}" />
</StackPanel>
</DockPanel>
</StackPanel>
using Prism.Commands;
using Prism.Mvvm;
using StrategyClient.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;namespace StrategyClient.ViewModels
{class SellStrategyViewModel : BindableBase{/// <summary>/// 当前选择的单选框/// </summary>private ConfigAccount _selectedRadioButton;/// <summary>/// 当前选择的单选框/// </summary>public ConfigAccount SelectedRadioButton{get => _selectedRadioButton;set => SetProperty(ref _selectedRadioButton, value);}/// <summary>/// 需要显示的一组单选框的信息链表/// </summary>public ObservableCollection<ConfigAccount> RadioButtons { get; } = new ObservableCollection<ConfigAccount>();/// <summary>/// 绑定命令触发(单选框选择改变时)/// </summary>public DelegateCommand<ConfigAccount> RadioCheckCommand { get; }public SellStrategyViewModel(){// 初始化单选框选项RadioButtons.Add(new ConfigAccount { SecurityId = "选项1", IsSelected = false });RadioButtons.Add(new ConfigAccount { SecurityId = "选项2", IsSelected = false });RadioButtons.Add(new ConfigAccount { SecurityId = "选项3", IsSelected = false });// 设置默认选中项if (RadioButtons.Count > 0){RadioButtons[0].IsSelected = true;SelectedRadioButton = RadioButtons[0];}// 注册命令RadioCheckCommand = new DelegateCommand<ConfigAccount>(OnRadioChecked);}private void OnRadioChecked(ConfigAccount item){// 更新选中项foreach (var radioButton in RadioButtons){radioButton.IsSelected = radioButton == item;}SelectedRadioButton = item;}}
}
检查UI绑定路径
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
......
<CheckBox IsChecked="{Binding IsSelectedV, Mode=TwoWay, diagnostics:PresentationTraceSources.TraceLevel=High}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
如果直接绑定到
IsSelectedV
属性不起作用,可以尝试使用CellValue
绑定:<CheckBox IsChecked="{Binding RowData.Row.IsSelectedV, Mode=TwoWay}"/>
在这种情况下,
RowData.Row
通常是指当前行的数据对象。
实现弹窗功能:
//App中注册对话框
containerRegistry.RegisterDialog<View, ViewModel>();
// 显示弹窗
void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback);
// 显示模态弹窗(阻塞式)
void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback);
主视图的ViewModel:
public ObservableCollection<string> Items
{
get;
set;
} = new ObservableCollection<string>();/// <summary>
/// 按钮按下弹窗
/// </summary>
private void Button_Click()
{
//传递参数
var parameters = new DialogParameters
{
{ "DataList", Items }
};//View:视图名,parameters:要传递的参数
_dialogService.ShowDialog("View", parameters, (IDialogResult result) =>
{//弹窗关闭后回调函数
// 从结果中获取数据链表
if (result.Parameters.TryGetValue<ObservableCollection<string>>("DataList", out var dataList))
{
Items = dataList;
}
});}
// 定义弹窗事件
public class ShowDialogEvent : PubSubEvent<DialogParameters> { }// 发布弹窗请求
var parameters = new DialogParameters { { "message", "保存成功!" } };
_eventAggregator.GetEvent<ShowDialogEvent>().Publish(parameters);
// 弹窗服务订阅事件并显示弹窗
_eventAggregator.GetEvent<ShowDialogEvent>()
.Subscribe(ShowDialog, ThreadOption.UIThread);
private void ShowDialog(DialogParameters parameters)
{
_dialogService.ShowDialog("MessageDialog", parameters);
}
View视图的ViewModel
class ViewModel : BindableBase, IDialogAware
{
public ObservableCollection<string> Items
{
get => _items;
set => SetProperty(ref _items, value);
}/// <summary>
/// 对话框事件,传递对话框的结果
/// </summary>
public event Action<IDialogResult> RequestClose;
/// <summary>
/// 关闭对话框时传递参数
/// </summary>
public event Action<IDialogParameters> RequestClosed;// 对话框标题
public string Title => "弹窗标题";/// <summary>
/// 允许关闭对话框
/// </summary>
/// <returns></returns>
public bool CanCloseDialog()
{
return true;
}/// <summary>
/// 关闭对话框时
/// </summary>
public void OnDialogClosed()
{
var resultParameters = new DialogParameters
{
{ "DataList", Items }
};
// 触发请求关闭事件
RequestClosed?.Invoke(resultParameters);
}/// <summary>
/// 打开对话框时
/// </summary>
/// <param name="parameters"></param>
public void OnDialogOpened(IDialogParameters parameters)
{
if (parameters.TryGetValue<ObservableCollection<SellStrategyModel>>("DataList", out var initialName))
{
Items = initialName;
}
}}
单选框:
<DockPanel x:Name="GroupRadioButton">
<ItemsControl ItemsSource="{Binding RadioButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Id}"
IsChecked="{Binding IsSelected, Mode=TwoWay}"
GroupName="RadioButtons"
Command="{Binding DataContext.RadioCheckCommand,
RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding}">
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
viewModel
/// <summary>
/// 当前选择的单选框
/// </summary>
private ConfigAccount _selectedRadioButton;
/// <summary>
/// 当前选择的单选框
/// </summary>
public ConfigAccount SelectedRadioButton
{
get => _selectedRadioButton;
set => SetProperty(ref _selectedRadioButton, value);
}/// <summary>
/// 需要显示的一组单选框的信息链表
/// </summary>
public ObservableCollection<ConfigAccount> RadioButtons { get; } = new ObservableCollection<ConfigAccount>();
/// <summary>
/// 绑定命令触发(单选框选择改变时)
/// </summary>
public DelegateCommand<ConfigAccount> RadioCheckCommand { get; }public ViewModel()
{
AddSecurityStrategy();
// 设置默认选中项
if (RadioButtons.Count > 0)
{
RadioButtons[0].IsSelected = true;
SelectedRadioButton = RadioButtons[0];
}
RadioCheckCommand = new DelegateCommand<ConfigAccount>(OnRadioChecked);
}/// <summary>
/// 单选框按钮选择时触发
/// </summary>
/// <param name="item">选择的单选框对象</param>
private void OnRadioChecked(ConfigAccount item)
{
// 更新选中项
//foreach (var radioButton in RadioButtons)
//{
// radioButton.IsSelected = radioButton == item;
//}
SelectedRadioButton = item;
}/// <summary>
/// 添加需要显示的单选框按钮
/// </summary>
private void AddSecurityStrategy()
{
string[] addStrategys = System.Configuration.ConfigurationManager.AppSettings["SellStrategy"].ToString().Split('|');
foreach (string addStrategy in addStrategys)
{
RadioButtons.Add(new ConfigAccount() { IsSelected = false, SecurityId = addStrategy });
}
}
动态加载不同模块的 UI
方法一:
1.注册区域
确保 "MainContent"
区域已通过 IRegionManager
注册(通常在 XAML 中声明或代码中动态添加):
_regionManager.Regions.Add("MainContent", new Region());
或者
<!-- 或在 XAML 中声明区域 --> <ContentControl prism:RegionManager.RegionName="MainContent" />
2.App.xaml.cs
//MainView:要加载的视图的名称,MainView1:起的名字
containerRegistry.RegisterForNavigation<MainWindowView, MainWindowViewModel>("MainView1");
或
//MainView:要加载的视图的名称,没起名字
registry.RegisterForNavigation<MainWindowView, MainWindowViewModel>();
3.点击按钮时实现ViewModel:
//起名字了这里传入起的名字
_regionManager.RequestNavigate("MainContent", "MainView1");
//没起名字,这里传入类名
_regionManager.RequestNavigate("MainContent", "MainWindowView");
//传递参数:
var parameters = new NavigationParameters();
parameters.Add("orderId", 123);
_regionManager.RequestNavigate("MainRegion", "OrderDetailView", parameters);
方法二:
1.注册区域
确保 "MainContent"
区域已通过 IRegionManager
注册(通常在 XAML 中声明或代码中动态添加):
_regionManager.Regions.Add("MainContent", new Region());
或者
<!-- 或在 XAML 中声明区域 --> <ContentControl prism:RegionManager.RegionName="MainContent" />
2.在模块初始化时注册视图到指定区域:
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{//开始的时候MainContent会显示loading视图
_regionManager.RegisterViewWithRegion("MainContent", typeof(loading));
}
3.点击按钮时实现ViewModel:
_regionManager.RequestNavigate("MainContent", "loading");
获取UI界面实例的view Model,调用对应的函数
方法一:
// 获取目标区域
IRegion contentRegion = _regionManager.Regions["MainContent"];
// 获取当前激活的视图
object activeView = contentRegion.ActiveViews.FirstOrDefault();
// 获取视图的 DataContext(即 ViewModel)
var viewModel = activeView.GetType().GetProperty("DataContext")?.GetValue(activeView) as DownLoadViewModel;
//调用视图方法改变UI内文字
viewModel?.testc();
方法二:
//注册为单例
registry.RegisterSingleton<DownLoadViewModel>();
// 直接解析 ViewModel 实例
var viewMode2 = _container.Resolve<DownLoadViewModel>();
viewMode2?.testc();
注册服务
方法 | 用途 | 生命周期控制 |
---|---|---|
RegisterForNavigation<TView>() | 注册视图到导航系统,自动绑定 ViewModel(通过命名约定) | 默认瞬态(每次导航创建新实例) |
RegisterSingleton<T>() | 注册类型为单例,全局唯一实例 | 单例 |
RegisterForNavigation<TView, TViewModel>() | 显式指定视图和 ViewModel 的绑定关系 | 可自定义(结合其他注册方法) |
Register<TInterface, TImplementation>() | 注册接口到具体实现 | 默认瞬态 |
RegisterInstance<T>(T instance) | 注册已创建的实例为单例 | 单例 |
//举例
registry.RegisterForNavigation<DownLoadView, DownLoadViewModel>("DownLoad1");
registry.RegisterForNavigation<MainWindowView, MainWindowViewModel>();
registry.RegisterSingleton<DownLoadViewModel>();
registry.RegisterForNavigation<DownLoadView>("downLoad");
registry.RegisterForNavigation<loading>();
containerRegistry.Register<IDownLoadService, DownLoadService>();
var downloadService = new DownLoadService(); containerRegistry.RegisterInstance<IDownLoadService>(downloadService);
管理区域和视图:
方法 | 用途 | 特点 |
---|---|---|
RegisterViewWithRegion | 静态注册视图到区域,自动加载 | 适用于固定视图,初始化时自动加载 |
RequestNavigate | 动态导航到视图,支持参数和回调 | 适用于按需加载的视图,支持导航逻辑 |
IRegion.Add / IRegion.Remove | 手动添加或移除视图 | 适用于精细控制视图的显示/隐藏 |
IRegion.Activate / IRegion.Deactivate | 激活或停用视图 | 适用于切换视图的可见性 |
IRegion.NavigationService | 通过导航服务实现复杂导航逻辑 | 适用于需要自定义导航流程的场景 |
//举例
protected override void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
// 将 OrderListView 注册到 MainRegion,视图会在区域初始化时自动加载 regionManager.RegisterViewWithRegion("MainRegion", typeof(OrderListView));
}
_regionManager.RequestNavigate("MainContent", "loading");
var region = _regionManager.Regions["MainRegion"];var view = _container.Resolve<OrderListView>();
region.Add(view); region.Activate(view); // 激活视图
var region = _regionManager.Regions["MainRegion"];var view = region.Views.FirstOrDefault(v => v is OrderListView); if (view != null)
{ region.Remove(view); }
var region = _regionManager.Regions["MainRegion"];var view = region.Views.FirstOrDefault(v => v is OrderListView); if (view != null)
{
region.Activate(view); // 激活视图
// region.Deactivate(view); // 停用视图
}
INavigationAware:
INavigationAware
是 Prism 框架中用于处理导航生命周期事件的核心接口。它允许 ViewModel 在导航过程中响应以下三个关键事件:
- 导航到当前视图时(
OnNavigatedTo
) - 从当前视图导航离开时(
OnNavigatedFrom
) - 导航确认阶段(
IsNavigationTarget
,用于决定是否复用现有 ViewModel 实例)
通过实现 INavigationAware
,可以精细控制 ViewModel 在导航过程中的行为,例如初始化数据、清理资源或决定是否复用实例。
INavigationAware
是 Prism 中管理导航生命周期的核心接口。- 通过实现
IsNavigationTarget
、OnNavigatedTo
和OnNavigatedFrom
,可以精细控制 ViewModel 在导航过程中的行为。 - 适用于需要初始化数据、清理资源或决定是否复用实例的场景。
- 合理使用导航参数(
NavigationParameters
)可以传递上下文数据。
区域上下文
是一种用于在区域(IRegion
)和其宿主控件(如 ContentControl
、ItemsControl
等)之间传递上下文数据的机制。它允许开发者在区域中共享数据,而无需直接依赖视图或视图模型,从而提升代码的解耦性和灵活性。
// 获取区域并设置上下文 var region = _regionManager.Regions["MainRegion"]; region.Context = new AppContext { UserId = "123", Role = "Admin" };//AppContext:自定义Mode
App.config用法
App.config代码:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<appSettings>
<add key="keyName" value="valueName"/>
<add key="keyName2" value="valueName2"/>
<add key="keyName3" value="valueName3"/>
</appSettings>
</configuration>
string settingValue = System.Configuration.ConfigurationManager.AppSettings["keyName"];
TemplateBinding 和Binding 的区别
TemplateBinding
:- 用途:专门用于控件模板(如
ControlTemplate
或DataTemplate
)中,用于将模板中的属性绑定到模板化控件(即应用该模板的控件)的对应属性。 - 上下文:只能在控件模板内部使用,用于模板和模板化控件之间的属性绑定。
- 用途:专门用于控件模板(如
Binding
:- 用途:是一种通用的数据绑定机制,用于在XAML中建立任何两个属性之间的绑定关系,无论是控件与控件之间、控件与数据源之间,还是其他任何对象属性之间的绑定。
- 上下文:可以在XAML的任何地方使用,不局限于控件模板。
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
<TextBox Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
ControlTemplate
ControlTemplate
是 WPF 和 UWP 等 XAML 框架中的核心概念,用于定义控件的视觉结构和交互行为。它允许开发者自定义控件的外观,而不必修改控件的底层逻辑。
一个典型的 ControlTemplate
包含以下关键部分:
- 触发器(Triggers):用于响应控件状态变化(如鼠标悬停、按下等),动态修改控件的外观。
- 内容呈现器(ContentPresenter):用于显示控件的内容(如按钮的文本或图像)。
- 控件部件(Parts):模板中定义的命名元素,控件逻辑可以通过
TemplateBinding
或GetTemplateChild
方法访问这些部件。 - 绑定(Bindings):通过
TemplateBinding
将模板中的属性绑定到模板化控件的属性,实现动态数据驱动。
<Style x:Key="CardButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value><ControlTemplate TargetType="Button">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="10"
Padding="5">//定义了按钮的外观,包括背景色、边框、圆角和内边距
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>//用于显示按钮的内容
</Border>
<ControlTemplate.Triggers>//当鼠标悬停在按钮上时(
IsMouseOver=True
),将Border
的背景色设置为LightBlue
。<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="LightBlue"/>
</Trigger>//当按钮被按下时(
IsPressed=True
),将Border
的背景色设置为DarkBlue
。
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="DarkBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate></Setter.Value>
</Setter></Style>
或者将ControlTemplate定义在在控件中:
<Button Content="按钮控件" Width="300" Height="80" Margin="5" >
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="Transparent" CornerRadius="5" BorderThickness="1" BorderBrush="#C9CCD5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
查看控件的默认模板:
在设计界面中用鼠标单击Button按钮右键-编辑模板-编辑副本。
会自动出现:
触发器
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Content" Value="MouseOver" TargetName="contentPresenter"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Content" Value="将ControlTemplate定义在在控件中" TargetName="contentPresenter"/>
</Trigger>
</ControlTemplate.Triggers>
在Triggers集合中增加了两个Trigger 对象,条件是当鼠标移上去或鼠标移开的时候,更改了Button的Content属性。
ControlTemplate 与 DataTemplate 的区别
ControlTemplate
:- 用于定义控件的外观和交互行为。
- 适用于自定义控件或修改现有控件的外观。
DataTemplate
:- 用于定义数据的可视化方式。
- 适用于将数据绑定到控件(如
ListBoxItem
、ComboBoxItem
)并自定义其显示方式。
ContentPresenter
ContentPresenter
是 WPF 和 UWP 等 XAML 框架中的一个核心控件,用于在控件模板(如 ControlTemplate
或 DataTemplate
)中动态呈现内容。它的主要作用是承载并显示被模板化的控件的内容,同时支持内容与模板的绑定和动态更新。
核心作用
- 内容占位符:在控件模板中作为内容的占位符,确保内容能够正确显示。
- 支持内容绑定:自动绑定到被模板化控件的
Content
或ContentTemplate
属性,实现动态内容展示。 - 继承样式和属性:自动继承模板中定义的样式(如
Foreground
、FontFamily
)和布局属性(如HorizontalAlignment
、VerticalAlignment
)。 - 触发器支持:与样式触发器(
Triggers
)配合,响应内容状态变化(如IsMouseOver
、IsEnabled
)并动态调整外观。
关键属性:
HorizontalAlignment="Center"
和VerticalAlignment="Center"
:确保内容在按钮中居中显示。RecognizesAccessKey="True"
:支持访问键(如&Save
中的&
符号)
<Button Content="按钮控件" Width="300" Height="80" Margin="5" >
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="Transparent" CornerRadius="5" BorderThickness="1" BorderBrush="#C9CCD5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
常见问题
- 内容不显示:
- 检查是否在模板中正确放置了
ContentPresenter
。 - 确保模板化控件的
Content
或ContentTemplate
已正确设置。
- 检查是否在模板中正确放置了
- 样式不继承:
- 确保
ContentPresenter
的属性(如Foreground
)未被模板中的其他控件覆盖。 - 使用
TemplateBinding
绑定样式属性。
- 确保
DataTemplate
DataTemplate
是 WPF 和 UWP 等 XAML 框架中用于定义数据可视化方式的模板。它允许开发者指定如何将数据对象(如业务模型、集合项等)呈现为 UI 元素(如文本、图像、按钮等)。通过 DataTemplate
,开发者可以将数据与 UI 分离,实现数据的灵活展示和复用。
一个典型的 DataTemplate
包含以下关键部分:
- 绑定(Bindings):用于将数据对象的属性绑定到 UI 元素的属性。
- 控件组合:通过组合多个 XAML 控件(如
TextBlock
、Image
、Button
)来定义数据的可视化结构。 - 触发器(Triggers):可选,用于响应数据状态变化,动态修改 UI 元素的外观。
- 数据类型指定:通过
DataType
属性指定模板适用的数据类型。
虚拟化:对于大型数据集合,启用虚拟化(如 VirtualizingStackPanel
)可以显著提高性能。
<ListBox ItemsSource="{Binding People}" VirtualizingStackPanel.IsVirtualizing="True"> <!-- 模板内容 --> </ListBox>
<!-- 定义 Person 类 -->
<Window.Resources>
<x:Type x:Key="PersonType" Type="local:Person"/>
<!-- 定义 DataTemplate 使用DataType
属性指定模板适用于Person
类型。-->
<DataTemplate DataType="{x:Type local:Person}">
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Age, StringFormat=Age: {0}}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<!-- 使用 DataTemplate 的 ListBox -->
<ListBox ItemsSource="{Binding People}">
<!-- 无需显式指定 ItemTemplate,因为已经定义了隐式 DataTemplate -->
- 由于
DataTemplate
定义了DataType
,当ListBox
的ItemsSource
包含Person
对象时,XAML 会自动应用该模板,无需显式指定ItemTemplate
。</ListBox>
如果需要为特定控件或场景显式指定 DataTemplate
,可以使用 ItemTemplate
、ContentTemplate
等属性
<ListBox ItemsSource="{Binding People}">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- 模板内容 -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
数据触发器(DataTriggers):
<DataTemplate DataType="{x:Type local:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Age}" Value="18">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
ItemsPanelTemplate
ItemsPanelTemplate
是 WPF 和 UWP 等 XAML 框架中的一个关键组件,用于定义 集合控件(如 ListBox
、ListView
、ComboBox
、ItemsControl
等)中项目的布局容器。它允许开发者自定义项目的排列方式,
核心作用
- 自定义布局容器:决定集合控件中项目的容器类型(如
StackPanel
、WrapPanel
、Grid
等)。 - 动态调整布局:根据业务需求灵活切换布局方式,无需修改数据绑定逻辑。
- 支持复杂布局:实现如网格、虚拟化滚动等高级布局需求。
关键特性
- 布局容器定义:指定
ItemsControl
的ItemsPanel
属性所使用的模板。 - 与
ItemTemplate
分离:ItemsPanelTemplate
负责布局容器,ItemTemplate
负责单个项目的外观。 - 支持虚拟化:与
VirtualizingStackPanel
配合,优化大数据量时的性能。
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<!-- 使用 ItemsPanelTemplate 将 ListBox 的项目布局为网格 -->
<ListBox Width="400" Height="300">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<!-- 使用 UniformGrid 作为布局容器,自动排列为网格 -->
<UniformGrid Columns="3" Rows="2"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="1" Margin="5" Padding="10">
<TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<!-- 示例数据 -->
<sys:String xmlns:sys="clr-namespace:System;assembly=mscorlib">Item 1</sys:String>
<sys:String xmlns:sys="clr-namespace:System;assembly=mscorlib">Item 2</sys:String>
</ListBox>
ItemsPanelTemplate
的定义:- 使用
<UniformGrid Columns="3" Rows="2"/>
作为布局容器,将项目排列为 3 列 2 行的网格。 UniformGrid
会自动平均分配空间给每个项目。
- 使用
ItemTemplate
的定义:- 使用
DataTemplate
定义每个项目的外观(如边框、文本对齐方式)。
- 使用
ItemTemplate
与 ItemsPanelTemplate
的区别
ItemTemplate
:- 定义单个项目的视觉外观(如文本、图像、按钮)。
- 影响项目的样式和内容。
ItemsPanelTemplate
:- 定义项目的布局容器(如
StackPanel
、Grid
)。 - 影响项目的排列方式。
- 定义项目的布局容器(如
维度 | ItemTemplate | ItemsPanelTemplate |
---|---|---|
职责 | 定义单个项目的视觉外观 | 定义所有项目的布局容器 |
作用范围 | 单个项目 | 所有项目 |
典型内容 | TextBlock 、Image 、Button 等 | StackPanel 、WrapPanel 、Grid 等 |
数据绑定 | 通过 {Binding} 绑定到数据属性 | 不涉及数据绑定 |
性能影响 | 轻量级,通常不影响性能 | 布局复杂度可能影响性能 |
<ListBox ItemsSource="{Binding Users}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
Setter
<Style TargetType="Button">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="Black"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Style
定义:TargetType="Button"
:表示该样式适用于所有Button
控件。
Setter
元素:<Setter Property="Foreground" Value="Red"/>
:将按钮的文本颜色设置为红色。<Setter Property="Background" Value="LightGray"/>
:将按钮的背景色设置为浅灰色。<Setter Property="BorderThickness" Value="1"/>
:设置按钮的边框厚度为 1。<Setter Property="Padding" Value="10,5"/>
:设置按钮的内边距为 10(水平)和 5(垂直)。
ControlTemplate
:- 自定义按钮的视觉结构,使用
Border
和ContentPresenter
实现圆角按钮。
- 自定义按钮的视觉结构,使用
绑定表达式:
Value
可以是一个绑定表达式,动态设置属性值。
<Setter Property="Foreground" Value="{Binding TextColor}"/>
条件设置:
- 结合
Trigger
或DataTrigger
,根据条件动态修改属性值。
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="DarkRed"/>
</Trigger>
</Style.Triggers>
作用域:
Setter
只在定义的 Style
或 ControlTemplate
范围内生效。
优先级:
如果多个 Setter
尝试设置同一属性,后定义的 Setter
会覆盖先前的值。
Style
Style
的作用
- 统一外观:通过
Style
,可以为控件定义统一的外观(如背景色、字体、边框等)。 - 复用性:命名的样式(通过
x:Key
定义)可以在多个控件中重复使用。 - 动态修改:可以通过
Trigger
或DataTrigger
动态修改样式。
x:Key
属性
- 命名样式:
x:Key
为样式指定一个唯一的名称,使其可以在 XAML 中被引用。 - 复用方式:通过
StaticResource
或DynamicResource
引用命名样式。
<Window.Resources>
<!-- 定义 CardButtonStyle 样式 -->
<Style x:Key="CardButtonStyle" TargetType="Button">
<Setter Property="Background" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="5"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- 鼠标悬停时的效果 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="DarkBlue"/>
</Trigger>
<!-- 按钮按下时的效果 -->
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="DarkBlue"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<!-- 使用 CardButtonStyle 样式的按钮 -->
<Button Content="Card Button 1" Style="{StaticResource CardButtonStyle}" Width="120" Height="60"/>
<Button Content="Card Button 2" Style="{StaticResource CardButtonStyle}" Width="120" Height="60" HorizontalAlignment="Right"/>
</Grid>
通过 Style="{StaticResource CardButtonStyle}"
将样式应用于按钮控件。
动态资源:
如果需要动态切换样式,可以使用 DynamicResource
:
<Button Content="Dynamic Style" Style="{DynamicResource CardButtonStyle}"/>
全局样式:
如果不指定 x:Key
,而是指定 TargetType
,样式将应用于所有指定类型的控件:
<Style TargetType="Button">
<!-- 样式定义 -->
</Style>
Trigger触发器
Trigger
是 WPF 和 UWP 等 XAML 框架中的核心机制,用于动态改变控件的样式或行为,而无需编写代码。通过 Trigger
,开发者可以根据控件的属性值、事件或数据状态自动调整控件的外观(如颜色、大小、可见性等)或触发动画。
核心作用
- 状态响应:根据控件的状态(如鼠标悬停、选中、禁用)自动改变样式。
- 数据驱动:根据数据绑定属性的值动态调整 UI。
- 减少代码:通过 XAML 声明式地实现交互逻辑,避免后台代码。
关键特性
- 条件判断:通过
Condition
定义触发条件。 - Setter 集合:通过
Setter
定义触发时的样式更改。 - 嵌套支持:
Trigger
可以嵌套在Style
、ControlTemplate
或DataTemplate
中。
Trigger 类型 | 作用 | 适用场景 |
---|---|---|
PropertyTrigger | 根据控件的属性值变化触发样式更改。 | 例如:按钮在鼠标悬停时改变背景色。 |
EventTrigger | 根据控件的事件(如 Loaded 、Click )触发动画或样式更改。 | 例如:页面加载时播放动画。 |
DataTrigger | 根据数据绑定属性的值触发样式更改。 | 例如:根据数据状态(如 IsSelected )改变项目样式。 |
MultiTrigger | 根据多个条件的组合触发样式更改。 | 例如:同时满足 IsMouseOver 和 IsEnabled 时改变样式。 |
MultiDataTrigger | 根据多个数据绑定属性的组合触发样式更改。 | 例如:根据多个数据状态(如 IsAdmin 和 IsLoggedIn )改变界面。 |
<Button Content="Hover Me" Width="100" Height="30">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="LightGray"/>
<Style.Triggers>
<!-- 鼠标悬停时改变背景色 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
DataTrigger
(根据数据状态改变样式)
<ListBox ItemsSource="{Binding Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="1" Margin="5" Padding="10">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<!-- 如果 IsSelected 为 True,则改变背景色 -->
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="LightBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>