WPF请稍候对话框

7
我正在使用C# 4.0开发一个WPF桌面应用程序,需要处理大量的长时间运行的操作(从数据库加载数据、计算模拟、优化路线等)。
当这些长时间运行的操作在后台运行时,我想显示一个“请稍候”对话框。当“请稍候”对话框显示时,应用程序应该被锁定,但仅禁用应用程序窗口并不是一个好主意,因为所有的DataGrid都会失去它们的状态(SelectedItem)。
目前我所做的工作有一些问题: 使用Create工厂方法创建了一个新的WaitXUI。Create方法期望标题文本和要锁定的主机控件的引用。Create方法设置窗口的启动位置、标题文本和要锁定的主机:
WaitXUI wait = WaitXUI.Create("Simulation running...", this);
wait.ShowDialog(new Action(() =>
{
    // long running operation
}));

使用重载的ShowDialog方法,可以显示WaitXUI。ShowDialog重载需要一个Action来包装长时间运行的操作。

在ShowDialog重载中,我只需在其自己的线程中启动Action,然后禁用宿主控件(将Opacity设置为0.5并将IsEnabled设置为false),并调用基类的ShowDialog。

public bool? ShowDialog(Action action) 
    {
    bool? result = true;

    // start a new thread to start the submitted action
    Thread t = new Thread(new ThreadStart(delegate()
    {
        // start the submitted action
        try
        {
            Dispatcher.UnhandledException += Dispatcher_UnhandledException;
            Dispatcher.Invoke(DispatcherPriority.Normal, action);
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            // close the window
            Dispatcher.UnhandledException -= Dispatcher_UnhandledException;
            this.DoClose();
        }
    }));
    t.Start();

    if (t.ThreadState != ThreadState.Stopped)
    {
        result = this.ShowDialog();
    }
    return result;
}

private new bool? ShowDialog() 
{
    DisableHost();
    this.Topmost = true;
    return base.ShowDialog();
}

private void DisableHost()
{
    if (host != null)
    {
        host.Dispatcher.Invoke(new Action(delegate()
        {
            this.Width = host.Width - 20;
            host.Cursor = Cursors.Wait;
            host.IsEnabled = false;
            host.Opacity = 0.5;
        }));
    }
}

这里存在以下问题:
  • 禁用主控件会导致失去状态信息(SelectedItems...)
  • 当线程在WaitXUI显示几毫秒后结束时,WaitXUI有时只显示几毫秒
  • 有时尽管线程仍在运行,但对话框根本不会出现
这些是我目前想到的主要问题。如何改进这个概念,或者可以采用什么其他方法来解决这个问题?
提前感谢!

将每个控件的IsEnabled属性设置为false。或者在窗口中捕获任何输入并将e.handled设置为true。 - paparazzo
这肯定是一个重复的。 - UIlrvnd
https://dev59.com/AnA75IYBdhLWcg3wFE6b - Jimmy
@Blam 但这并没有解决我的任何问题。 - goflo
@Akane 有类似的问题,但都有其他(较低)的要求!将我的问题声明为重复,并不发布解决方案是没有帮助的;-) - goflo
2个回答

11

在开发WPF应用程序时,一些侧面思考总是很有帮助的。你只需使用一个Grid、一个Rectangle、一个bool属性(可能已经存在),以及一个BooleanToVisibilityConverter,就可以轻松满足你的要求,而且你不必禁用任何控件。

这个想法很简单。在你的视图内容前面添加一个白色的Rectangle,将其Opacity属性设置在0.5左右,然后将它的Visibility属性与你的视图模型或代码后面的bool属性进行数据绑定,并插入BooleanToVisibilityConverter即可:

<Grid>
    <Grid>
        <!--Put your main content here-->
    </Grid>
    <Rectangle Fill="White" Opacity="0.7" Visibility="{Binding IsWaiting, 
        Converter={StaticResource BooleanToVisibilityConverter}}" />
    <!--You could add a 'Please Wait' TextBlock here-->
</Grid>

现在如果你想禁用控件,只需将bool属性设置为trueRectangle就会使UI看起来模糊:

IsWaiting = true;

谢谢你的回答。这与 BusyIndicator(由_Eugene P_建议)使用的概念相同,可能是一个可行的解决方案。我不喜欢的唯一一件事是,我必须将上面的代码添加到每个调用 WaitXUI 的 UI 中。而且有很多 UI(>50)使用等待对话框。如果我可以在一个地方(在 WaitXUI 中)完成这个操作就好了! :-/ - goflo
你似乎误解了...如果你将此放入 MainWindow.xaml,并将你的 bool 属性放在主视图模型中,则只需要一个。将 所有 正常的应用程序内容放在其中写着 Put your main content here 的位置。 - Sheridan
好的,你是对的。我只需要在每个窗口中而不是每个UI中使用那段代码。我会尝试一下。谢谢! - goflo
1
不提供BoolToVisibilityConverter的完整解决方案或任何提示,答案将不完整。 - Adam Ri
这是你询问澄清的方式吗,@AdamRi?如果你仔细阅读了我的回答,你就会看到我提到了标准的BooleanToVisibilityConverter,它已经包含在.NET Framework中。因此,你应该明白对神秘的BoolToVisibilityConverter的引用只是一个打字错误,而你应该使用前面提到的BooleanToVisibilityConverter。但还是感谢你的评论。 - Sheridan

1

我认为不需要创建自己的实现,这是多余的。

可以查看已经创建的组件,例如BusyIndicator,以满足类似的需求。这是至关重要和有效的。

来自codeplex的更多信息。


感谢您的回答。BusyIndicator正在使用Sharidan建议的相同概念。那将是一个可能的解决方案。我不喜欢的唯一一件事是,我必须为每个调用WaitXUI的UI添加BusyIndicator的UI代码。而且有很多UI(> 50)使用等待对话框。如果我可以在一个地方(在WaitXUI中)完成这个操作就好了! :-/ - goflo
我不确定我理解你想表达的意思,但是无论如何,busyindicator是可绑定的,你可以将其设置为根视图占位符,然后注入任何子视图(页面/等)。接下来,你可以轻松使用绑定进行控件自定义。 - Eugene P.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接