2010-04-27 66 views
3

也许我错误地记住了Winforms的工作方式,或者我正在过度复杂化,但这是我的问题。WPF背景线程调用

我有一个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. 线程1会打码,并调用ShowDialog的登录表单上

  2. 线程2也将打码,并会尽快致电登录线程1已经调用ShowDia日志,因为调用ShowDialog的畅通线程1(我相信的,因为方式的WPF消息泵作品)

...最终效果是,我有弹出用户同时多个登录表单。

我想要的是让用户重新登录到应用程序的同步方式......我在这里错过了什么?

在此先感谢。

回答

1

很抱歉的延迟跟进。

我基本上通过实施的DoEvents为WPF几天前固定的阻塞问题在UI线程: 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); 
     } 
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 :-)

+0

给Paul的答案增加一点细节,我相信他说你应该锁定securityContext。这样,一旦一个线程访问并修改了securityContext(大概是登录),那么其他跟随的线程将不会弹出登录对话框,因为securityContext在访问时已经被设置。 – Dave 2010-04-27 04:06:38

+0

好点。我应该提到我尝试过....请考虑以下事项:1.后台线程进入并调用Application Dispatcher线程,同时锁定securityContextLock对象。 2.用户单击以最大化应用程序,并且真正的UI线程进入并尝试访问securityContext ...但它被锁定....所以整个UI线程冻结...甚至认为步骤1中的人管理为了显示登录表单,UI线程被冻结,并且用户不能与登录表单交互。所以我们陷入僵局。 – Jeff 2010-04-27 04:17:57

+0

我还应该提到忽略而不是阻塞不是一个选项,因为任何要求securityContext的线程都会立即需要它来创建WCF通道凭证。 – Jeff 2010-04-27 04:32:42