Forum: PC-Programmierung wpf bilder im anderen Task faden


von Lisa (Gast)


Lesenswert?

Hi,

ich habe eine xaml mit einem Storyboard mit welchen ich Bilder 
einfadenlassen kann
1
<Storyboard x:Key="FadeIn" >
2
<DoubleAnimation Duration="0:0:.25" Storyboard.TargetProperty="Opacity" From="0" To="1" />
3
</Storyboard>

wenn ich jetzt das Storyboard auch meinem Bild anwende
1
Storyboard StFadeOut = Resources["FadeOut"] as Storyboard).Clone();
2
StFadeOut.Begin(bild);

dann erscheint das "langsam" ...

nun möchte ich aber in einem anderen Task das ganze anwenden aber leider 
kann ich das Storyboard irgendwie nicht freigeben:
Beitrag "WPF C# image.Source wieder freigeben"
diese News habe ich gesehen, und nach dieser News müsste ich ja nur
das Storybord im Hauptthread Freezen und im Task dispatchen... leider 
klappt das nicht

1
Storyboard StFadeOut;
2
Main() {
3
4
StFadeOut = Resources["FadeOut"] as Storyboard).Clone();
5
StFadeOut.freeze();
6
}
7
8
9
Task() {
10
  StFadeOut.Dispatcher.Invoke(new Action(() =>
11
  {
12
    StFadeOut.Begin(bild);
13
  }));
14
}


leider funktioniert das "gar nicht"

vielen Dank für Tipp

* drück *
Lisa

von Frank (Gast)


Lesenswert?

Lisa schrieb:
> leider funktioniert das "gar nicht"

Was heißt den gar nicht?

Folgendes fällt mir auf:

1. Im XAML hat dein Storyboard den Key "FadeIn", im C#-Code greifst du 
aber auf ein Storyboard "FadeOut" zu.
2. Warum klonst du das Storyboard?
3. Warum der Aufruf von Freeze?

Der folgende Code funktioniert bei mir jedenfalls problemlos:
1
<Storyboard x:Key="FadeIn" >
2
  <DoubleAnimation Duration="0:0:.25" Storyboard.TargetProperty="Opacity" From="0" To="1" />
3
</Storyboard>
1
var StFadeIn = (Storyboard)Resources["FadeIn"];
2
var t = new Task(() =>
3
{
4
  StFadeIn.Dispatcher.Invoke(new Action(() =>
5
  {
6
    StFadeIn.Begin(bild);
7
  }));
8
});
9
10
t.Start();

von Lisa (Gast)


Lesenswert?

Okay, vielen dank...

das funktioniert jetzt so...

allerdings habe ich noch immer etwas was ich mir "anders" vorgestellt 
habe...

mein Aufbau:
1
Storyboard StFadeOut;
2
main() {
3
StFadeIn = (Storyboard)Resources["FadeIn"];
4
FadeImage();
5
}
6
7
public async Task FadeImage()
8
{
9
  await Task.Run(() =>
10
  {
11
    StFadeIn.Dispatcher.Invoke(new Action(() =>
12
    {
13
      Thread.Sleep(5000);
14
      StFadeIn.Begin(bild);
15
    }));
16
  });
17
}

während der 5 Sekunden, dreht sich meine Sanduhr und auch die App 
"hängt" ... somit läuft das nicht in einem Hintergrund-Task sondern 
belegt die App ...

was ist der Unterschied zwischen

> var t = new Task(() =>
> t.Start();
UND
> await Task.Run(() =>

=> Wobei auch hier genau das selbe Ergebnis herauskommt... vermutlich 
nur eine andere "Schreibweise"???

Ziel ist es Bilder Ein- oder Auszufaden, und das selbstverständlich so, 
dass die App nicht belegt ist <- klar kommen die 5Sekunden schlafen noch 
weg und das ganze wird über einen "DispatcherTimer" aufgerufen... aber 
ich will auch nicht, dass die App hängt, wenn ich 2 Sekunden fade oder 
so (daher kann mann das Sleep sinnbildlich als Reservator für den 
Hintergrundprozess sehen)...

> evtl. ist auch meine Herangehensweise überarbeitbar, dann bin ich offen für 
Kritik :)

* drück *
Lisa

von Frank (Gast)


Lesenswert?

Der Aufruf von
1
Dispatcher.Invoke(...)
 dient ja dazu, den angegebenen Code (in deinem Fall das Starten des 
Storyboards) im GUI-Thread auszuführen. D.h. auch dein
1
Thread.Sleep(5000)
 wird im GUI-Thread ausgeführt, was diesen dann blockiert.

Für gewöhnlich führt man alle lang andauernden Operationen (in deinem 
Fall vermutlich das Laden eines Bildes aus irgendeiner Quelle) in einem 
Hintergrundthread durch. Wenn die Daten dann vollständig geladen sind, 
aktualisiert man die GUI. Das entspricht ja schon mal deinem Vorgehen, 
das passt also soweit.
Natürlich muss man darauf achten, dass man die Operationen, die im 
GUI-Thread laufen, so kurz wie möglich hält.

Im Code ausgedrückt:
1
public async Task FadeImage()
2
{
3
  await Task.Run(() =>
4
  {
5
    // Zeitintensive Operationen hier ausführen
6
    Thread.Sleep(5000);
7
8
    StFadeIn.Dispatcher.Invoke(new Action(() =>
9
    {
10
      // Hier nur noch die GUI aktualisieren
11
      StFadeIn.Begin(bild);
12
    }));
13
  });
14
}

Lisa schrieb:
> aber
> ich will auch nicht, dass die App hängt, wenn ich 2 Sekunden fade oder
> so

In diesem konkreten Beispiel gilt: WPF kümmert sich schon darum, dass 
die Anwendung während des Fadens weiterhin reagiert (probiers einfach 
mal aus, indem du Zeit im Storyboard erhöhst).

Ohne jetzt zu tief ins Detail gehen zu wollen: Man kann das erreichen 
indem man die lang andauernde Operation immer wieder kurz "unterbricht" 
und die MessageQueue abarbeitet. Aus persönlicher Erfahrung rate ich dir 
die Finger davon zu lassen. So etwas sorgt in regelmäßigen Abständen für 
die kuriosesten Bugs (muss beruflich selbst eine Anwendung betreuen, in 
der so etwas gemacht wird ;-)).

Daher gilt (wie oben schon geschrieben): Lang dauernde Operationen im 
Hintergrundthread durchführen und dann nur noch die GUI aktualisieren.

Lisa schrieb:
> was ist der Unterschied zwischen
>
>> var t = new Task(() =>
>> t.Start();
> UND
>> await Task.Run(() =>
>
> => Wobei auch hier genau das selbe Ergebnis herauskommt... vermutlich
> nur eine andere "Schreibweise"???
1
await Task.Run(() =>

Zum einen startet das den Task und entspricht damit dem ersten Code. Das 
await (was übrigens nur in Methoden verwendet werden kann, die als async 
gekennzeichnet sind), wartet aber auch bis der Task abgeschlossen ist. 
Das heißt, Code der nach dem mit await gekennzeichneten Aufruf kommt, 
wird erst ausgeführt, wenn der Task abgearbeitet ist:
1
private async void Test()
2
{
3
  await Task.Run(() => Thread.Sleep(5000));
4
5
  MessageBox.Show("Diese MessageBox wird erst nach 5 Sekunden angezeigt.");
6
}

Während das async auf die Beendigung des Tasks wartet, wird die 
Kontrolle übrigens an den Aufrufer der asynchronen Methode 
zurückgegeben:
1
private void MyMethod()
2
{
3
  Test();
4
5
  MessageBox.Show("Diese MessageBox wird sofort angezeigt.");
6
}
7
8
private async void Test()
9
{
10
  await Task.Run(() => Thread.Sleep(5000));
11
12
  MessageBox.Show("Diese MessageBox wird erst nach 5 Sekunden angezeigt.");
13
}

von Frank (Gast)


Lesenswert?

Sorry gerade fällt mir noch auf:

Frank schrieb:
> public async Task FadeImage()
> {
>   await Task.Run(() =>
>   {
>     // Zeitintensive Operationen hier ausführen
>     Thread.Sleep(5000);
>
>     StFadeIn.Dispatcher.Invoke(new Action(() =>
>     {
>       // Hier nur noch die GUI aktualisieren
>       StFadeIn.Begin(bild);
>     }));
>   });
> }

hier sollte man das await natürlich weglassen. Sonst blockiert der 
Aufruf von FadeImage() wirklich so lange, bis die lang andauernden 
Operationen abgeschlossen sind.

von Lisa (Gast)


Lesenswert?

dankeschön <3 ...

sehr sehr gut

lg lisa

von Lisa (Gast)


Lesenswert?

evtl. noch eine Frage.

Kann ich irgendwo testen welches Storyboard angewandt wurde?

also wenn ich jetzt 2 Storyboards habe:
FadeOut.Begin(Bild)

==> TESTE FadeOut an Bild => TRUE

FadeIn.Begin(Bild)

==> TESTE FadeOut an Bild => FALSE

Danke =)
lisa

von Lisa (Gast)


Lesenswert?

okay, überflüssige frage...
> nicht das Storyboard ist interessant sondern die Opacity, und die kann ich 
leicht erfragen...

hihi ... danke

von Lisa (Gast)


Lesenswert?

Hi,

wie kann ich die "Duration" mit C#-Code ändern?
1
<Storyboard x:Name="FadeOut" x:Key="FadeOut">
2
<DoubleAnimation Duration="0:0:0.5" Storyboard.TargetProperty="Opacity" AccelerationRatio="0.4" DecelerationRatio="0.4" From="1" To="0" />
3
</Storyboard>

FadeOut.DoubleAnimation.Duration = "0:0:5" <- ist zu einfach gedacht ^^

lisa

von Lisa (Gast)


Lesenswert?

hier hab ich auch die Lösung gefunden...

Für euch:
DoubleAnimation dFOut = (DoubleAnimation)StFadeOut.Children[0];
dFOut.Duration = new Duration(new TimeSpan(ts));

lisa

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.