简体   繁体   中英

WPF UI not responsive in spite of process running in a seperate thread. Why?

Using threading in WPF makes no difference for me. The UI continues to be not responsive.

This is my code:

private void button1_Click_1(object sender, RoutedEventArgs e)
{
    Thread th = new Thread(new ThreadStart(LoadImages));
    th.Start();
}

void LoadImages()
{
    this.Dispatcher.Invoke(DispatcherPriority.Normal, new System.Windows.Forms.MethodInvoker(delegate() {
        IService1 svc = ConnectAndGetObject();
        foreach (byte[] imgbytes in svc.GetImageDateWise(datePicker1.DisplayDate, DateTime.Now, "test"))
        {
            using (MemoryStream mem = new MemoryStream(imgbytes))
            {
                BitmapImage jpgimage = new BitmapImage();
                jpgimage.BeginInit();

                jpgimage.CacheOption = BitmapCacheOption.OnLoad;

                jpgimage.StreamSource = mem;

                jpgimage.EndInit();

                //  PngBitmapDecoder decodejpg = new PngBitmapDecoder(mem, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnDemand);
                Image wpfimage = new Image();
                wpfimage.Source = jpgimage.Clone();

                lbx.Items.Add(wpfimage);
                lbx.UpdateLayout();
                Thread.Sleep(1000);
            }
        }
    }));
}

Updated working code:

      List<MemoryStream> mems = new List<MemoryStream>();
        void LoadImages()
        {

            IService1 svc = ConnectAndGetObject();
            foreach (byte[] imgbytes in svc.GetImageDateWise(GetDate(), DateTime.Now, "test"))
            {
   this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() {
       mems.Add(new MemoryStream(imgbytes));


                    BitmapImage jpgimage = new BitmapImage();
                    jpgimage.BeginInit();

                    jpgimage.CacheOption = BitmapCacheOption.None;

                    jpgimage.StreamSource = mems[mems.Count-1];

                    jpgimage.EndInit();

                  Image wpfimage = new Image();
                  wpfimage.Source = jpgimage.Clone();
                    lbx.Items.Add(wpfimage);
                    lbx.UpdateLayout();
                    Thread.Sleep(500);


   }));
            }
            mems.Clear();


        }

In your code the entire body of LoadImages is Invoked right back to the main thread. So you do not have a multi-threaded solution at all, just a complicated one.

Here is my stab at it:

// untested
void LoadImages()
{
       // this.Dispatcher.Invoke(DispatcherPriority.Normal, 
       //    new System.Windows.Forms.MethodInvoker(delegate() {
    IService1 svc = ConnectAndGetObject();
    foreach (byte[] imgbytes in svc.GetImageDateWise(datePicker1.DisplayDate, DateTime.Now, "test"))
    {   
        using (MemoryStream mem = new MemoryStream(imgbytes))
        {    
            BitmapImage jpgimage = new BitmapImage();
            jpgimage.BeginInit();    
            jpgimage.CacheOption = BitmapCacheOption.OnLoad;    
            jpgimage.StreamSource = mem;    
            jpgimage.EndInit();

            // only invoke the part actually touching the UI    
            this.Dispatcher.Invoke(DispatcherPriority.Normal, 
              new System.Windows.Forms.MethodInvoker(delegate() {
                  Image wpfimage = new Image();
                  wpfimage.Source = jpgimage; //.Clone();
                  lbx.Items.Add(wpfimage);
                  lbx.UpdateLayout();   } ));

            Thread.Sleep(1000);
        }
    }    
   //  }));
}

The problem is that the first thing your non UI thread does is invoke a delegate on the UI thread. The net result is a lot of overhead with no cocurrency at all.

Reorg your code so that only UI tasks are done on the UI thread.

void LoadImages() 
{ 
    IService1 svc = ConnectAndGetObject(); 
...
Dispatcher.Invoke( () => lbx.Items.Add(wpfimage));
....
} 

You're doing too much in your delegate. This might be the cause of your problems, plus the 1 second sleep you have in there.

Just have the code that updates the list box in the delegate:

        lbx.Items.Add(wpfimage);
        lbx.UpdateLayout();

Failing that raise an event in your thread and subscribe to it in your main application and update the UI from that.

You'll need a method along these lines:

    private void ImageAdded_EventHandler(object sender, ImageAddedEventArgs e)
    {
        Action action = () => ImageAdded(e.Image);
        if (Dispatcher.CheckAccess())
        {
            action();
        }
        else
        {
            Dispatcher.Invoke(DispatcherPriority.Normal, action);
        }
    }

Where ImageAddedEventArgs is a class based on EventArgs that has a property of the image data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM