前言:
在前面的系列中,我们虽然完成了其大部分功能,但是,离正真运行,还是有一大段距离
当你F5运行时,在弹出对话框之后,如果你不即时点确定,或者上个WC回来之后,你会发现已经提示出错了
这节开始,我们将对其进行一小步一小步的优化,来避免一些明显容易引发的错误。
感知一下最原始的消息弹出框如下图:

一:传统消息框,容易引发命案
1:原始的消息框,是线程阻塞类型的,很容易引发超时问题
线程阻塞?怎么理解?
简单的说就是,WCF服务端给客户端发送了消息提示之后,一直进入等待状态,直到玩家点了确定,这时才继续做其它事情。
会产生什么问题?
玩家上WC去了?消息没人确认,自然就会超时引发异常了,而且那线程也没法接下去干其它活。
解决方案?
a:传统解决方案[加上倒计时,还是线程阻塞类型]
当初我只是想在这传统的消息框上加上倒计时自动确认,这样可以少一点避免超时情况。
于是搜了一些资料,发现要用winapi来处理,这个这个....大才小用了吧。
b:更优的解决方案
无意中发现Silverlight的ChildWindow,正好解决了这一问题。
因为 ChildWindow使用异步方式,非线程阻塞,消息一弹之后线程就回家去了。
而且用Sivlerlight内置的定时器DispatcherTimer,非常容易实现倒计时。
二:实现自定义非线程阻塞倒计时对话框,纯种Sivlerlight
1:看看纯种的长成什么样
新建项目-》Silverlight 子窗口 -》起名叫MsgBox-》找另一个界面调用一下。
比如在登陆页面测试一下:MsgBox box=new MsgBox();box.Show();
结果所见如图:

说明:
1:有背景灰色层,界面原生的还传统消息框好看多了。
2:重点提示:当初刚试的时候是直接运行MsgBox,然后在其构造函数中调用Show(),结果是出不来的。
2:改造-界面小小改动
我们将原来的xaml改造成如下:
<
controls:ChildWindow
x:Class
="ChessProject.MsgBox"
...省略一点... Width
="290"
Height
="141"
Title
="系统消息"
>
<
Grid
x:Name
="LayoutRoot"
Margin
="2"
Height
="97"
Width
="270"
>
<
Button
Visibility
="Collapsed"
x:Name
="CancelButton"
Content
="取消"
Click
="CancelButton_Click"
Width
="75"
Height
="23"
HorizontalAlignment
="Right"
Margin
="0,62,93,12"
/>
<
Button
x:Name
="OKButton"
Content
="确定"
Click
="OKButton_Click"
Width
="75"
Height
="23"
HorizontalAlignment
="Right"
Margin
="0,62,10,12"
/>
<
TextBlock
Height
="41"
TextWrapping
="Wrap"
HorizontalAlignment
="Left"
Margin
="15,15,0,0"
Name
="tbMsg"
Text
="请按确定按钮确定"
VerticalAlignment
="Top"
Width
="224"
/>
</
Grid
>
</
controls:ChildWindow
>
界面效果如图,和上图差不多[这里把取消放在前面,只是为了不显示取消时,确定还保留在原位好看点]:

3:改造,在标题加入倒计时
a:加入计时器并初始化
DispatcherTimer timer;
//
定时器
public
MsgBox()
{
InitializeComponent();
timer
=
new
DispatcherTimer();
timer.Interval
=
TimeSpan.FromSeconds(
1
);
timer.Tick
+=
new
EventHandler(timer_Tick);
}
b:新加show方法并实现倒计时
int
defaultTime
=
3
;
//
默认N秒
string
userTitle;
DispatcherTimer timer;
//
定时器
public
MsgBox()
{
//
...省略...
}
void
timer_Tick(
object
sender, EventArgs e)
{
Title
=
string
.Format(userTitle
+
"
[倒计时自动确定:{0}秒]
"
, defaultTime);
defaultTime
--
;
if
(defaultTime
==
0
)
{
ResetTimer();
}
}
void
ResetTimer()
{
timer.Stop();
defaultTime
=
3
;
}
public
void
Show(
string
msg,
string
title)
{
tbMsg.Text
=
msg;
userTitle
=
title;
Show();
timer.Start();
}
c:再调用一下看结果
MsgBox box
=
new
MsgBox();
box.Show(
"
http://cyq1162.cnblogs.com
"
,
"
路过秋天
"
);
如图:

4:扩展show函数:加入回调/倒计时时间/按钮类型/默认确认类型
首先:这个子窗口是异步的,所以,在点击完确定时,需要增加多一个回调函数;
接着:默认3秒,很明显情况不同,时间也要稍为增加变动一下;
然后:有时候按钮只是确定,有时候就是取消+确定;
最后:倒计时时间到了,默认执行确定,还是执行取消。
于是,要实现了:
a:如何实现回调?
默认子窗口就有Closed事件,我们用它的事件,在点击确定或取消时,执行Close()方法
public
MsgBox()
{
InitializeComponent();
timer
=
new
DispatcherTimer();
timer.Interval
=
TimeSpan.FromSeconds(
1
);
timer.Tick
+=
new
EventHandler(timer_Tick);
this
.Closed
+=
new
EventHandler(MsgBox_Closed);
}
void
MsgBox_Closed(
object
sender, EventArgs e)
{
//
待实现
}
private
void
OKButton_Click(
object
sender, RoutedEventArgs e)
{
this
.DialogResult
=
true
;
Close();
//
调用一下关闭
}
private
void
CancelButton_Click(
object
sender, RoutedEventArgs e)
{
this
.DialogResult
=
false
;
Close();
//
调用一下关闭
}
问题?我们难道对所有的确定都执行相同的代码?
首先说:异步不能像同步那样写if(show(xxx,xxx)){方法}
于是说:这是个很严重的问题,因为不同的确定,我们执行的事件肯定是不同的。
解决问题?匿名委托出手了!!!
匿名委托:[Action
<
T1,T2,T3...N个重载
>
],这是个很好用的东东,可以传进方法名称,在执行后调用不同的方法。
匿名委托实现:
Action
<
bool
>
callBackEvent;
//
全局定义
void
MsgBox_Closed(
object
sender, EventArgs e)
{
if
(callBackEvent
!=
null
)
{
callBackEvent(DialogResult.Value);
}
}
那委托是如何传入的?Show方法增加扩展参数传入。
b:这里贴出完整代码,一并实现:倒计时时间/按钮类型/默认确认类型
完整的MsgBox代码
public
partial
class
MsgBox : ChildWindow
{
int
defaultTime
=
3
;
//
默认N秒
string
userTitle;
DispatcherTimer timer;
//
定时器
Action
<
bool
>
callBackEvent;
bool
autoOKConfirm
=
true
;
public
MsgBox()
{
InitializeComponent();
timer
=
new
DispatcherTimer();
timer.Interval
=
TimeSpan.FromSeconds(
1
);
timer.Tick
+=
new
EventHandler(timer_Tick);
this
.Closed
+=
new
EventHandler(MsgBox_Closed);
}
void
MsgBox_Closed(
object
sender, EventArgs e)
{
//
待实现
}
void
timer_Tick(
object
sender, EventArgs e)
{
Title
=
string
.Format(userTitle
+
"
[倒计时自动确定:{0}秒]
"
, defaultTime);
defaultTime
--
;
if
(defaultTime
==
0
)
{
ResetTimer();
if
(autoOKConfirm)
{
OKButton_Click(
null
,
null
);
}
else
{
CancelButton_Click(
null
,
null
);
}
}
}
void
ResetTimer()
{
timer.Stop();
defaultTime
=
3
;
}
public
void
Show(
string
msg,
string
title)
{
Show(msg, title, defaultTime,
null
,
true
, MessageBoxButton.OK);
}
public
void
Show(
string
msg,
string
title,
int
timeSecond, Action
<
bool
>
callBack)
{
Show(msg, title, timeSecond, callBack,
false
, MessageBoxButton.OKCancel);
}
public
void
Show(
string
msg,
string
title,
int
timeSecond, Action
<
bool
>
callBack,
bool
autoOK, MessageBoxButton button)
{
tbMsg.Text
=
msg;
userTitle
=
title;
if
(button
==
MessageBoxButton.OK)
{
OKButton.Content
=
"
确定
"
;
CancelButton.Visibility
=
System.Windows.Visibility.Collapsed;
}
else
{
CancelButton.Visibility
=
System.Windows.Visibility.Visible;
OKButton.Content
=
"
同意
"
;
CancelButton.Content
=
"
拒绝
"
;
}
defaultTime
=
timeSecond;
autoOKConfirm
=
autoOK;
callBackEvent
=
callBack;
Show();
timer.Start();
}
private
void
OKButton_Click(
object
sender, RoutedEventArgs e)
{
this
.DialogResult
=
true
;
}
private
void
CancelButton_Click(
object
sender, RoutedEventArgs e)
{
this
.DialogResult
=
false
;
}
}
三:接下来便是苦力活了,把原来用到传统对框的提示,通通改过来。
这个改的点有点多,留到下节MsgBox使用时细细说了。
最后上一实际应用中的图:
