关闭由其他线程拥有的窗口

本文关键字:拥有 窗口 线程 其他 | 更新日期: 2023-09-27 18:18:32

我是新手。我在我的WPF应用程序中使用后台线程与DB和消息通信。

其中一个视图模型应该打开一个单独的窗口。因为这应该作为一个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();
    }

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

关闭由其他线程拥有的窗口

在我进一步讨论之前,您说您是线程的新手,我想强调您的应用程序在不同线程上打开窗口可能没有很好的理由。使用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();
}

再次阅读了您的一些评论后,我想我对这个场景有了更好的理解。这是你需要做的。

首先,确保其中一个ViewModels有对需要打开和关闭窗口的Model的引用。一种方法是构造函数依赖注入。

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

接下来,您需要在该ViewModel中捕获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();
        }
    }
}