wpf 窗口刷新首先起作用,然后停止

发布于 2024-08-26 04:58:33 字数 2608 浏览 3 评论 0原文

我有一个例程,可以获取目录中所有图像的列表,然后对所有图像运行 MD5 摘要。由于这需要一段时间才能完成,因此我弹出一个带有进度条的窗口。进度条由我传递给长时间运行的例程的 lambda 更新。

第一个问题是进度窗口从未更新(我猜这在 WPF 中很正常)。由于 WPF 缺少 Refresh() 命令,我通过调用 Dispatcher.Invoke() 修复了此问题。现在进度条更新了一段时间,然后窗口就停止更新了。长时间运行的工作最终会完成,窗口会恢复正常。

我已经尝试过BackgroundWorker,但很快就因与长时间运行的进程触发的事件相关的线程问题而感到沮丧。因此,如果这确实是最好的解决方案,而我只需要更好地学习范例,请这么说。

但我对这里的方法感到非常满意,除了它会在一段时间后停止更新(例如,在包含 1000 个文件的文件夹中,它可能会更新 50-100 个文件,然后“挂起”) 。在此活动期间,UI 不需要响应,除了报告进度之外。

无论如何,这是代码。首先是进度窗口本身:

public partial class ProgressWindow : Window
{
    public ProgressWindow(string title, string supertext, string subtext)
    {
        InitializeComponent();
        this.Title = title;
        this.SuperText.Text = supertext;
        this.SubText.Text = subtext;
    }

    internal void UpdateProgress(int count, int total)
    {
        this.ProgressBar.Maximum = Convert.ToDouble(total);
        this.ProgressBar.Value = Convert.ToDouble(count);
        this.SubText.Text = String.Format("{0} of {1} finished", count, total);
        this.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }

    private static Action EmptyDelegate = delegate() { };
}


<Window x:Class="Pixort.ProgressWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Pixort Progress" Height="128" Width="256" WindowStartupLocation="CenterOwner" WindowStyle="SingleBorderWindow" ResizeMode="NoResize">
    <DockPanel>
        <TextBlock DockPanel.Dock="Top" x:Name="SuperText" TextAlignment="Left" Padding="6"></TextBlock>
        <TextBlock DockPanel.Dock="Bottom" x:Name="SubText" TextAlignment="Right" Padding="6"></TextBlock>
        <ProgressBar x:Name="ProgressBar" Height="24" Margin="6"/>
    </DockPanel>
</Window>

长时间运行的方法(在 Gallery.cs 中):

public void ImportFolder(string folderPath, Action<int, int> progressUpdate)
{
    string[] files = this.FileIO.GetFiles(folderPath);

    for (int i = 0; i < files.Length; i++)
    {
        // do stuff with the file
        if (null != progressUpdate)
        {
            progressUpdate.Invoke(i + 1, files.Length);
        }
    }
}

其调用方式如下:

 ProgressWindow progress = new ProgressWindow("Import Folder Progress", String.Format("Importing {0}", folder), String.Empty);
 progress.Show();
 this.Gallery.ImportFolder(folder, ((c, t) => progress.UpdateProgress(c, t)));
 progress.Close();

I've got a routine that grabs a list of all images in a directory, then runs an MD5 digest on all of them. Since this takes a while to do, I pop up a window with a progress bar. The progress bar is updated by a lambda that I pass in to the long-running routine.

The first problem was that the progress window was never updated (which is normal in WPF I guess). Since WPF lacks a Refresh() command I fixed this with a call to Dispatcher.Invoke(). Now the progress bar is updated for a while, then the window stops being updated. The long-running work does eventually finish and the windows go back to normal.

I have already tried a BackgroundWorker and quickly became frustrated by a threading issue related to an event triggered by the long-running process. So if that's really the best solution and I just need to learn the paradigm better, please say so.

But I'd be really much happier with the approach I've got here, except that it stops updating after a bit (for example, in a folder with 1000 files, it might update for 50-100 files, then "hang"). The UI does not need to be responsive during this activity, except to report on progress.

Anyway, here's the code. First the progress window itself:

public partial class ProgressWindow : Window
{
    public ProgressWindow(string title, string supertext, string subtext)
    {
        InitializeComponent();
        this.Title = title;
        this.SuperText.Text = supertext;
        this.SubText.Text = subtext;
    }

    internal void UpdateProgress(int count, int total)
    {
        this.ProgressBar.Maximum = Convert.ToDouble(total);
        this.ProgressBar.Value = Convert.ToDouble(count);
        this.SubText.Text = String.Format("{0} of {1} finished", count, total);
        this.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }

    private static Action EmptyDelegate = delegate() { };
}


<Window x:Class="Pixort.ProgressWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Pixort Progress" Height="128" Width="256" WindowStartupLocation="CenterOwner" WindowStyle="SingleBorderWindow" ResizeMode="NoResize">
    <DockPanel>
        <TextBlock DockPanel.Dock="Top" x:Name="SuperText" TextAlignment="Left" Padding="6"></TextBlock>
        <TextBlock DockPanel.Dock="Bottom" x:Name="SubText" TextAlignment="Right" Padding="6"></TextBlock>
        <ProgressBar x:Name="ProgressBar" Height="24" Margin="6"/>
    </DockPanel>
</Window>

The long running method (in Gallery.cs):

public void ImportFolder(string folderPath, Action<int, int> progressUpdate)
{
    string[] files = this.FileIO.GetFiles(folderPath);

    for (int i = 0; i < files.Length; i++)
    {
        // do stuff with the file
        if (null != progressUpdate)
        {
            progressUpdate.Invoke(i + 1, files.Length);
        }
    }
}

Which is called thusly:

 ProgressWindow progress = new ProgressWindow("Import Folder Progress", String.Format("Importing {0}", folder), String.Empty);
 progress.Show();
 this.Gallery.ImportFolder(folder, ((c, t) => progress.UpdateProgress(c, t)));
 progress.Close();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

我恋#小黄人 2024-09-02 04:58:33

Mate 使用 DataBinding 进行简单的 WPF 编程。
请参阅 MVVM 设计模式的解释相同。

将进度条值属性与 DataContext 类中定义的某些源属性绑定。
并更新调度程序调用方法中的源属性。

WPF 引擎将负责休息。

您当前编写的代码没有任何绑定...

Mate do simple WPF programming by using DataBinding.
Refer MVVM design pattern explaining same.

Bind progressbar value property with some source property defined in DataContext class.
And update source property in dispatcher invoked method.

WPF engine will take care of rest.

You have currently written code wothout any binding...

夏雨凉 2024-09-02 04:58:33

如果我理解正确的话,您现在在主线程上完成所有工作。这意味着您从正常的消息泵(调度程序)中占用了(太多)时间。

简短的修复将类似于 WinForm 的 Application.DoEvents() 但我不知道是否有 WPF 等效项。

更好的解决方案是使用线程,然后后台工作者是最简单的方法。也许可以扩展该事件问题。

If I understand correctly you do all your work on the Main thread now. That means you are taking away (too much) time from the normal Messagepump (Dispatcher).

The short fix would be an analog to WinForm's Application.DoEvents() but I don't know if there is a WPF equivalent.

The better solution would be to use a Thread, and then the Backgroundworker is the easiest approach. Maybe expand on that event issue.

北凤男飞 2024-09-02 04:58:33

事实证明,这与 UpdateProgress 中的 DispatcherPriority 有关。将 DispatcherPriority.Render 更改为较低的值,在我的例子中 DispatcherPriority.Background 就成功了。

亨克的回答让我相信,如果消息泵不堪重负,它需要帮助来弄清楚何时该做什么。改变优先级似乎只是一张门票。

Turns out this is related to the DispatcherPriority in UpdateProgress. Changing DispatcherPriority.Render to something lower, in my case DispatcherPriority.Background did the trick.

Henk's answer lead me to believe that if the message pump is overwhelmed, that it needed help sorting out what to do when. Changing priority seems to be just the ticket.

梦里寻她 2024-09-02 04:58:33

修改代码以执行预期操作。
[注意:Xaml代码未修改]


 public partial class ProgressWindow : Window
   {
      public ProgressWindow(string title, string supertext, string subtext)
      {
         InitializeComponent();
         EmptyDelegate = RaiseOnDispatcher;
        this.Title = title; 
        this.SuperText.Text = supertext; 
        this.SubText.Text = subtext; 
      }


    internal void UpdateProgress(int count, int total) 
    {
       this.Dispatcher.Invoke(EmptyDelegate,DispatcherPriority.Render,new object[]{count,total}); 
    }

    private static Action<int, int> EmptyDelegate = null;

    private void RaiseOnDispatcher(int count, int total)
    {
       this.ProgressBar.Maximum = Convert.ToDouble(total);
       this.ProgressBar.Value = Convert.ToDouble(count);
       this.SubText.Text = String.Format("{0} of {1} finished", count, total);
    }
   }


   public class Gallery
   {
      static Action<int, int> ActionDelegate=null;
      public static void ImportFolder(string folderPath, Action<int, int> progressUpdate)
      {
         ActionDelegate = progressUpdate;
         BackgroundWorker backgroundWorker = new BackgroundWorker();
         backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
         backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_WorkCompleted);
         backgroundWorker.RunWorkerAsync(folderPath);
         backgroundWorker = null;
      }

     static void worker_DoWork(object sender, DoWorkEventArgs e)
      {
         string folderPath = e.Argument.ToString();
         DirectoryInfo dir = new DirectoryInfo(folderPath);
         FileInfo[] files = dir.GetFiles();

         for (int i = 0; i < files.Length; i++)
         {
            // do stuff with the file 
            Thread.Sleep(1000);// remove in actual implementation
            if (null != ActionDelegate)
            {
               ActionDelegate.Invoke(i + 1, files.Length);
            }
         }
      }
      static void worker_WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
         //do after work complete
      }

      public static void Operate()
      {
         string folder = "folderpath";
         ProgressWindow progress = new ProgressWindow("Import Folder Progress", String.Format("Importing {0}", folder), String.Empty);
         progress.Show();
         Gallery.ImportFolder(folder, ((c, t) => progress.UpdateProgress(c, t)));
         progress.Close();
      }


   }

Modified code for performing expected operation.
[Note : Xaml code is not modified]


 public partial class ProgressWindow : Window
   {
      public ProgressWindow(string title, string supertext, string subtext)
      {
         InitializeComponent();
         EmptyDelegate = RaiseOnDispatcher;
        this.Title = title; 
        this.SuperText.Text = supertext; 
        this.SubText.Text = subtext; 
      }


    internal void UpdateProgress(int count, int total) 
    {
       this.Dispatcher.Invoke(EmptyDelegate,DispatcherPriority.Render,new object[]{count,total}); 
    }

    private static Action<int, int> EmptyDelegate = null;

    private void RaiseOnDispatcher(int count, int total)
    {
       this.ProgressBar.Maximum = Convert.ToDouble(total);
       this.ProgressBar.Value = Convert.ToDouble(count);
       this.SubText.Text = String.Format("{0} of {1} finished", count, total);
    }
   }


   public class Gallery
   {
      static Action<int, int> ActionDelegate=null;
      public static void ImportFolder(string folderPath, Action<int, int> progressUpdate)
      {
         ActionDelegate = progressUpdate;
         BackgroundWorker backgroundWorker = new BackgroundWorker();
         backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
         backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_WorkCompleted);
         backgroundWorker.RunWorkerAsync(folderPath);
         backgroundWorker = null;
      }

     static void worker_DoWork(object sender, DoWorkEventArgs e)
      {
         string folderPath = e.Argument.ToString();
         DirectoryInfo dir = new DirectoryInfo(folderPath);
         FileInfo[] files = dir.GetFiles();

         for (int i = 0; i < files.Length; i++)
         {
            // do stuff with the file 
            Thread.Sleep(1000);// remove in actual implementation
            if (null != ActionDelegate)
            {
               ActionDelegate.Invoke(i + 1, files.Length);
            }
         }
      }
      static void worker_WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
         //do after work complete
      }

      public static void Operate()
      {
         string folder = "folderpath";
         ProgressWindow progress = new ProgressWindow("Import Folder Progress", String.Format("Importing {0}", folder), String.Empty);
         progress.Show();
         Gallery.ImportFolder(folder, ((c, t) => progress.UpdateProgress(c, t)));
         progress.Close();
      }


   }

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文