Laden...

Aktualisierung einer Grafik in WPF

Erstellt von ezekiel vor 10 Jahren Letzter Beitrag vor 10 Jahren 945 Views
E
ezekiel Themenstarter:in
2 Beiträge seit 2012
vor 10 Jahren
Aktualisierung einer Grafik in WPF

Hallo,

mein erster Post hier, und gleich mit einem Problem. Habe erst vor kurzem angefangen mit WPF zu arbeiten aber es klemmt noch an vielen Stellen. Akut ist folgendes Problem:

Ich habe ein Tab, indem ich eine animierte Grafik über eine ObservableCollection (shapes) anzeigen und aktualisiert haben möchte.

(MainWindow.xaml)


[...]
<!--                              -->
<!--     T A B     G R A P H      -->
<!--                              -->
<!--                              -->
<TabItem x:Name="tabGraphic" Header="Graphic" Background="{x:Null}" ClipToBounds="True" GotFocus="tabGraphic_GotFocus" KeyDown="tabGraphic_KeyDown" >
    <Canvas x:Name="graphicCanvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <ItemsControl x:Name="myItemsControl" ItemsSource="{Binding Shapes., IsAsync=True, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate x:Name="myItemTemplate">
                <Canvas x:Name="myCanvas" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
     </ItemsControl>
</Canvas>
</TabItem>
[...]

Im MainWindow.xaml.cs


[...]
public MainWindow()
 {
    InitializeComponent();

     // GraphicUI
    graphicUIWorker.DoWork += graphicUIWorker_DoWork;
    graphicUIWorker.RunWorkerCompleted += graphicUIWorker_RunWorkerCompleted;
          
    graphicViewModel = new ViewModel.GraphicViewModel();
    graphicViewModel.PropertyChanged += UpdateGraphic;

    graphicCanvas.DataContext = this.graphicViewModel;

    graphicUIWorker.RunWorkerAsync();
}

void UpdateGraphic(object sender, PropertyChangedEventArgs e)
{
    designerCanvas.InvalidateArrange();
}

// Worker
void graphicUIWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker graphicUIWorker = sender as BackgroundWorker;

    graphicUIWorker.WorkerReportsProgress = false;
    graphicUIWorker.WorkerSupportsCancellation = true;

    Dispatcher.BeginInvoke(new Action(() => { graphicViewModel.StartAnimation(); }));
 }

Und die Grafik wird über das Hinzufügen von geometrischen Formen zu einer ObservableCollection erzeugt:

(GraphicViewModel.cs)


private ObservableCollection<Shape> shapes = new ObservableCollection<Shape>();
public ObservableCollection<Shape> Shapes
 {
    get { return shapes; }
    set
    {
        shapes = value;
    }
}

public void StartAnimation()
        {
            // Vertikalfahrt
            foreach (double stroke in strokeList)
            {
                shapes.Clear();

                DrawPunch(stroke);
                
                System.Threading.Thread.Sleep(500);
            }

        }

private void DrawPunch(double stroke)
{
    SolidColorBrush myBrush = new SolidColorBrush(Colors.Red);

     //Größe
     Rectangle rect = new Rectangle();
     rect.Height = 300;
     rect.Width = 250;
     //Transparenz
     rect.Opacity = 50;
     //Farbe
     myBrush = new SolidColorBrush(Colors.Brown);
     rect.Fill = myBrush;

     // Position
     rect.SetValue(Canvas.LeftProperty, 100);
     rect.SetValue(Canvas.TopProperty, stroke);
     Shapes.Add(rect);
}

Ich habe den Code stark vereinfacht, aber die Struktur sollte erkennbar sein.

Die Idee ist, dass in einer Schleife das Rechteck neu gezeichnet wird in Abhängigkeit des Weges (stroke, wurde vorher berechnet und die Schritte in einer Liste gespeichert). Leider wird die Grafik erst ganz am Ende aktualisiert.

Ich habe nun mehrere Tage im Internet gesucht und einiges ausprobiert - komme aber keinen Schritt weiter. Nun bin ich eher noch verwirrter als zuvor. Was mache ich falsch / was habe ich übersehen oder was muss ich anders machen?

S
248 Beiträge seit 2008
vor 10 Jahren

Hallo,

die GUI wird erst aktualisiert, wenn deine methode "StartAnimation" zurückkehrt. Anstall mit Sleep zu warten, muss du diese zyklisch aufrufen, z.B. mit Hilfe eines Timers (der alle 500ms) ausgelöst wird.

Grüße

4.942 Beiträge seit 2008
vor 10 Jahren

Hallo und willkommen,

du hast einen typischen Anfängerfehler beim Umgang mit Threads gemacht:
deine StartAnimation-Methode läuft vollständig im GUI-Thread und durch den Sleep dort, wird die GUI blockiert, s.a. [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke)

Nur die DrawPunch-Methode darfst (bzw. mußt) du 'invoken'.

Und ich bin mir ziemlich sicher, daß du mithilfe der WPF-Animationsklasse wesentlich besser zum Ergebnis kämst.

E
ezekiel Themenstarter:in
2 Beiträge seit 2012
vor 10 Jahren

Vielen Dank für die Antworten. Ich habe mir gestern Abend in ruhe schonmal die FAQ dazu nochmals durchgelesen. Ganz klar, wie ich meine DrawPunch-Routine aufrufen soll ist mir noch nicht, aber ich versuche es mal.

Die Animationsgeschichte habe ich mir vorher schonmal angeschaut - für die einfache Bewegungen z.B. des Stempels sicherlich geeignet. Der Stempel verformt allerdings ein Blech, dessen Darstellung und Verformung nicht ganz so einfach ist - daher habe ich davon Abstand genommen.