C# 关闭由其他线程拥有的窗口
我不熟悉穿线。我在我的WPF应用程序中使用后台线程与DB对话并进行消息通信 其中一个视图模型应打开一个单独的窗口。由于这应该作为UI线程运行,因此我正在执行以下操作:C# 关闭由其他线程拥有的窗口,c#,.net,wpf,multithreading,mvvm-light,C#,.net,Wpf,Multithreading,Mvvm Light,我不熟悉穿线。我在我的WPF应用程序中使用后台线程与DB对话并进行消息通信 其中一个视图模型应打开一个单独的窗口。由于这应该作为UI线程运行,因此我正在执行以下操作: private void OnSelection(SelectionType obj) { Thread thread = new Thread(ShowRegionWindow); thread.SetApartmentState(ApartmentState.STA);
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()));
...
}
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();
}
}
}
然后在某个地方提起这个事件:
private void OnClick(object sender, RoutedEventArgs args)
{
CloseRegionWindows();
}
在再次阅读了你的一些评论之后,我想我对这个场景有了更好的理解。这是你需要做的 首先,确保其中一个ViewModel引用了需要打开和关闭窗口的模型。实现这一点的一种方法是构造函数依赖项注入
public ViewModel(Model model) // or IModel
{
...
接下来,您需要捕获该ViewModel中的UI调度程序。最好的地方可能也是ViewModel构造函数
private Dispatcher dispatcher;
public ViewModel(Model model)
{
dispatcher = Dispatcher.CurrentDispatcher;
...
public ViewModel(Model model)
{
dispatcher = Dispatcher.CurrentDispatcher;
model.OpenWindow += OnWindowOpen;
model.CloseWindow += OnWindowClose;
...
}
现在在模型中创建两个事件;一个打开,一个关闭窗户
class Model
{
internal event Action OpenWindow = delegate { };
internal event Action CloseWindow = delegate { };
...
并在ViewModel构造函数中订阅它们
private Dispatcher dispatcher;
public ViewModel(Model model)
{
dispatcher = Dispatcher.CurrentDispatcher;
...
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事件,就像引发任何事件一样。您的模型可能如下所示:
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()));
...
}
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();
}
}
}
为什么它必须作为UI线程运行?如果只是创建一个昂贵的对象,您可以在背景踏板上创建该对象,然后将其绑定到主踏板上(在主踏板上易于关闭),而不是创建对象。它正在创建另一个WPF窗口您的问题陈述是“与DB对话的后台线程”。如果您没有创建一个与DB对话的对象,那么您对DB做了什么?我的问题是没有使用线程与DB对话。我已经通过后台线程完成了。现在,在一些操作中,后台线程需要打开一个新的WPF窗口并关闭。我该怎么做呢。对不起,如果我的问题不清楚为什么后台线程需要打开窗口?您可以在主线程上有一个回调,从后台线程检索结果以打开新窗口。什么操作需要新窗口?你能重构以获得主服务器的信息并启动另一个后台吗?这对我不起作用。代码运行时窗口被冻结。并在代码完成后解冻。主线程正在冻结新线程。该线程还需要
thread.SetApartmentState(ApartmentState.STA)代码>否则它会崩溃。为了防止冻结窗口,您需要添加System.Windows.Threading.Dispatcher.Run()
但是如果您添加了,您就不能再停止线程了。