前言
本文通过一个简单的demo示例,介绍了MaterialDesign在wpf应用程序中的使用,实现wpf的mvvm,并封装了MaterialDesign酷炫的弹窗对话框,在此做一些总结,并在文章的最后分享完整源码。
demo效果如下:
搭建MaterialDesign开发环境
添加引用
在VS2022中,基于.net6.0创建wpf项目,在Nuget包管理器输入“MaterialDesign”,安装MaterialDesign。
添加资源字典
在App.xaml文件中添加MaterialDesign的资源字典
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />
<!--<materialDesign:BundledTheme BaseTheme="Dark" PrimaryColor="DeepPurple" SecondaryColor="Lime" />-->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
编译一下,之后在MainWindow.xaml中随便加一些控件(比如Button),就能看到MaterialDesign的样式了。本demo的主界面设计如下:
说明:在MainWindow的设计中,使用了“<md:DialogHost Identifier=“Root”>”,其中md是对MaterialDesign的命名空间的申明,这个xaml意思是将此窗口作为对话框的DialogHost,并用“Root”命名作为唯一识别。此名称将传递到对话框中,告诉对话框悬停在哪个DialogHost上。在实际应用中,可以有多个DialogHost,取不同的名称即可。
MVVM的简单封装
在不使用第三方mvvm框架的情况下,我对INotifyPropertyChanged(绑定UI显示)和ICommand(绑定命令)进行了简单封装。如下:
ViewModelBase
internal class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string property=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
DelegateCommand
internal class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> action)
{
_action = action;
}
private Action<object> _action;
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
_action?.Invoke(parameter);
}
}
对话框设计
一般在cs程序中最常用的对话框就是信息提示框、确认对话框、信息输入框、等待进度提示框等。这里我进行了封装,信息输入框(InputView)、信息提示/确认对话框(MessageView)、转圈等待提示框(CurcularProgressView),其中MessageView既可以做信息提示框,也可以做确认询问框。
InputView
新建一个用户控件,取名“InputView”,以此作为信息输入框。界面设计如下:
其中,输入框的标题、提示语、默认值,都是可以通过传入参数配置的。
MessageView
新建一个用户控件,取名“MessageView”,以此作为信息提示/确认对话框。界面设计如下:
其中,对话框的标题、提示语、Cancel按钮是否可见(是否询问对话框),都是可以通过传入参数配置的。
CurcularProgressView
对话框的参数传递及数据回传
为对话框的参数传递及数据回传,设计专门的类
internal enum CommonDialogButton
{
Cancel=0, //取消
Ok=1 //确认
}
/// <summary>
/// 对话框返回的结果
/// </summary>
internal class CommonDialogResult
{
/// <summary>
/// 点击了哪个按钮
/// </summary>
public CommonDialogButton Button { get; set; }
/// <summary>
/// 回传的数据
/// </summary>
public object Data { get; set; }
}
/// <summary>
/// 对话框的参数
/// </summary>
public class CommonDialogParams
{
/// <summary>
/// 对话框悬停的Host名称
/// </summary>
public string DialogHost { get; set; } = "Root";
/// <summary>
/// 对话框的标题
/// </summary>
public string DialogTitle { get; set; }
/// <summary>
/// 对话框的提示语
/// </summary>
public string DialogTipText { get; set; }
/// <summary>
/// 如果是输入框,则可设置默认值
/// </summary>
public string DefaultText { get; set; }
/// <summary>
/// 是否是询问对话框
/// </summary>
public bool IsQuestion { get; set; }
}
ViewModel类(主要的功能实现类)
InputViewModel
internal class InputViewModel: ViewModelBase
{
public InputViewModel()
{
SureCommand = new DelegateCommand(Sure);
CancelCommand = new DelegateCommand(Cancel);
}
private string title;
public string Title
{
get { return title; }
set { title = value; RaisePropertyChanged(); }
}
private string tipText;
public string TipText
{
get { return tipText; }
set { tipText = value; RaisePropertyChanged(); }
}
private string inputString;
public string InputString
{
get { return inputString; }
set { inputString = value; RaisePropertyChanged(); }
}
private void Cancel(object p)
{
if (DialogHost.IsDialogOpen(DialogHostName))
DialogHost.Close(DialogHostName, new CommonDialogResult() {Button=CommonDialogButton.Cancel,Data=null });
}
private void Sure(object p)
{
if (string.IsNullOrEmpty(InputString))
return;
if (DialogHost.IsDialogOpen(DialogHostName))
{
DialogHost.Close(DialogHostName, new CommonDialogResult() { Button = CommonDialogButton.Ok, Data = inputString });
}
}
private string DialogHostName { get; set; } = "Root";
public DelegateCommand SureCommand { get; set; }
public DelegateCommand CancelCommand { get; set; }
public void InitParams(CommonDialogParams p)
{
DialogHostName = p.DialogHost;
Title = p.DialogTitle;
TipText = p.DialogTipText;
InputString = p.DefaultText;
}
}
MessageViewModel
internal class MessageViewModel:ViewModelBase
{
public MessageViewModel()
{
SureCommand = new DelegateCommand(Sure);
CancelCommand = new DelegateCommand(Cancel);
}
private string title;
public string Title
{
get { return title; }
set { title = value; RaisePropertyChanged(); }
}
private Visibility visibility = Visibility.Visible;
public Visibility Visibility
{
get { return visibility; }
set { visibility = value; RaisePropertyChanged(); }
}
private string content;
public string Content
{
get { return content; }
set { content = value; RaisePropertyChanged(); }
}
private void Cancel(object p)
{
if (DialogHost.IsDialogOpen(DialogHostName))
DialogHost.Close(DialogHostName, new CommonDialogResult() { Button = CommonDialogButton.Cancel, Data = null });
}
private void Sure(object p)
{
if (DialogHost.IsDialogOpen(DialogHostName))
{
DialogHost.Close(DialogHostName, new CommonDialogResult() { Button = CommonDialogButton.Ok, Data = null });
}
}
private string DialogHostName { get; set; } = "Root";
public DelegateCommand SureCommand { get; set; }
public DelegateCommand CancelCommand { get; set; }
public void InitParams(CommonDialogParams p)
{
DialogHostName = p.DialogHost;
Title = p.DialogTitle;
Content = p.DialogTipText;
Visibility = p.IsQuestion ? Visibility.Visible : Visibility.Hidden;
}
}
调用对话框的静态方法
以下两个静态方法,分别调用信息输入框和信息提示/确认框。
internal class CommonDialogShow
{
public static async Task<object> ShowInputDialog(string host, string title, string tipText, string defaulvalue)
{
CommonDialogParams ps = new CommonDialogParams()
{ DialogHost = host, DialogTitle = title, DialogTipText = tipText, DefaultText = defaulvalue };
return await DialogHost.Show(new InputView(ps), host);
}
public static async Task<object> ShowMessageDialog(string host, string title, string tipText, bool isQuestion)
{
CommonDialogParams ps = new CommonDialogParams()
{ DialogHost = host, DialogTitle = title, DialogTipText = tipText, IsQuestion=isQuestion };
return await DialogHost.Show(new MessageView(ps), host);
}
public static async Task<object> ShowCurcularProgress(string host, Action action)
{
DialogHost.Show(new CurcularProgressView(), host);
try
{
await Task.Run(() =>
{
action();
});
return new CommonDialogResult() { Button = CommonDialogButton.Ok };
}
finally
{
DialogHost.Close(host);
}
}
}
MainWindow调用对话框
新建类MainWindowViewModel作为MainWindow的ViewModel,具体代码实现如下:
internal class MainWindowViewModel : ViewModelBase
{
private string textValue="Hello, you can change this text !";
public string TextValue
{
get { return textValue; }
set { textValue = value; RaisePropertyChanged(); }
}
public DelegateCommand UpdateCommand
{
get
{
return new DelegateCommand(async (src) =>
{
CommonDialogResult result = await CommonDialogShow.ShowInputDialog("Root", "edit", "please edit the text", src.ToString()) as CommonDialogResult;
if (result.Button == CommonDialogButton.Ok)
{
await CommonDialogShow.ShowCurcularProgress("Root", () =>
{
System.Threading.Thread.Sleep(2000);
TextValue = result.Data.ToString();
});
}
});
}
}
public DelegateCommand DeleteCommand
{
get
{
return new DelegateCommand(async (src) =>
{
CommonDialogResult result = await CommonDialogShow.ShowMessageDialog("Root", "caution", "do you want to delete the text?", true) as CommonDialogResult;
if (result.Button == CommonDialogButton.Ok)
{
await CommonDialogShow.ShowCurcularProgress("Root", () =>
{
System.Threading.Thread.Sleep(2000);
TextValue = "";
});
await CommonDialogShow.ShowMessageDialog("Root", "success", "the text has been deleted!", false);
}
});
}
}
}
源码分享
最后,将本demo的完整源码分享在csdn上,欢迎下载共同学习和交流。
源码下载