2011-12-19 36 views
1

我是线程新手。我在我的WPF应用程序中使用后台线程来与数据库和消息通信进行交谈。关闭由不同线程拥有的窗口

其中一个视图模型应该打开一个单独的窗口。由于这应该作为UI线程运行,我这样做:

private void OnSelection(SelectionType obj) 
    { 
     Thread thread = new Thread(ShowRegionWindow); 
     thread.SetApartmentState(ApartmentState.STA); 
     thread.Start(); 
    } 
    private void ShowRegionWindow() 
    { 
     var rWindow = new RegionWindow(); 
     rWindow .Show(); 
     rWindow .Closed += (s, e) => System.Windows.Threading.Dispatcher.ExitAllFrames(); 
     System.Windows.Threading.Dispatcher.Run(); 
    } 

现在我需要关闭此窗口的另一条消息。我怎么做?

+0

为什么它必须作为UI线程运行?如果只是创建一个昂贵的对象,您可以在背景上创建对象,然后将其绑定在主要对象(易于关闭的位置)。 – Paparazzi 2011-12-19 22:37:22

+0

它不创建对象。它正在创建另一个WPF窗口 – katie77 2011-12-20 00:14:09

+0

您的问题陈述是“后台线程与数据库交谈”。如果你没有创建一个对象与数据库交谈,那么你在做什么与数据库? – Paparazzi 2011-12-20 02:34:44

回答

0

在我进一步讨论之前,你说过你是线程新手,我想强调一下,应用程序在不同线程上打开窗口可能没有什么好的理由。你使用MVVM是好事,但你可能做得不对。理想情况下,所有视图和视图模型都将位于主UI线程上。在与视图模型交互之前,模型图层中的任何工作线程都需要调用UI调度器。例如,您可能在工作线程上有更新事件调用视图模型上的处理程序来更新UI。应该在调用该事件之前或之后立即调用UI调度程序。 (要清楚不过,模型不应该知道的视图模型)。

事实上,你似乎是建立在一个UI事件处理程序,这意味着你应该只是做一个新的窗口:

private void OnSelection(SelectionType obj) 
{ 
    var rWindow = new RegionWindow(); 
    rWindow.Show(); 
} 

但是,也许你有一个完全合法的理由来按照你的方式来完成它。如果是这样,你可以从调用线程关闭新窗口的一种方式是传入一个事件。你可以做这样的事情:

private event Action CloseRegionWindows = delegate { }; // won't have to check for null 

private void OnSelection(SelectionType obj) 
{ 
    Thread thread = new Thread(() => ShowRegionWindow(ref CloseRegionWindows)); 
    ... 
} 

private void ShowRegionWindow(ref Action CloseRegionWindows) 
{ 
    var rWindow = new RegionWindow(); 
    rWindow.Show(); 
    CloseRegionWindows +=() => rWindow.Dispatcher.BeginInvoke(new ThreadStart(() => rWindow.Close())); 
    ... 
} 

然后某处提高该事件:

private void OnClick(object sender, RoutedEventArgs args) 
{ 
    CloseRegionWindows(); 
} 
+0

这不适合我。代码运行时窗口被冻结。并在代码完成后解冻。主线程冻结新线程。该线程还需要'thread.SetApartmentState(ApartmentState.STA);否则它会崩溃。为了防止冻结窗口,你需要添加'System.Windows.Threading.Dispatcher.Run();'但是如果你添加了你不能停止线程了。 – Franck 2013-10-29 14:32:25

0

又读了一些您的意见后,我想我有一个更好的了解情况的。这是你需要做的。

首先,确保您的某个ViewModel具有对需要打开和关闭窗口的Model的引用。一种实现方法是构造函数依赖注入。

public ViewModel(Model model) // or IModel 
{ 
    ... 

接下来,您需要捕获该视图模型中的UI调度器。这个最好的地方也许是ViewModel构造函数。

private Dispatcher dispatcher; 

public ViewModel(Model model) 
{ 
    dispatcher = Dispatcher.CurrentDispatcher; 
    ... 

现在在您的模型中创建两个事件;一个打开,一个关闭窗口。

class Model 
{ 
    internal event Action OpenWindow = delegate { }; 
    internal event Action CloseWindow = delegate { }; 
    ... 

并在您的ViewModel构造函数中订阅它们。

public ViewModel(Model model) 
{ 
    dispatcher = Dispatcher.CurrentDispatcher; 
    model.OpenWindow += OnWindowOpen; 
    model.CloseWindow += OnWindowClose; 
    ... 
} 

现在用ViewModel类中的UI Dispatcher打开和关闭窗口;

private Window window; 

private void OnWindowOpen() 
{ 
    // still on background thread here 

    dispatcher.BeginInvoke(new ThreadStart(() => 
    { 
     // now we're on the UI thread 

     window = new Window(); 
     window.Show(); 
    } 
} 

private void OnWindowClose() 
{ 
    dispatcher.BeginInvoke(new ThreadStart(() => 
    { 
     window.Close(); 
    } 
} 

最后,从您的模型中的后台线程中引发OpenWindow和CloseWindow事件,就像引发任何事件一样。你的模型可能看起来像这样:

class Model 
{ 
    private Thread worker; 

    internal event Action OpenWindow = delegate { }; 
    internal event Action CloseWindow = delegate { }; 

    public Model() 
    { 
     worker = new Thread(Work); 
     worker.Start(); 
    } 

    private void Work() 
    { 
     while(true) 
     { 
      if (/*whatever*/) OpenWindow(); 
      else if (/*whatever*/) CloseWindow(); 
     } 
    } 
}