【问题标题】:Open New Windows from Background Thread on ViewModel从 ViewModel 上的后台线程打开新窗口
【发布时间】:2011-11-18 08:53:25
【问题描述】:

所以我有一个带有启动画面的 WPF 应用程序 (MVVM)。在启动画面启动时,我在 ViewModel 上有一个后台线程,它执行一些与启动相关的活动。在某些情况下,我想打开几个额外的窗口(需要用户输入等......)。我在尝试执行此操作时遇到了许多问题/错误/异常(主要是围绕那个新窗口 - 也是 MVVM - 试图填充其 UI 项目,例如组合框)。所以我把这个问题拉回了一个更简单的形式——“tempWindow”没有任何东西,所以它不会引发 UI 填充错误,但基本上它只是打开它们,一旦后台线程完成,就关闭它们.如果有人能指出我在这里做错的事情的正确方向,我将不胜感激。

ViewModel 的构造函数启动一个后台线程

public SplashScreenViewModel()
{            
   this.LoadingStatusText = "Starting Startup Processing ... ";
   this.VersionNumber = "version " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();

   var threadBackgroundStartUpProcesses = new Thread(new ThreadStart(this.BackgroundStartUpProcesses));
   threadBackgroundStartUpProcesses.SetApartmentState(ApartmentState.STA);
   threadBackgroundStartUpProcesses.IsBackground = true;
   threadBackgroundStartUpProcesses.Start();
}

后台线程应该只打开三个窗口(确实如此),但这些窗口应该保持打开状态(一旦线程完成它们就会消失)。

private void BackgroundStartUpProcesses()
{
    for (int i = 0; i < 3; i++)
    {
        var objTempWindow = new tempWindow();
        objTempWindow.Show();
    }
}

// 编辑:已更新响应,现在当 TempWindow 有一个从 TempWindowViewModel 填充的组合框时会出现错误。

private void BackgroundStartUpProcesses()
{
    for (int i = 0; i < 3; i++)
    {
        var objTempWindow = new tempWindow();
        objTempWindow.Show();
    }

    System.Windows.Threading.Dispatcher.Run();
}

当它打开 TempWindow 时出现异常:“调用线程无法访问此对象,因为不同的线程拥有它。”被抛出,并且似乎是当该窗口上的 ComboBox 试图被填充时。

【问题讨论】:

  • 我最终创建了一个要创建的窗口列表,然后在启动结束时使用该窗口列表引发一个事件并通过该事件将其传递回 WPF SplashScreenView,然后在视图处理该事件,遍历视图列表并从那里打开窗口......不确定这是否是最好的,但它可以工作

标签: wpf multithreading mvvm


【解决方案1】:

我怀疑窗户需要一个所有者,当所有者去世时,窗户也是如此。在主线程上打开窗口会使它们粘在一起。像这样的...

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var threadBackgroundStartUpProcesses = new Thread(new ParameterizedThreadStart(this.BackgroundStartUpProcesses));
        threadBackgroundStartUpProcesses.SetApartmentState(ApartmentState.STA);
        threadBackgroundStartUpProcesses.IsBackground = true;
        threadBackgroundStartUpProcesses.Start(System.Windows.Threading.Dispatcher.CurrentDispatcher);
    }

    private void BackgroundStartUpProcesses(object d)
    {
        System.Windows.Threading.Dispatcher dispatcher = (System.Windows.Threading.Dispatcher) d;
         for (int i = 0; i < 3; i++)
         {
             dispatcher.BeginInvoke((Action)(() =>
             {
                 var objTempWindow = new tempWindow();
                 objTempWindow.Show();

             }));

         }

    }
}

编辑

我刚刚对 WPF 线程进行了一些挖掘,看起来您可以在多个线程上打开窗口,但您需要启动新的调度程序。请参阅本页末尾附近:

http://msdn.microsoft.com/en-us/library/ms741870.aspx

在您的BackgroundStartupProcesses 下,objTempWindow.Show() 添加这一行

System.Windows.Threading.Dispatcher.Run();

【讨论】:

  • 迭代,这似乎保留了最后一个窗口(Dispatcher.Run() 建议),但不是其他两个...
  • 对不起,我的错。你需要把它放在循环之外,在底部。
  • 谢谢...这绝对适用于这种简化的情况,希望当我在“tempWindow”上重新添加 mvvm 时它会继续工作...再次感谢!
【解决方案2】:

您是否在关闭初始屏幕之前设置了MainWindow? WPF 将应用程序的第一个打开窗口设置为MainWindow,除非您更改了App.xamlShutdownMode,否则一旦您关闭初始屏幕,应用程序就会关闭。

【讨论】:

  • 我没有设置 MainWindow,我没有将它设置为 App.xaml 上的任何内容,然后在 Application_Startup 上执行 var objSplashScreenView = new UI.SplashScreen.SplashScreenView(); objSplashScreenView.ShowCentered();
  • 所以你的启动画面变成了主窗口,当你关闭启动画面时,它会关闭整个应用程序。摆弄 App.xaml 中的 ShutdownMode 或尝试设置 MainWindow。
猜你喜欢
  • 2018-06-29
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
  • 2017-08-15
  • 1970-01-01
  • 1970-01-01
  • 2015-07-23
  • 1970-01-01
相关资源
最近更新 更多