如何在Wpf程序中使用MaterialDesign,实现mvvm,及封装常用的确认对话框、信息输入对话框、进度等待框

前言

本文通过一个简单的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上,欢迎下载共同学习和交流。
源码下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hyq106

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值