也许我错误地记得Winforms是如何运作的,或者我因此而过于复杂,但这是我的问题。
我有一个WPF客户端应用程序,它通过WCF与服务器通信。当前用户可以“注销”WPF客户端,该客户端关闭所有打开的屏幕,仅保留导航窗格,并最小化程序窗口。当用户重新最大化程序窗口时,系统会提示他们登录。简单。
但有时事情发生在后台线程上 - 比如客户端每5分钟尝试进行一次刷新一些缓存数据的WCF调用。如果用户在5分钟计时器触发时退出怎么办?那么,应该提示用户重新登录...这当然必须在UI线程上发生。
private static ISecurityContext securityContext;
public static ISecurityContext SecurityContext
{
get
{
if (securityContext == null)
{
// Login method shows a window and prompts the user to log in
Application.Current.Dispatcher.Invoke((Action)Login);
}
return securityContext;
}
}
private static void Login()
{
if (securityContext == null) { \
/* show login window and set securityContext */
var w = new LoginWindow();
w.ShowDialog();
securityContext = w.GetSecurityContext();
}
}
到目前为止这么好,对吗?但是当多个线程遇到这个代码点时会发生什么?
好吧,我的第一个直觉是,因为我在Application.Current.Dispatcher上进行同步,所以我应该没问题,无论哪个线程首先命中,都要负责显示登录表单并让用户登录...
不是这样......
线程1将点击代码并在登录表单上调用ShowDialog
线程2也会点击代码并在线程1调用ShowDialog后立即调用Login,因为调用ShowDialog解锁线程1(我相信因为WPF消息泵的工作方式)
< / LI> 醇>...最终效果是我有多个登录表单一次弹出给用户。
我想要的是一种让用户重新登录应用程序的同步方式......我在这里缺少什么?
提前致谢。
答案 0 :(得分:1)
也许有点锁定?
您可以监控条目,也可以忽略(不阻止)其他轮询操作。使用单个入口点仅显示一次登录表单并等待...
http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx
此外,请考虑缓存用户凭据,而不是重新提示他们,例如SecureString的:
http://msdn.microsoft.com/en-us/library/system.security.securestring.aspx
PK: - )
答案 1 :(得分:1)
对于延迟的后续行动感到抱歉。
我几天前在UI线程上通过基本实现WPF的DoEvents修复了阻塞问题: http://khason.net/blog/how-to-doevents-in-wpf/
所以现在,许多线程,后台和UI都可以调用UI线程,如果窗口已经显示,将“模拟”ShowDialog的行为,但不会阻塞,也不会显示第二个登录窗口...希望对任何人都有意义。
void ShowLoginWindow(Window window)
{
if (window != null )
{
if (window.Visibility != Visibility.Visible)
{
try
{
result = window.ShowDialog();
}
catch (Exception ex)
{
}
}
else
{
// don't block the UI thread, but wait till the dialog window returns
while(window.Visibilit y== Visibility.Visible)
{
DoEvents();
}
return window.DialogResult;
}
}
return result;
}
void DoEvents()
{
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg)
{
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue = false;
}, f);
Dispatcher.PushFrame(f);
}