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 中管理导航生命周期的核心接口。- 通过实现
IsNavigationTarget
、OnNavigatedTo
和OnNavigatedFrom
,可以精细控制 ViewModel 在导航过程中的行为。 - 适用于需要初始化数据、清理资源或决定是否复用实例的场景。
- 合理使用导航参数(
NavigationParameters
)可以传递上下文数据。
区域上下文
是一种用于在区域(IRegion
)和其宿主控件(如 ContentControl
、ItemsControl
等)之间传递上下文数据的机制。它允许开发者在区域中共享数据,而无需直接依赖视图或视图模型,从而提升代码的解耦性和灵活性。
// 获取区域并设置上下文 var region = _regionManager.Regions["MainRegion"]; region.Context = new AppContext { UserId = "123", Role = "Admin" };//AppContext:自定义Mode