Wpf学习片段

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 中管理导航生命周期的核心接口。
  • 通过实现 IsNavigationTargetOnNavigatedTo 和 OnNavigatedFrom,可以精细控制 ViewModel 在导航过程中的行为。
  • 适用于需要初始化数据、清理资源或决定是否复用实例的场景。
  • 合理使用导航参数(NavigationParameters)可以传递上下文数据。

区域上下文 

是一种用于在区域(IRegion)和其宿主控件(如 ContentControlItemsControl 等)之间传递上下文数据的机制。它允许开发者在区域中共享数据,而无需直接依赖视图或视图模型,从而提升代码的解耦性和灵活性。

// 获取区域并设置上下文 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
    • 用途:专门用于控件模板(如ControlTemplateDataTemplate)中,用于将模板中的属性绑定到模板化控件(即应用该模板的控件)的对应属性。
    • 上下文:只能在控件模板内部使用,用于模板和模板化控件之间的属性绑定。
  • 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
    • 用于定义数据的可视化方式。
    • 适用于将数据绑定到控件(如 ListBoxItemComboBoxItem)并自定义其显示方式。

ContentPresenter 

ContentPresenter 是 WPF 和 UWP 等 XAML 框架中的一个核心控件,用于在控件模板(如 ControlTemplate 或 DataTemplate)中动态呈现内容。它的主要作用是承载并显示被模板化的控件的内容,同时支持内容与模板的绑定和动态更新。

核心作用

  • 内容占位符:在控件模板中作为内容的占位符,确保内容能够正确显示。
  • 支持内容绑定:自动绑定到被模板化控件的 Content 或 ContentTemplate 属性,实现动态内容展示。
  • 继承样式和属性:自动继承模板中定义的样式(如 ForegroundFontFamily)和布局属性(如 HorizontalAlignmentVerticalAlignment)。
  • 触发器支持:与样式触发器(Triggers)配合,响应内容状态变化(如 IsMouseOverIsEnabled)并动态调整外观。

关键属性

  • 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 控件(如 TextBlockImageButton)来定义数据的可视化结构。
  • 触发器(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,可以使用 ItemTemplateContentTemplate 等属性

<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 框架中的一个关键组件,用于定义 集合控件(如 ListBoxListViewComboBoxItemsControl 等)中项目的布局容器。它允许开发者自定义项目的排列方式,

核心作用

  • 自定义布局容器:决定集合控件中项目的容器类型(如 StackPanelWrapPanelGrid 等)。
  • 动态调整布局:根据业务需求灵活切换布局方式,无需修改数据绑定逻辑。
  • 支持复杂布局:实现如网格、虚拟化滚动等高级布局需求。

关键特性

  • 布局容器定义:指定 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>

  1. ItemsPanelTemplate 的定义
    • 使用 <UniformGrid Columns="3" Rows="2"/> 作为布局容器,将项目排列为 3 列 2 行的网格。
    • UniformGrid 会自动平均分配空间给每个项目。
  2. ItemTemplate 的定义
    • 使用 DataTemplate 定义每个项目的外观(如边框、文本对齐方式)。

ItemTemplate 与 ItemsPanelTemplate 的区别

  • ItemTemplate
    • 定义单个项目的视觉外观(如文本、图像、按钮)。
    • 影响项目的样式和内容。
  • ItemsPanelTemplate
    • 定义项目的布局容器(如 StackPanelGrid)。
    • 影响项目的排列方式。
维度ItemTemplateItemsPanelTemplate
职责定义单个项目的视觉外观定义所有项目的布局容器
作用范围单个项目所有项目
典型内容TextBlockImageButton 等StackPanelWrapPanelGrid 等
数据绑定通过 {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>

  1. Style 定义
    • TargetType="Button":表示该样式适用于所有 Button 控件。
  2. 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(垂直)。
  3. 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 可以嵌套在 StyleControlTemplate 或 DataTemplate 中。
Trigger 类型作用适用场景
PropertyTrigger根据控件的属性值变化触发样式更改。例如:按钮在鼠标悬停时改变背景色。
EventTrigger根据控件的事件(如 LoadedClick)触发动画或样式更改。例如:页面加载时播放动画。
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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值