2009-07-01 8 views
3

ich eine WPF-Anwendung, die eine Komponente verwendet, die eine Bitmap meiner Anwendung sendet, sobald sie verfügbar werden, erhalte ich diese Bitmaps in einem Delegierten ich auf diese Komponente zu übergeben.100% reaktions UI in WPF

Ich habe einen neuen Thread für diesen Prozess erstellt und es funktioniert sehr gut, die Bitmaps kommt als MemoryStream und ich erstelle nur das BitmapSource-Objekt aus diesem Stream innerhalb einer Dispatcher.BeginInvoke Methodenaufruf. Nachdem ich das BitmapSource-Objekt habe, füge ich sie zu einem StackPanel hinzu, damit der Benutzer eine Reihe von Bildern sehen kann, die zum Arbeiten verfügbar sind. So weit so gut ...

Das Problem ist, dass diese Bitmaps recht groß sind, wie 3000x2000 + Pixel, und es dauert etwa 50 ~ ms, um diese Bitmaps zu erstellen und der Warteschlange hinzuzufügen, und wenn dieser Code ausgeführt wird Innerhalb des BeginInvoke-Aufrufs blockiert es die Benutzeroberfläche für diese Zeit und verursacht ein sehr störendes Verhalten. (Um dies zu reproduzieren, rufen Sie einfach alle 5 Sekunden Thread.Sleep(50) auf).

Wie kann ich dieses Problem beheben, damit der Benutzer immer ansprechbar ist?

danke!

Antwort

1

Zwei Möglichkeiten kommen schnell in den Sinn.

Die erste ist eine Background zu verwenden, um die Umwandlung des Memory in eine Bitmap auszuführen.

der Zweite ist, dass die Transformation zu einem Thread weg zu führen.

+0

I zweiter Der zweite Vorschlag (Thread). –

4

Es gibt zwei Ideen, die Sie vielleicht prüfen:

einige Besonderheiten können auf Ians Blog gefunden werden, um die Bitmap innerhalb Dispatcher.Invoke nicht schaffen

  1. Sie. Das würde es praktisch im UI-Thread erzeugen und die Dinge verlangsamen. Erstellen Sie es stattdessen im Hintergrundthread, fixieren Sie es und übergeben Sie dann die eingefrorene BitmapSource an den Vordergrundthread.

  2. Abhängig von Ihrer Anwendung braucht das StackPanel nicht die volle 3000x2000 Auflösung? Wenn dies der Fall ist, sollten Sie die Bilder im Hintergrundthread verkleinern, bevor Sie sie einfrieren.

Der folgende Code tut # 1 oben:

Window1.xaml

<Window x:Class="BitmapFrameDemo.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="Window1" Height="300" Width="300"> 
    <Grid> 
     <Image Name="image"/> 
    </Grid> 
</Window> 

Window1.xaml.cs

using System; 
using System.IO; 
using System.Net; 
using System.Threading; 
using System.Windows; 
using System.Windows.Media.Imaging; 
using System.Windows.Threading; 

namespace BitmapFrameDemo { 

    public partial class Window1 : Window { 

     private Thread  thread  = null; 
     private Dispatcher dispatcher = null; 

     private void ThreadMain() { 
      // obtain the image memory stream 
      WebRequest   request  = WebRequest.Create("http://stackoverflow.com/content/img/so/logo.png"); 
      WebResponse   response = request.GetResponse(); 
      Stream    stream  = response.GetResponseStream(); 

      // create a bitmap source while still in the background thread 
      PngBitmapDecoder decoder  = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); 
      BitmapFrame   frame  = decoder.Frames[0]; 

      // freeze the bitmap source, so that we can pass it to the foreground thread 
      BitmapFrame   frozen  = (BitmapFrame) frame.GetAsFrozen(); 
      dispatcher.Invoke(new Action(() => { image.Source = frozen; }), new object[] { }); 
     } 

     public Window1() { 
      InitializeComponent(); 

      dispatcher = Dispatcher.CurrentDispatcher; 
      thread = new Thread(new ThreadStart(this.ThreadMain)); 
      thread.Start(); 
     } 
    } 
} 

alt text http://www.freeimagehosting.net/uploads/66bdbce78a.png

1

wirklich alles, was Sie brauchen zu tun ist das I sAsync für True bei der Bindung an das Bild. Ich würde jedoch empfehlen mit einer PriorityBinding und eine Art von Standard-Bild, das der Benutzer sehen kann, so dass sie wissen, dass es nicht vollständig geladen ist vorzubereiten.

<StackPanel> 
    <Image> 
     <Image.Source> 
      <PriorityBinding> 
       <Binding Path="SlowImage" 
         IsAsync="True" /> 
       <Binding Path="DefaultImage" /> 
      </PriorityBinding> 
     </Image.Source> 
    </Image> 
</StackPanel> 


public partial class Window1 : Window, INotifyPropertyChanged 
{ 

    public Window1() 
    { 
     InitializeComponent(); 

     DefaultImage = new BitmapImage(new Uri("http://stackoverflow.com/content/img/so/logo.png")); 

     SlowImage = new BitmapImage(new Uri("http://serverfault.com/content/img/sf/logo.png")); 

     this.DataContext = this; 
    } 

    private BitmapImage myDefaultImage; 
    public BitmapImage DefaultImage 
    { 
     get { return this.myDefaultImage; } 
     set 
     { 
      this.myDefaultImage = value; 
      this.NotifyPropertyChanged("Image"); 
     } 
    } 

    private BitmapImage mySlowImage; 
    public BitmapImage SlowImage 
    { 
     get 
     { 
      Thread.Sleep(5000); 
      return this.mySlowImage; 
     } 
     set 
     { 
      this.mySlowImage = value; 
      this.NotifyPropertyChanged("SlowImage"); 
     } 
    } 

    #region INotifyPropertyChanged Members 

    private void NotifyPropertyChanged(String info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    #endregion 
} 
+0

Wo haben Sie diese NotifyPropertyChanged gefunden? – Schwertz

+0

NotifyPropertyChanged ist eine Implementierung der INotifyPropertyChanged-Schnittstelle, die die Aktualisierung der Benutzeroberfläche für Sie übernimmt: http://msdn.microsoft.com/en-us/library/ms229614.aspx Ich werde meine Antwort aktualisieren, um die gesamte Fensterklasse einzuschließen. – rmoore

+0

hab es dank! – Schwertz