当前位置:   article > 正文

【WPF】消息蒙版弹窗UI以及await实现等待反馈(popup)_wpf popup

wpf popup

一、概要

原版的消息框太丑?不喜欢?如果您对原版消息框的外观不太满意,或者不符合您的应用程序的需求,那么可以通过自定义消息框来实现所需的外观和功能。

原版的消息框:
原版消息框展示
可以看出这个消息框可能和你设计的UI界面格格不入
自定义消息框可以实现各种风格和布局的效果。
下面我展示一个B端设计的消息框界面
设计来自B端-薪福通
那么我们如何做到类似的效果呢?

  • 构思背景遮罩逻辑
  • 制作UI界面(遮罩+Popup)
  • Cs后端弹窗实现(Await)

实现效果展示:
退出实现效果展示
演示GIF

其中,按钮颜色、文本,图标等都可以通过自定义。

快速通道:下载链接

二、UI制作流程

下面我们一步一步制作这个弹窗。

1.遮罩逻辑

首先是背景层:
背景层是一个半透明的遮罩蒙版。

<Grid x:Name="MessageGridBox" Visibility="Collapsed">
    <Border Background="White" Opacity="0.8" CornerRadius="5"/>
	<!--这里放消息框的部分--!/>


	<!--这里放消息框的部分--!/>
</Grid>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

效果就是一块蒙版挡住用户其他使用区域。(当然你也可以选择不挡着或者设计逻辑选择性挡住或者不挡住,挡住的话可以防止用户不处理这个消息就去其他操作。)
这里的CornerRadius="5"可以改为自己的主界面的圆角,当然也可以不设置。

2.Popup展示层

中间层是Popup展示层:
为了让消息框弹出不那么突兀,使用了PopupFade展示效果显示。

<Popup x:Name="MessageGridPopup" PopupAnimation="Fade" Placement="Center" AllowsTransparency="True" StaysOpen="True">
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="450" MaxHeight="250" MinWidth="400" MinHeight="165">
        <Grid.RowDefinitions>
            <RowDefinition Height="55"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Rectangle Grid.RowSpan="2" Fill="White" Margin="5" Opacity="1" RadiusY="5" RadiusX="5">
            <Rectangle.Effect>
                <DropShadowEffect Color="#FFBBBBBB" Direction="0" BlurRadius="10" RenderingBias="Quality" ShadowDepth="0" Opacity="0.3"></DropShadowEffect>
            </Rectangle.Effect>
        </Rectangle>
        <!--这里放消息展示内容部分--!/>


		<!--这里放消息展示内容部分--!/>
    </Grid>
</Popup>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.消息展示层

消息展示层是核心区域,用于动态更新消息框的展示文本。
我分三个部分来介绍:

  • 标题部分
 <Grid Margin="10,0,10,0">
     <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Margin="10,0,0,0">
         <TextBlock x:Name="MessageIconQ" Text="&#xe743;" FontFamily="{DynamicResource Iconfont}" FontSize="22" VerticalAlignment="Center" Margin="10"/>
         <TextBlock x:Name="MessageIconC" Text="&#xe623;" FontFamily="{DynamicResource Iconfont}" FontSize="22" VerticalAlignment="Center" Margin="10" Visibility="Collapsed"/>
         <TextBlock  x:Name="MessageTitleBox" Text="标题显示" FontFamily="{DynamicResource BlodFont}" FontSize="15" VerticalAlignment="Center"/>
     </StackPanel>
     <Button HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,20,10" FontSize="22" Style="{DynamicResource wdButton}" Click="CancelBtn_Click">
         <Button.Content>
             <TextBlock FontFamily="{DynamicResource Iconfont}" Text="&#xe620;"/>
         </Button.Content>
     </Button>
 </Grid>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 内容区域
 <StackPanel Grid.RowSpan="2" Margin="10">
     <TextBlock x:Name="MessageContentBox" Margin="50,60,50,80" TextWrapping="Wrap" Text="消息框测试" FontSize="14" FontFamily="{DynamicResource RegularFont}"/>
 </StackPanel>
  • 1
  • 2
  • 3
  • 按钮部分
 <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10" >
     <Button x:Name="CancelBtn" Content="取消" Style="{DynamicResource wButton}" Width="100" FontSize="15" Height="35" Margin="10" Click="CancelBtn_Click"/>
     <Button x:Name="ConfirmBtn" Content="确定" Style="{DynamicResource mButton}" Width="100" FontSize="15" Height="35" Margin="10" Click="ConfirmBtn_Click"/>
 </StackPanel>
  • 1
  • 2
  • 3
  • 4

按钮还可以继续增加,逻辑我就不赘述了,可以看后面的代码研究一下。

值得注意的是:这里的Style是我自己做的,你也可以更换为自己的按钮样式,如果有需要可以后台私信我。除了底层逻辑,这些样式都可以自己自定义。

三、C#后端代码逻辑

1. 定义变量

首先定义一个TaskCompletionSource<bool> 命名taskCompletionSource,当然名字可以自己改。

private TaskCompletionSource<bool> taskCompletionSource;
#这段内容需要全局变量哦
  • 1
  • 2

2. 定义函数

写一个显示消息框的函数,注意函数需要async才支持等待用户回复。

private async Task<bool> ShowMessageGridBoxAsync(string MessageContent,string title,bool isconfirm = true, int ButtonIndex=0)
{
	 /// <summary>
	 /// 参数分别是 
	 /// MessageContent 信息内容
	 /// title 信息标题
	 /// isconfirm 是否确认按钮(判断Icon情况)
	 /// ButtonIndex 按钮类型
	 /// </summary>	
	//#我这里的后两个参数带默认值,所以可以直接简化写。

	//#展示消息框
	MessageGridBox.Visibility = Visibility.Visible;
	MessageTitleBox.Text = title;
	MessageContentBox.Text = MessageContent;  
	//#上面这些比较简单就不赘述了。分别赋值进去。
	
	//#重新赋值一个 taskCompletionSource 让其重置。
	taskCompletionSource = new TaskCompletionSource<bool>();

	//#判断ICON 其中MessageIconC是确认的ICON,MessageIconQ是询问的ICON
    if (isconfirm)
    {
        CancelBtn.Visibility = Visibility.Collapsed;
        MessageIconC.Visibility = Visibility.Visible; 
        MessageIconQ.Visibility = Visibility.Collapsed;
    }
    else
    {
        CancelBtn.Visibility = Visibility.Visible;
        MessageIconC.Visibility = Visibility.Collapsed; 
        MessageIconQ.Visibility = Visibility.Visible;
    }
      
	//#判断按钮的类型
	switch (ButtonIndex)
	{
	    case 0:
	        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["mButton"]);
	        ConfirmBtn.Content = "确认";
	        break;
	    case 1:
	        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["RButton"]);
	        ConfirmBtn.Content = "删除";
	        System.Media.SystemSounds.Exclamation.Play();
	        break;
	    case 2:
	        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["RButton"]);
	        ConfirmBtn.Content = "确认";
	        System.Media.SystemSounds.Exclamation.Play();
	        break;
	    case 3:
	        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["RButton"]);
	        ConfirmBtn.Content = "重试";
	        System.Media.SystemSounds.Exclamation.Play();
	        break;
	    case 4:
	        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["mButton"]);
	        ConfirmBtn.Content = "我知道了";
	        break;
	}
	
	//#这里写了4个类型,你可以再自定义更多

	//#这里要先关闭再打开!不然动态效果就没了。
	MessageGridPopup.IsOpen = false;
	MessageGridPopup.IsOpen = true;
	
	//#等待Task用户操作
	await taskCompletionSource.Task;

	//#操作后隐藏遮罩与Popup
	MessageGridBox.Visibility = Visibility.Collapsed;
	MessageGridPopup.IsOpen = false;
	
	//#返回Task的Result
	return taskCompletionSource.Task.Result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

3.写按钮按下的逻辑

 private void ConfirmBtn_Click(object sender, RoutedEventArgs e)
 {
     // 设置任务结果为true,并标记任务为已完成
     taskCompletionSource.SetResult(true);
 }

 private void CancelBtn_Click(object sender, RoutedEventArgs e)
 {
     // 设置任务结果为false,并标记任务为已完成
     taskCompletionSource.SetResult(false);
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.如何调用这个函数

确认框情况

ShowMessageGridBoxAsync("-欢迎使用-", "欢迎:");
  • 1

需要判断的情况

bool result = await ShowMessageGridBoxAsync("寻找不到该文件,是否删除?", "错误:", false, 1);
//这里的1是删除按钮哦,可以看前面的定义
  • 1
  • 2

四、技术细节/常见错误

  1. 消息框初始化需要关闭状态。不然会出错哦
  2. Popup的StaysOpen="True",不然点击其他地方就自动关闭了,然后就会剩下遮罩无法操作
  3. Popup和Grid都需要在用户操作完后隐藏!
  4. 在调用需要await的情况,所在函数需要支持async才可以,
    “await”运算符只能用于异步方法中。请考虑用“async"修饰符标记此方法。

举个例子:

private async void DeleteButton_Click(object sender, RoutedEventArgs e)
{ 
    bool result = await ShowMessageGridBoxAsync("点击删除后记录将被删除,该操作不可逆。", "确认要删除这条记录么?", false, 1);    
    if (result)
    {
    	//按钮逻辑
    }   
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

五、小结/完整代码

Xml部分

<Grid x:Name="MessageGridBox" Visibility="Collapsed">
    <Border Background="White" Opacity="0.8" CornerRadius="5"/>
    <Popup x:Name="MessageGridPopup" PopupAnimation="Fade" Placement="Center" AllowsTransparency="True" StaysOpen="True">
        <Grid HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="450" MaxHeight="250" MinWidth="400" MinHeight="165">
            <Grid.RowDefinitions>
                <RowDefinition Height="55"/>
                <RowDefinition />
            </Grid.RowDefinitions>
            <Rectangle Grid.RowSpan="2" Fill="White" Margin="5" Opacity="1" RadiusY="5" RadiusX="5">
                <Rectangle.Effect>
                    <DropShadowEffect Color="#FFBBBBBB" Direction="0" BlurRadius="10" RenderingBias="Quality" ShadowDepth="0" Opacity="0.3"></DropShadowEffect>
                </Rectangle.Effect>
            </Rectangle>                        
            <Grid Margin="10,0,10,0">
                <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Margin="10,0,0,0">
                    <TextBlock x:Name="MessageIconQ" Text="&#xe743;" FontFamily="{DynamicResource Iconfont}" FontSize="22" VerticalAlignment="Center" Margin="10"/>
                    <TextBlock x:Name="MessageIconC" Text="&#xe623;" FontFamily="{DynamicResource Iconfont}" FontSize="22" VerticalAlignment="Center" Margin="10" Visibility="Collapsed"/>
                    <TextBlock  x:Name="MessageTitleBox" d:Text="标题显示" FontFamily="{DynamicResource BlodFont}" FontSize="15" VerticalAlignment="Center"/>
                </StackPanel>
                <Button HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,20,10" FontSize="22" Style="{DynamicResource wdButton}" Click="CancelBtn_Click">
                    <Button.Content>
                        <TextBlock FontFamily="{DynamicResource Iconfont}" Text="&#xe620;"/>
                    </Button.Content>
                </Button>
            </Grid>
            <StackPanel Grid.RowSpan="2" Margin="10">
                <TextBlock x:Name="MessageContentBox" Margin="50,60,50,80" TextWrapping="Wrap" Text="消息框测试" FontSize="14" FontFamily="{DynamicResource RegularFont}"/>
            </StackPanel>
            <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10" >
                <Button x:Name="CancelBtn" Content="取消" Style="{DynamicResource wButton}" Width="100" FontSize="15" Height="35" Margin="10" Click="CancelBtn_Click"/>
                <Button x:Name="ConfirmBtn" Content="确定" Style="{DynamicResource mButton}" Width="100" FontSize="15" Height="35" Margin="10" Click="ConfirmBtn_Click"/>
            </StackPanel>
        </Grid>
    </Popup>
</Grid>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

C#部分

namespace Pro
{
	public partial class MainWindow : Window
	{
		private TaskCompletionSource<bool> taskCompletionSource;
		/// <summary>
		/// 参数分别是 
		/// MessageContent 信息内容
		/// title 信息标题
		/// isconfirm 是否确认按钮(判断Icon情况)
		/// ButtonIndex 按钮类型
		/// </summary>	
		private async Task<bool> ShowMessageGridBoxAsync(string MessageContent,string title,bool isconfirm = false, int ButtonIndex=0)
		{
			 
			//#我这里的后两个参数带默认值,所以可以直接简化写。
		
			//#展示消息框
			MessageGridBox.Visibility = Visibility.Visible;
			MessageTitleBox.Text = title;
			MessageContentBox.Text = MessageContent;  
			//#上面这些比较简单就不赘述了。分别赋值进去。
			
			//#重新赋值一个 taskCompletionSource 让其重置。
			taskCompletionSource = new TaskCompletionSource<bool>();
		
			//#判断ICON 其中MessageIconC是确认的ICON,MessageIconQ是询问的ICON
		    if (isconfirm)
		    {
		        CancelBtn.Visibility = Visibility.Collapsed;
		        MessageIconC.Visibility = Visibility.Visible; 
		        MessageIconQ.Visibility = Visibility.Collapsed;
		    }
		    else
		    {
		        CancelBtn.Visibility = Visibility.Visible;
		        MessageIconC.Visibility = Visibility.Collapsed; 
		        MessageIconQ.Visibility = Visibility.Visible;
		    }
		      
			//#判断按钮的类型
			switch (ButtonIndex)
			{
			    case 0:
			        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["mButton"]);
			        ConfirmBtn.Content = "确认";
			        break;
			    case 1:
			        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["RButton"]);
			        ConfirmBtn.Content = "删除";
			        System.Media.SystemSounds.Exclamation.Play();
			        break;
			    case 2:
			        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["RButton"]);
			        ConfirmBtn.Content = "确认";
			        System.Media.SystemSounds.Exclamation.Play();
			        break;
			    case 3:
			        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["RButton"]);
			        ConfirmBtn.Content = "重试";
			        System.Media.SystemSounds.Exclamation.Play();
			        break;
			    case 4:
			        ConfirmBtn.SetValue(Button.StyleProperty, Application.Current.Resources["mButton"]);
			        ConfirmBtn.Content = "我知道了";
			        break;
			}
			
			//#这里写了4个类型,你可以再自定义更多
		
			//#这里要先关闭再打开!不然动态效果就没了。
			MessageGridPopup.IsOpen = false;
			MessageGridPopup.IsOpen = true;
			
			//#等待Task用户操作
			await taskCompletionSource.Task;
		
			//#操作后隐藏遮罩与Popup
			MessageGridBox.Visibility = Visibility.Collapsed;
			MessageGridPopup.IsOpen = false;
			
			//#返回Task的Result
			return taskCompletionSource.Task.Result;
		}
		 private void ConfirmBtn_Click(object sender, RoutedEventArgs e)
		 {
		     // 设置任务结果为true,并标记任务为已完成
		     taskCompletionSource.SetResult(true);
		 }
		
		 private void CancelBtn_Click(object sender, RoutedEventArgs e)
		 {
		     // 设置任务结果为false,并标记任务为已完成
		     taskCompletionSource.SetResult(false);
		 }

		private async void DeleteButton_Click(object sender, RoutedEventArgs e)
		{ 
		    bool result = await ShowMessageGridBoxAsync("点击删除后记录将被删除,该操作不可逆。", "确认要删除这条记录么?", false, 1);    
		    if (result)
		    {
		    	ShowMessageGridBoxAsync("-你点击了删除-", "提示:");
		    }else
		    {
		    	ShowMessageGridBoxAsync("-你点击了取消-", "提示:");
		    }		    
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109

感谢观看,代码还有需要完善的地方,欢迎指正。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/44345
推荐阅读
相关标签
  

闽ICP备14008679号