代码之家  ›  专栏  ›  技术社区  ›  Shaun Bowe

在WPF中显示“等待”屏幕

  •  7
  • Shaun Bowe  · 技术社区  · 16 年前

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      this.Cursor = System.Windows.Input.Cursors.Pen;
      WaitScreen.Visibility = Visibility.Visible;
    
      // Do something long here
      for (Int32 i = 0; i < 100000000; i++)
      {
        String s = i.ToString();
      }
    
      WaitScreen.Visibility = Visibility.Collapsed;
      this.Cursor = System.Windows.Input.Cursors.Arrow; 
    }
    

    5 回复  |  直到 16 年前
        1
  •  17
  •   Ray    16 年前

    单线程操作确实会很痛苦,而且它永远不会像你想的那样工作。在WPF中,窗口最终会变黑,程序将变为“无响应”。

    我建议使用BackgroundWorker来完成长时间运行的任务。

    private void DoWork(object sender, DoWorkEventArgs e)
    {
        //Do the long running process
    }
    
    private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Hide your wait dialog
    }
    
    private void StartWork()
    {
       //Show your wait dialog
       BackgroundWorker worker = new BackgroundWorker();
       worker.DoWork += DoWork;
       worker.RunWorkerCompleted += WorkerCompleted;
       worker.RunWorkerAsync();
    }
    

    这确实是最简单的方法,而不是试图单线程完成。

        2
  •  16
  •   Shaun Bowe    16 年前

    this thread .

    public static void ForceUIToUpdate()
    {
      DispatcherFrame frame = new DispatcherFrame();
    
      Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
      {
        frame.Continue = false;
        return null;
      }), null);
    
      Dispatcher.PushFrame(frame);
    }
    

        3
  •  4
  •   Community Mohan Dere    8 年前

    执行一段代码,以与执行实际任务的方法相同的方式同步显示等待消息。只需将这一行放在冗长的过程之前:

    Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => { /* Your code to display a waiting message */ }));
    

    .

    注: here .

    选项#2

    做这件事 桌面应用程序 Application.DoEvents 方法。WPF提供了两种方法来实现类似的结果:

    选项#2.1 DispatcherFrame class .

    检查一个有点笨重的例子 MSDN :

    [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public void DoEvents()
    {
        DispatcherFrame frame = new DispatcherFrame();
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
        Dispatcher.PushFrame(frame);
    }
    
    public object ExitFrame(object f)
    {
        ((DispatcherFrame)f).Continue = false;
        return null;
    }
    

    调用空操作

    Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, (Action)(() => { }));
    

    请参阅讨论,哪一个(2.1或2.2)更好 here

    选项#3

    当您显示的不是简单的等待消息,而是动画时,它会派上用场。在等待另一个长时间渲染操作完成的同时渲染加载动画是一个问题。基本上,我们需要两个渲染线程。你不能在一个窗口中有多个渲染线程,但你可以将加载动画放在一个有自己渲染线程的新窗口中,使其看起来不像是一个单独的窗口。

    下载 this github “,但我在网上找不到了),或者看看下面的主要观点:

    public partial class LoadingOverlayWindow : Window
    {
        /// <summary>
        ///     Launches a loading window in its own UI thread and positions it over <c>overlayedElement</c>.
        /// </summary>
        /// <param name="overlayedElement"> An element for overlaying by the waiting form/message </param>
        /// <returns> A reference to the created window </returns>
        public static LoadingOverlayWindow CreateAsync(FrameworkElement overlayedElement)
        {
            // Get the coordinates where the loading overlay should be shown
            var locationFromScreen = overlayedElement.PointToScreen(new Point(0, 0));
    
            // Launch window in its own thread with a specific size and position
            var windowThread = new Thread(() =>
                {
                    var window = new LoadingOverlayWindow
                        {
                            Left = locationFromScreen.X,
                            Top = locationFromScreen.Y,
                            Width = overlayedElement.ActualWidth,
                            Height = overlayedElement.ActualHeight
                        };
                    window.Show();
                    window.Closed += window.OnWindowClosed;
                    Dispatcher.Run();
                });
            windowThread.SetApartmentState(ApartmentState.STA);
            windowThread.Start();
    
            // Wait until the new thread has created the window
            while (windowLauncher.Window == null) {}
    
            // The window has been created, so return a reference to it
            return windowLauncher.Window;
        }
    
        public LoadingOverlayWindow()
        {
            InitializeComponent();
        }
    
        private void OnWindowClosed(object sender, EventArgs args)
        {
            Dispatcher.InvokeShutdown();
        }
    }
    
        4
  •  3
  •   Daniel Earwicker    16 年前

    另一种选择是将长时间运行的例程编写为返回的函数 IEnumerable<double>

    yield return 30;
    

    例如,这意味着已经完成了30%的路程。然后,您可以使用WPF计时器在“后台”将其作为协作协程执行。

    It's described in some detail here, with sample code.