【问题标题】:ShowDialog() while doing some actionsShowDialog() 同时做一些动作
【发布时间】:2017-08-28 08:48:41
【问题描述】:

我正在创建一个窗口以在我的 MainWindow 上显示当前正在执行的任务。

我想做的是这样的:

Window progressDialog = new ProgressDialog();
using(progressDialog .ShowDialog())
{
    //Perform some tasks and Update UI on progressDialog

} // Close/Dispose the progressdialog by itself

显然ShowDialog() 阻止了执行,所以我不能做某事。在 MainWindow 上,而另一个是 shwon。

我明确需要的东西:

  1. 将该/任何Dialog 显示为真正的对话框(TopMost 不是选项
    • 我想阻止用户做某事。加载时
  2. using 会非常好,因为它易于使用并且可以处理长时间运行的任务

编辑:

关于我的想法的详细信息:

  1. 用户点击“刷新”
  2. 已创建新的“状态/进度-更新”
  3. 将 StatusUpdate 传递给 ProgressWindow(orange)(如果可能,我们从 using 获取该窗口)
  4. 如果有 ProgressWindow 的活动实例,则将 StatusUpdate 添加到其集合中 - 否则显示新窗口
  5. 在 EndOfUsing CloseWindow() 结束时

例如“刷新”可以包含以下操作:

  1. 连接数据库
  2. 查询数据
  3. 显示数据

我想在我的进度窗口中为每个项目添加一个条目。

这可能吗? - 我对替代品持开放态度:P

【问题讨论】:

  • WPF modal progress window的可能重复
  • 为什么在调用 ShowDialog 方法之前不启动任务?
  • 假设我的进度窗口包含一个 ListView,显示每个任务的进度条。 1 完成后,我想开始下一个,更新窗口并将其添加到我的 ListView。 ShowDialog() 将阻止我开始另一个任务
  • 这不是重复的。 @Felix D. 专门询问是否可以使用using 模式在 UI 线程上进行计算(与链接的问题不同,它要求对进度窗口提供一般指导)

标签: c# wpf multithreading window


【解决方案1】:

假设我的进度窗口包含一个ListView,显示每个任务的进度条。 1 完成后,我想开始下一个,更新窗口并将其添加到我的ListView。 ShowDialog() 将阻止我开始另一个任务。

如果您在调用阻塞 ShowDialog() 方法之前启动任务,则不会。下面是一个基本的例子。

XAML:

<ListBox x:Name="lb" />
<Button Content="Show Dialog" Click="Button_Click_1" />

代码:

public MainWindow()
{
    InitializeComponent();

    Task.Run(() =>
    {
        while (true)
        {
            //simulate some long-running work...
            System.Threading.Thread.Sleep(1500);
            Dispatcher.BeginInvoke(new Action(() => lb.Items.Add("task x...")));
        }
    });
}

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    Window dialog = new Window();
    dialog.ShowDialog();
}

在显示对话框时,任务将继续更新ListBox

【讨论】:

  • 现在似乎是一个完全不同的问题(更广泛!)。我真的不明白你的问题。在我的示例中打开对话框时,窗口会按预期更新。你还想要什么?
【解决方案2】:

我已经在生产应用程序中完成了这项工作。这可以通过以下方式完成:

  1. 创建您自己的IDisposable 进度窗口包装器
  2. 在包装器的构造函数中启动一个新的STA 线程,然后从该线程创建并显示(使用Show(),而不是ShowDialog())您的进度窗口,其中Topmost 属性设置为true。记得在Show-ing 你的窗口之后在这个线程上调用Dispatcher.Run()
  3. 通过Dispatcher.BeginInvoke 调用更新进度窗口中的所有数据
  4. 在包装器的Dispose 方法中,关闭窗口并关闭线程。

编辑:可以使用Win32 api EnableWindow 来禁用主窗口:

public static class WindowHelper
{
    [DllImport("user32.dll")]
    static extern bool EnableWindow(IntPtr hWnd, bool bEnable);


    public static void DisableWindow(Window window)
    {
        EnableWindow(new WindowInteropHelper(window).EnsureHandle(), false);

    }

    public static void EnableWindow(Window window)
    {
        EnableWindow(new WindowInteropHelper(window).EnsureHandle(), true);
    }
}

编辑:在另一个线程上运行窗口的代码

var thread = new Thread(() =>
{
    window_ = new ProgressWindow();
    window_.Show();

    window_.Closed += (sender, args) =>
    {
        window_.Dispatcher.InvokeShutdown();
    };

    Dispatcher.Run();

});

thread.Name = "ProgressWindowThread";
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

可以使用Dispatcher.BeginInvokewindow.Close() 完成关闭。

【讨论】:

  • 这不会阻止用户与实际窗口进行交互。因为它不是“真正的”对话框:/
  • 进度窗口应该只有最少的交互选项(主要是取消按钮)。
  • 这与与该窗口的交互无关。我不希望用户在 MAINWINDOW 上做任何事情。就像在刷新时再次按“刷新” => TopMost 无法处理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多