前言
之前项目都是用Mvvmlight的,但是毕竟功能不如prism全,所以自己玩一玩Prism。这里主要记录在使用过程中卡壳需要找一下资料或者觉得会经常使用的地方;
#给Module注册一个默认的页面
public class ModuleRightModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("ContentRegion", typeof(IconContent));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<IconContent>();
}
}
#让页面缓存
Viewmodel 继承IRegionMemberLifetime
public bool KeepAlive => true;
#ViewModel 和View 绑定
在View里面添加prism:ViewModelLocator.AutoWireViewModel="True"
嗯,ViewModel 和View的绑定规则很死,看源码:
https://github.com/PrismLibrary/Prism/blob/master/src/Prism.Core/Mvvm/ViewModelLocationProvider.cs 45行
static Func<Type, Type> _defaultViewTypeToViewModelTypeResolver =
viewType =>
{
var viewName = viewType.FullName;
viewName = viewName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName);
return Type.GetType(viewModelName);
};
就只是把命名空间里面的.Views改成.ViewModels;所以你换个文件夹,层级不对呀之类的 都匹配不到(嗯 自己手动换命名空间的无视);
最后当你的视图以View结尾的时候,就只能匹配到以Model结尾的VM(这个是看源码之后才知道的,所以看源码是很重要滴);
#prism 弹出子窗体
- prism默认有一个DialogWindow,一般情况下,我们只需要构建窗体里面的内容就可以了。只需要注册后窗体内容以及对应的ViewModel继承IDialogAware就可以了。
在App.xaml.cs里面添加
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
///在这里注册Dialog
containerRegistry.RegisterDialog<Setting,SettingViewModel>("Setting");
}
ViewModel(SettingViewModel)需要继承IDialogAware,用来和承载页面的窗体交互。
- 如果需要修改弹出框的样式
1.直接修改DialogWindow样式
<prism:Dialog.WindowStyle>
<Style TargetType="Window">
<Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterScreen" />
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="ShowInTaskbar" Value="False"/>
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:Dialog.WindowStyle>
2.使用自己定义的窗体
a.新建一个窗体Window,并使其继承IDialogWindow
b.在App.xaml.cs里面添加注册代码
containerRegistry.RegisterDialogWindow<ChildWindow>();
- 当一个应用里面需要弹出多个自定义的窗体。
prism的IOC 在注册的时候有参数name;
///添加扩展方法,以便于可以在弹框的时候根据Name,来选择弹出哪一个窗体
public static class IContainerRegistryExtensions
{
/// <summary>
/// Registers an object that implements IDialogWindow to be used to host all dialogs in the IDialogService.
/// </summary>
/// <typeparam name="TWindow">The Type of the Window class that will be used to host dialogs in the IDialogService</typeparam>
/// <param name="containerRegistry"></param>
public static void RegisterDialogWindow<TWindow>(this IContainerRegistry containerRegistry,string name) where TWindow : Prism.Services.Dialogs.IDialogWindow
{
containerRegistry.Register(typeof(Prism.Services.Dialogs.IDialogWindow), typeof(TWindow),name);
}
}
窗体有名字了,但是IDialogService里面一个Show和ShowDialog方法都没有传WindowName的方法,那就继续扩展,以下代码主要来源于Prism的源码,最新源码有带WindowName的方法,但是引用的版本里面是没有的,又再一次验证了看源码的重要性。
public interface IToolDialogService: IDialogService
{
void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName);
void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName);
}
public class ToolDialogService : IToolDialogService
{
private readonly IContainerExtension _containerExtension;
/// <summary>
/// Initializes a new instance of the <see cref="DialogService"/> class.
/// </summary>
/// <param name="containerExtension"></param>
public ToolDialogService(IContainerExtension containerExtension)
{
_containerExtension = containerExtension;
}
/// <summary>
/// Shows a non-modal dialog.
/// </summary>
/// <param name="name">The name of the dialog to show.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
public void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback)
{
ShowDialogInternal(name, parameters, callback, false);
}
/// <summary>
/// Shows a non-modal dialog.
/// </summary>
/// <param name="name">The name of the dialog to show.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
/// <param name="windowName">The name of the hosting window registered with the IContainerRegistry.</param>
public void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName)
{
ShowDialogInternal(name, parameters, callback, false, windowName);
}
public void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName,string unique)
{
ShowDialogInternal(name, parameters, callback, false, windowName, unique);
}
/// <summary>
/// Shows a modal dialog.
/// </summary>
/// <param name="name">The name of the dialog to show.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
public void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback)
{
ShowDialogInternal(name, parameters, callback, true);
}
/// <summary>
/// Shows a modal dialog.
/// </summary>
/// <param name="name">The name of the dialog to show.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
/// <param name="windowName">The name of the hosting window registered with the IContainerRegistry.</param>
public void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName)
{
ShowDialogInternal(name, parameters, callback, true, windowName);
}
void ShowDialogInternal(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool isModal, string windowName = null)
{
IDialogWindow dialogWindow = CreateDialogWindow(windowName);
ConfigureDialogWindowEvents(dialogWindow, callback);
ConfigureDialogWindowContent(name, dialogWindow, parameters);
if (isModal)
dialogWindow.ShowDialog();
else
dialogWindow.Show();
}
/// <summary>
/// Create a new <see cref="IDialogWindow"/>.
/// </summary>
/// <param name="name">The name of the hosting window registered with the IContainerRegistry.</param>
/// <returns>The created <see cref="IDialogWindow"/>.</returns>
protected virtual IDialogWindow CreateDialogWindow(string name)
{
if (string.IsNullOrWhiteSpace(name))
return _containerExtension.Resolve<IDialogWindow>();
else
return _containerExtension.Resolve<IDialogWindow>(name);
}
/// <summary>
/// Configure <see cref="IDialogWindow"/> content.
/// </summary>
/// <param name="dialogName">The name of the dialog to show.</param>
/// <param name="window">The hosting window.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
protected virtual void ConfigureDialogWindowContent(string dialogName, IDialogWindow window, IDialogParameters parameters)
{
var content = _containerExtension.Resolve<object>(dialogName);
var dialogContent = content as FrameworkElement;
if (dialogContent == null)
throw new NullReferenceException("A dialog's content must be a FrameworkElement");
var viewModel = dialogContent.DataContext as IDialogAware;
if (viewModel == null)
throw new NullReferenceException("A dialog's ViewModel must implement the IDialogAware interface");
ConfigureDialogWindowProperties(window, dialogContent, viewModel);
MvvmHelpers.ViewAndViewModelAction<IDialogAware>(viewModel, d => d.OnDialogOpened(parameters));
}
/// <summary>
/// Configure <see cref="IDialogWindow"/> and <see cref="IDialogAware"/> events.
/// </summary>
/// <param name="dialogWindow">The hosting window.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
protected virtual void ConfigureDialogWindowEvents(IDialogWindow dialogWindow, Action<IDialogResult> callback)
{
Action<IDialogResult> requestCloseHandler = null;
requestCloseHandler = (o) =>
{
dialogWindow.Result = o;
dialogWindow.Close();
};
RoutedEventHandler loadedHandler = null;
loadedHandler = (o, e) =>
{
dialogWindow.Loaded -= loadedHandler;
dialogWindow.GetDialogViewModel().RequestClose += requestCloseHandler;
};
dialogWindow.Loaded += loadedHandler;
CancelEventHandler closingHandler = null;
closingHandler = (o, e) =>
{
if (!dialogWindow.GetDialogViewModel().CanCloseDialog())
e.Cancel = true;
};
dialogWindow.Closing += closingHandler;
EventHandler closedHandler = null;
closedHandler = (o, e) =>
{
dialogWindow.Closed -= closedHandler;
dialogWindow.Closing -= closingHandler;
dialogWindow.GetDialogViewModel().RequestClose -= requestCloseHandler;
dialogWindow.GetDialogViewModel().OnDialogClosed();
if (dialogWindow.Result == null)
dialogWindow.Result = new DialogResult();
callback?.Invoke(dialogWindow.Result);
dialogWindow.DataContext = null;
dialogWindow.Content = null;
};
dialogWindow.Closed += closedHandler;
}
/// <summary>
/// Configure <see cref="IDialogWindow"/> properties.
/// </summary>
/// <param name="window">The hosting window.</param>
/// <param name="dialogContent">The dialog to show.</param>
/// <param name="viewModel">The dialog's ViewModel.</param>
protected virtual void ConfigureDialogWindowProperties(IDialogWindow window, FrameworkElement dialogContent, IDialogAware viewModel)
{
var windowStyle = Dialog.GetWindowStyle(dialogContent);
if (windowStyle != null)
window.Style = windowStyle;
window.Content = dialogContent;
window.DataContext = viewModel; //we want the host window and the dialog to share the same data context
if (window.Owner == null)
window.Owner = Application.Current?.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);
}
}
最后别忘记了在App.xaml.cs 添加
containerRegistry.Register<IToolDialogService,ToolDialogService>();
- 但是,以上还是不能满足。因为这个子窗体总是在父窗体上面,下面这句惹的祸,而且我想只有一个子窗体,如果存在,我再点击的时候,只激活,不新建怎么处理(例如微信的文件处理子窗体)
if (window.Owner == null) window.Owner = Application.Current?.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);
思路:给窗体添加一个唯一值,然后打开的时候判断是否存在,如果有就激活,没有则新建。只有Show才会出现这种情况。 添加一个接口 public interface ISingleWindow { string UniqueValue { get; set; } } IToolDialogService 增加方法 void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName,string unique); ToolDialogService实现 public void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName,string unique) { ShowDialogInternal(name, parameters, callback, false, windowName, unique); } void ShowDialogInternal(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool isModal, string windowName = null, string unique = null) { if (unique != null) { var window = Application.Current?.Windows.OfType<Window>().FirstOrDefault(x => x is ISingleWindow singleWindow && singleWindow.UniqueValue == unique); if (window != null) { if (window.WindowState == WindowState.Minimized) { window.WindowState = WindowState.Normal; } window.Activate(); return; } } IDialogWindow dialogWindow = CreateDialogWindow(windowName); if (dialogWindow is ISingleWindow singleWindow) { singleWindow.UniqueValue = unique; } ConfigureDialogWindowEvents(dialogWindow, callback); ConfigureDialogWindowContent(name, dialogWindow, parameters); if (isModal) dialogWindow.ShowDialog(); else dialogWindow.Show(); }