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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905365.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消息~组件(群聊类型)ConcurrentHashMap发送

为什么选择ConcurrentHashMap&#xff1f; 在开发聊天应用时&#xff0c;我们需要存储和管理大量的聊天消息数据&#xff0c;这些数据会被多个线程频繁访问和修改。比如&#xff0c;当多个用户同时发送消息时&#xff0c;服务端需要同时处理这些消息的存储和查询。如果用普通的…

Stapi知识框架

一、Stapi 基础认知 1. 框架定位 自动化API开发框架&#xff1a;专注于快速生成RESTful API 约定优于配置&#xff1a;通过标准化约定减少样板代码 企业级应用支持&#xff1a;适合构建中大型API服务 代码生成导向&#xff1a;显著提升开发效率 2. 核心特性 自动CRUD端点…

基于深度学习的水果识别系统设计

一、选择YOLOv5s模型 YOLOv5&#xff1a;YOLOv5 是一个轻量级的目标检测模型&#xff0c;它在 YOLOv4 的基础上进行了进一步优化&#xff0c;使其在保持较高检测精度的同时&#xff0c;具有更快的推理速度。YOLOv5 的网络结构更加灵活&#xff0c;可以根据不同的需求选择不同大…

Spring Security与SaToken的对比

Spring Security与SaToken的详细对照与优缺点分析 1. 核心功能与设计理念 对比维度Spring SecuritySaToken核心定位企业级安全框架&#xff0c;深度集成Spring生态&#xff0c;提供全面的安全解决方案&#xff08;认证、授权、攻击防护等&#xff09;轻量级权限认证框架&#…

【docker】--镜像管理

文章目录 拉取镜像启动镜像为容器连接容器法一法二 保存镜像加载镜像镜像打标签移除镜像 拉取镜像 docker pull mysql:8.0.42启动镜像为容器 docker run -dp 8080:8080 --name container_mysql8.0.42 -e MYSQL_ROOT_PASSWORD123123123 mysql:8.0.42 连接容器 法一 docker e…

力扣HOT100之二叉树:543. 二叉树的直径

这道题本来想到可以用递归做&#xff0c;但是还是没想明白&#xff0c;最后还是去看灵神题解了&#xff0c;感觉这道题最大的收获就是巩固了我对lambda表达式的掌握。 按照灵神的思路&#xff0c;直径可以理解为从一个叶子出发向上&#xff0c;在某个节点处拐弯&#xff0c;然后…

web 自动化之 yaml 数据/日志/截图

文章目录 一、yaml 数据获取二、日志获取三、截图 一、yaml 数据获取 需要安装 PyYAML 库 import yaml import os from TestPOM.common import dir_config as Dirdef read_yaml(key,file_name"test_datas.yaml"):file_path os.path.join(Dir.testcases_dir, file_…

rtty操作记录说明

rtty操作记录说明 前言 整理资料发现了几年前做的操作记录&#xff0c;分享出来&#xff0c;希望对大家有用。 rtty-master&#xff1a;rtty客户端程序&#xff0c;其中buffer\log\ssl为源码的子目录&#xff0c;从git上下载https://github.com/zhaojh329&#xff0c; rtty…

mybatis中${}和#{}的区别

先测试&#xff0c;再说结论 userService.selectStudentByClssIds(10000, "wzh or 11");List<StudentEntity> selectStudentByClssIds(Param("stuId") int stuId, Param("field") String field);<select id"selectStudentByClssI…

【运维】MacOS蓝牙故障排查与修复指南

在日常使用macOS系统过程中&#xff0c;蓝牙连接问题时有发生。无论是无法连接设备、连接不稳定还是蓝牙功能完全失效&#xff0c;这些问题都会严重影响我们的工作效率。本文将分享一些实用的排查方法和修复技巧&#xff0c;帮助你解决macOS系统上的蓝牙故障。 问题症状 常见…

数据结构(一) 绪论

一. 时间复杂度: (1)定义: 时间复杂度是衡量算法执行时间随输入规模(通常用n表示)增长的变化趋势的指标,时间复杂度用O符号表示 用于描述算法在最坏情况下或平均情况下的时间需求 时间复杂度关注的是操作次数的增长率&#xff0c;而非具体执行时间 常见的时间复杂度由小到大依次…

网络协议与系统架构分析实战:工具与方法全解

网络协议与系统架构分析实战&#xff1a;工具与方法全解 在互联网系统的开发、运维与安全分析中&#xff0c;协议解析与抓包分析是不可或缺的核心技能。本文将系统梳理主流协议解析工具、协议自动识别方案&#xff0c;并结合实际抓包案例&#xff0c;讲解如何还原和推测底层系…

发那科机器人4(编程实例)

发那科机器人4(编程实例) 一、编程实例1、直线运动实例2、圆弧运动实例3、曲线运动实例4、物料搬运实例5、异步输送带检测一、编程实例 1、直线运动实例 本节内容:直线运动实例 本次实例,采用的是基础模块,以基础模块当中的四边形为例,演示一下机器人的直线运动。 编程…

agent初识

AI Agent 时代已来&#xff1a;不止于聊天的智能体&#xff0c;将如何重塑我们的世界&#xff1f; AI Agent 时代已来&#xff1a;不止于聊天的智能体&#xff0c;将如何重塑我们的世界&#xff1f; 你是否曾惊叹于 ChatGPT 的对答如流&#xff1f;或者 Midjourney 的妙笔生花…

.Net HttpClient 使用Json数据

HttpClient 使用Json数据 现代Web项目中&#xff0c;Json是最常用的数据格式。不论是前后端的交互中&#xff0c;还是纯前端项目中&#xff0c;都是如此。因此&#xff0c;.Net HttpClient 能不能更加方便、快捷的处理Json格式数据&#xff0c;也就至关重要了&#xff01; 文末…

UDP--DDR--SFP,FPGA实现之指令监测模块实现

指令监测模块实现介绍 如下图所示&#xff0c;为指令监测模块的运行框图 将指令设置为8bytes数据&#xff0c;故需要一个64位寄存器进行缓存&#xff0c;在进行数据缓存时&#xff0c;数据不可以输出至下一级模块&#xff0c;故对数据和有效指示信号也应该进行相应延迟&#…

JavaScript双问号操作符(??)详解,解决使用 || 时因类型转换带来的问题

目录 JavaScript双问号操作符&#xff08;??&#xff09;详解&#xff0c;解决使用||时因类型转换带来的问题 一、双问号操作符??的基础用法 1、传统方式的痛点 2、双问号操作符??的精确判断 3、双问号操作符??与逻辑或操作符||的对比 二、复杂场景下的空值处理 …

智能体的典型应用:自动驾驶、智能客服、智能制造、游戏AI与数字人技术

本文为《React Agent&#xff1a;从零开始构建 AI 智能体》专栏系列文章。 专栏地址&#xff1a;https://blog.csdn.net/suiyingy/category_12933485.html。项目地址&#xff1a;https://gitee.com/fgai/react-agent&#xff08;含完整代码示​例与实战源&#xff09;。完整介绍…

Ubuntu 22.04(WSL2)使用Docker安装Redis

Ubuntu 22.04&#xff08;WSL2&#xff09;使用Docker安装Redis 本教程将指导您在运行于WSL2的Ubuntu 22.04上通过Docker安装Redis 7.4.3。您将获得一个配置了自定义设置、持久化存储和安全选项的Redis实例。 前提条件 WSL2上已安装Ubuntu 22.04。WSL2上已安装并运行Docker&…

浅谈 Redis 数据类型

浅谈 Redis 数据类型 &#xff08;一&#xff09;String 类型 Redis 的 String 类型 是二进制安全的&#xff0c;可以用来存储 文本字符串、int 类型数据和 bitmap 位图 等数据。 1. 字符串操作 适用于存储 文本、JSON、序列化数据 等任意二进制安全的内容 命令作用示例SET设…