WPF 自定义MessageBox系列
第一节 简单MessageBox
第二节 倒计时MessageBox(本节)
第三节 自定义按钮的MessageBox
第四节 界面操作分离的MessageBox
第五节 替换系统的MessageBox
前言
本篇是在上一篇《C# wpf 实现简单自定义MessageBox》的基础上添加倒计时功能,有些应用场景会使用倒计时的消息框,这种通常需要我们自己实现,一般使用定时器就可以很容易实现倒计时的消息框了。
一、代码
1、MessageBox.xaml
<Window x:Class="WpfApp1.MessageBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MessageBox"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="NoResize"
ShowInTaskbar="False"
Topmost="True"
WindowStartupLocation="CenterScreen"
Width="600"
Height="260"
WindowStyle="None"
Closing="Window_Closing"
Loaded="Window_Loaded"
>
<Viewbox>
<Grid Width="600" Height="260">
<Border Margin="10" Background="White" >
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.5"/>
</Border.Effect>
<Grid Margin="20">
<TextBlock x:Name="TB_Context" Margin="0,0,0,40" Text="Ensure?" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" Foreground="Gray"/>
<Button VerticalAlignment="Bottom" Content="" Tag="No" Margin="-174,0,0,0" Height="64" Width="144" Click="No_Button_Click" Cursor="Hand" >
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border" Background="White" BorderBrush="#cccccc" >
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2"/>
</Border.Effect>
<TextBlock Name="text" Text="{Binding NoLeftTime.TotalSeconds,StringFormat='No({0})'}" FontSize="24" Foreground="Gray" HorizontalAlignment="Center" VerticalAlignment="Center">
</TextBlock>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding NoLeftTime.TotalSeconds}" Value="-1">
<Setter TargetName="text" Property="Text" Value="No"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
<Button VerticalAlignment="Bottom" Content="Yes" Tag="Yes" Margin="174,0,0,0" Height="64" Width="144" Click="Yes_Button_Click" Cursor="Hand">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border" Background="Gray" >
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2"/>
</Border.Effect>
<TextBlock Name="text" Text="{Binding YesLeftTime.TotalSeconds,StringFormat='Yes({0})'}" FontSize="24" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center">
</TextBlock>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding YesLeftTime.TotalSeconds}" Value="-1">
<Setter TargetName="text" Property="Text" Value="Yes"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Border>
</Grid>
</Viewbox>
</Window>
2、MessageBox.xaml.cs
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Threading;
namespace WpfApp1
{
public class MessageResult
{
/// <summary>
/// 结果,Yes为true,No为false
/// </summary>
public bool IsYes { get; set; }
public TimeSpan YesLeftTime { get; set; }
public TimeSpan NoLeftTime { get; set; }
}
public class MessageBoxEventArgs : EventArgs
{
/// <summary>
/// 结果,Yes为true,No为false
/// </summary>
public MessageResult Result { get; set; }
}
/// <summary>
/// MessageBox.xaml 的交互逻辑
/// </summary>
public partial class MessageBox : Window, INotifyPropertyChanged
{
public event EventHandler<MessageBoxEventArgs> Result;
public event PropertyChangedEventHandler PropertyChanged;
public string Context
{
get { return TB_Context.Text; }
set { TB_Context.Text = value; }
}
TimeSpan _yesLeftTime = TimeSpan.FromSeconds(-1);
public TimeSpan YesLeftTime
{
get { return _yesLeftTime; }
set { _yesLeftTime = value; PropertyChanged?.Invoke(this,new PropertyChangedEventArgs("YesLeftTime"));}
}
TimeSpan _noLeftTime= TimeSpan.FromSeconds(-1);
public TimeSpan NoLeftTime
{
get { return _noLeftTime; }
set { _noLeftTime = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("NoLeftTime")); }
}
bool _isLegal = false;
DispatcherTimer _timer;
public MessageBox()
{
InitializeComponent();
DataContext = this;
}
public static void Show(string context, EventHandler<MessageBoxEventArgs> result)
{
var mb = new MessageBox();
mb.Context = context;
mb.Result += result;
mb.Show();
}
public static MessageResult ShowDialog(string context)
{
return ShowDialog(context,null,null);
}
public static MessageResult ShowDialog(string context,TimeSpan ?yestCountDown, TimeSpan? noCountDown)
{
var mb = new MessageBox();
mb.Context = context;
MessageResult r = null;
if(yestCountDown!=null)
{
mb.YesLeftTime = yestCountDown.Value;
}
if (noCountDown != null)
{
mb.NoLeftTime = noCountDown.Value;
}
mb.Result += (s, e) => {
r = e.Result;
};
mb.ShowDialog();
return r;
}
private void No_Button_Click(object sender, RoutedEventArgs e)
{
_isLegal = true;
Close();
Result?.Invoke(this, new MessageBoxEventArgs() { Result = new MessageResult() { IsYes=false, YesLeftTime=this.YesLeftTime, NoLeftTime= this.NoLeftTime } });
}
private void Yes_Button_Click(object sender, RoutedEventArgs e)
{
_isLegal = true;
Close();
Result?.Invoke(this, new MessageBoxEventArgs() { Result = new MessageResult() { IsYes = true, YesLeftTime = this.YesLeftTime, NoLeftTime = this.NoLeftTime } });
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = !_isLegal;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if(YesLeftTime.TotalSeconds>0||NoLeftTime.TotalSeconds > 0)
{
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(1);
_timer.Tick += (S, E) =>
{
if (YesLeftTime.TotalSeconds > 0)
{
YesLeftTime = YesLeftTime.Add(-TimeSpan.FromSeconds(1));
}
else if (YesLeftTime.TotalSeconds == 0)
{
_timer.Stop();
Yes_Button_Click(null, null);
}
if (NoLeftTime.TotalSeconds > 0)
{
NoLeftTime = NoLeftTime.Add(-TimeSpan.FromSeconds(1));
}
else if (NoLeftTime.TotalSeconds == 0)
{
_timer.Stop();
No_Button_Click(null, null);
}
};
_timer.Start();
}
}
}
}
二、效果预览
三、调用方法
Yes倒计时30秒
var r = MessageBox.ShowDialog("Ensure?",TimeSpan.FromSeconds(30), null);
if (r.IsYes)
{
//选择了Yes
}
else
{
//选择了No
}
No倒计时30秒
var r = MessageBox.ShowDialog("Ensure?",null,TimeSpan.FromSeconds(30));
if (r.IsYes)
{
//选择了Yes
}
else
{
//选择了No
}
总结
使用了DispatcherTimer作为定时器,不用关系线程切换问题。总的来说还是比较容易实现的,虽然调用接口实现的比较简单,而且也考虑过按照系统的MessageBox接口来实现一套。但是个人认为Windows的MessageBox参数有点过于复杂,尤其是,OK、Cancel、Yes、No那一系列的按钮。与其使用那些固定的按钮,还不如定义几个灵活简单的可定制按钮。当然本篇文帐并没有做到这一点,这个将在下一篇文章中实现。