Aufgaben laufen lassen, ohne dass sie die UI behindern

  • WPF

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    Aufgaben laufen lassen, ohne dass sie die UI behindern

    Hallo liebes Forum, ich habe folgendes Problem bzw. Frage.

    Ich habe zu Übungszwecken (um mit dem ViewModel Konzept und dem UI Design in WPF sicherer zu werden) ein kleines Programm geschrieben.

    In eine Textbox gibt man einen Liednamen ein und das Programm sucht im Internet nach passenden Liedern und zeigt dir in einer Listbox mit DataTemplate die gefundenen Lieder an (max. 5).
    Allerdings braucht diese Abfrage im Internet ca. 1 Sekunde, was ja nicht schlimm ist, das kann man ja nicht schneller machen. Da die ObservableCollection, in der die gefundenen Lieder gespeichert sind, nach jedem eingegebenen Zeichen in der Textbox (UpdateTrigger: OnPropertyChanged) aktuallisiert wird, "hängt" sich das Programm in der Zeit auf, bis die Sucheinträge angezeigt werden.
    Ich stelle es mir aber so vor, dass in der Zeit wo die Suchergebnisse laden, man das Programm normal weiternutzen kann, sprich ein weiteres Zeichen in die Textbox eingeben, etc, und in der Listbox am besten irgend so ein Kreis der sich dreht und daneben ´"Bitte warten" steht.
    Ich glaube, da muss man mit async und await arbeiten, aber leider habe ich keinen Ansatz, wie das geht.

    Über einen Ansatz wäre ich sehr dankbar.

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor

    flori2212 schrieb:

    Ich glaube, da muss man mit async und await arbeiten, aber leider habe ich keinen Ansatz, wie das geht.

    Was hindert dich daran, selbständig im Netz zu suchen ? :S Die nötigen Stichworte hast du doch ! ;)
    Hallo FormFollowsFunction,

    das Problem ist, dass ich noch nicht so sicher im Umgang mit den Schlüsselwörtern bin, gerade in Verbindung mit der WPF UI.

    Ich habe nur await und async bis jetzt nur ein Mal in einem Projekt verwendet (und erlich gesagt nicht richtig verstanden).

    Also wenn mit jemand einen Link oder Ansatz für die Umsetzung in WPF geben könnte wäre ich sehr dankbar.

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor
    Hallo,

    ich muss mich leider nochmals melden.
    Ich habe mir einige Artikel im Internet durchgelesen (auch den verlinkten), allerdings habe ich leider keine Lösung gefunden (weil ich es event. auch noch nicht ganz verstanden habe :( )

    Ich zeige jetzt hier mal meinen Code:

    C#-Quellcode

    1. using MusicListWriter.Model.JsonSerializer;
    2. using Newtonsoft.Json;
    3. using Newtonsoft.Json.Linq;
    4. using System;
    5. using System.Collections.Generic;
    6. using System.Collections.ObjectModel;
    7. using System.Diagnostics;
    8. using System.Linq;
    9. using System.Text;
    10. using System.Threading;
    11. using System.Threading.Tasks;
    12. using System.Windows.Input;
    13. using unirest_net.http;
    14. using ViewModel;
    15. namespace MusicListWriter.ViewModel
    16. {
    17. public class MainViewModel : ViewModelBase
    18. {
    19. private string _SearchTrackText;
    20. public string SearchTrackText {
    21. get { return _SearchTrackText; }
    22. set { _SearchTrackText = value; RaisePropertyChanged(); /*Hier muss der Aufruf hin*/ }
    23. }
    24. private ObservableCollection<TrackViewModel> _SearchTrackList;
    25. public ObservableCollection<TrackViewModel> SearchTrackList
    26. {
    27. get { return _SearchTrackList; }
    28. set
    29. {
    30. _SearchTrackList = value;
    31. RaisePropertyChanged();
    32. }
    33. }
    34. // ....
    35. public MainViewModel()
    36. {
    37. SearchTrackList = new ObservableCollection<TrackViewModel>();
    38. TrackList = new ObservableCollection<TrackViewModel>();
    39. }
    40. public async void RefreshSearchTrackList()
    41. {
    42. SearchTrackList.Clear();
    43. HttpResponse<string> response = Unirest.get("https://deezerdevs-deezer.p.rapidapi.com/search?q=" + SearchTrackText)
    44. .header("X-RapidAPI-Host", "deezerdevs-deezer.p.rapidapi.com")
    45. .header("X-RapidAPI-Key", "f6745fab91msha0f34ec4cacb41cp17f672jsn5cfb24e6cc1a")
    46. .asJson<string>();
    47. try
    48. {
    49. JObject josnFile = JObject.Parse(response.Body);
    50. JArray datas = JArray.Parse(josnFile.SelectToken("data").ToString());
    51. int i = 0;
    52. foreach (var result in datas)
    53. {
    54. if (i < 5)
    55. {
    56. Track t = JsonConvert.DeserializeObject<Track>(result.ToString());
    57. HttpResponse<string> responseGenre = Unirest.get("https://deezerdevs-deezer.p.rapidapi.com/album/" + t.Album.ID)
    58. .header("X-RapidAPI-Host", "deezerdevs-deezer.p.rapidapi.com")
    59. .header("X-RapidAPI-Key", "f6745fab91msha0f34ec4cacb41cp17f672jsn5cfb24e6cc1a")
    60. .asJson<string>();
    61. JObject genreObject = JObject.Parse(responseGenre.Body);
    62. JArray genreDatas = JArray.Parse(genreObject.SelectToken("genres").SelectToken("data").ToString());
    63. foreach (var genreresult in genreDatas)
    64. {
    65. Genre g = new Genre();
    66. g.Name = genreresult.SelectToken("name").ToString();
    67. t.Album.Genre = g;
    68. }
    69. SearchTrackList.Add(new TrackViewModel(t));
    70. i++;
    71. }
    72. else { break; }
    73. }
    74. }
    75. catch { }
    76. }
    77. }
    78. }



    Wie man sieht ist die Methode RefreshSearchTrackList die Methode, die asyncron laufen soll. Aufgerufen wird sie in dem einem Setter von dem Property.
    Allerdings reicht es ja nicht auss, vor die Methode ein async zu schreiben :) .

    Wenn mir einer an diesem expliziten Beispiel helfen könnte wäre ich sehr dankbar.

    Viele Grüße
    Florian


    PS:
    Spoiler anzeigen
    ​Für den Http Abruf gibt es auch eine Methode mit dem Task Objekt (die auch asyncron ist), wo ich aber nicht weiß, wie man das Ergebnis weiterverarbeitet:

    C#-Quellcode

    1. Task<HttpResponse<string>> response = Unirest.get("https://deezerdevs-deezer.p.rapidapi.com/search?q=eminem").header("X-RapidAPI-Host", "deezerdevs-deezer.p.rapidapi.com").header("X-RapidAPI-Key", "f6745fab91msha0f34ec4cacb41cp17f672jsn5cfb24e6cc1a").asJsonAsync();
    ----

    WebApps mit C#: Blazor
    Bei einem Async ohne Await müsste das VS eigentlich meckern.
    docs.microsoft.com/de-de/dotne…-reference/keywords/async
    Die Methode als Async zu kennzeichnen reicht nicht aus. Da muss auch eine Funktion awaitet werden.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Dksksm“ ()

    @flori2212
    Wichtig ist mal das du die Daten Asyncron aus dem Web abrufst. Also mit einer Methode welche mit Async endet. z.b. GetResponseAsync(). Das gibt dir einen Task<WebResponse> zurück.
    Im zusammenspiel mit dem KeyWord AWAIT in deiner Asyncronen (mit Async gekennzeichneten Methode) ist dies nicht viel anders zu schreiben wie ohne Async/Await.

    Siehe hier.

    Der Aufbau der ViewModels könnte so aussehen:

    C#-Quellcode

    1. using System.Collections.ObjectModel;
    2. using System.Threading.Tasks;
    3. namespace AsyncDemo
    4. {
    5. public class MainViewModel : ViewModelBase
    6. {
    7. private string _SearchTrackText;
    8. public string SearchTrackText
    9. {
    10. get { return _SearchTrackText; }
    11. set { _SearchTrackText = value; RaisePropertyChanged(); RefreshSearchTrackList(); }
    12. }
    13. private ObservableCollection<TrackViewModel> _SearchTrackList;
    14. public ObservableCollection<TrackViewModel> SearchTrackList
    15. {
    16. get { return _SearchTrackList; }
    17. set
    18. {
    19. _SearchTrackList = value;
    20. RaisePropertyChanged();
    21. }
    22. }
    23. // ....
    24. public MainViewModel()
    25. {
    26. SearchTrackList = new ObservableCollection<TrackViewModel>();
    27. }
    28. public async void RefreshSearchTrackList()
    29. {
    30. VMisBusy = true;
    31. await Task.Delay(1000); //1 Sek warten. Hier sollte das Webservice Asyncron (!!) aufgerufen werden.
    32. SearchTrackList.Clear();
    33. for (int i = 0; i < 10; i++)
    34. {
    35. SearchTrackList.Add(new TrackViewModel() { Trackname = $"New Track {i}" });
    36. }
    37. VMisBusy = false;
    38. }
    39. }
    40. }


    Ich hbe meißt in meiner ViewModelBase-Basisklasse eine Eigenschaft VmIsBusy welche ich immer ein oder ausschalten kann. Im View dann einen einfachen BusyIndicator welcher mit einem BooleanToVisibility Converter ein oder ausgeblendet wird. Siehe Beispiel:

    XML-Quellcode

    1. <Window x:Class="AsyncDemo.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:AsyncDemo"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="450" Width="800">
    9. <Window.Resources>
    10. <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    11. </Window.Resources>
    12. <Window.DataContext>
    13. <local:MainViewModel/>
    14. </Window.DataContext>
    15. <Grid>
    16. <StackPanel Margin="10">
    17. <TextBox Text="{Binding SearchTrackText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="10"/>
    18. <ListBox Margin="10" ItemsSource="{Binding SearchTrackList}" DisplayMemberPath="Trackname"/>
    19. </StackPanel>
    20. <local:WaitingIndicator Visibility="{Binding VMisBusy, Converter={StaticResource BooleanToVisibilityConverter}}"
    21. HorizontalAlignment="Center" VerticalAlignment="Center" Height="100" Width="100"/>
    22. </Grid>
    23. </Window>


    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo,

    Vielen Dank @Nofear23m , dank deiner Hilfe hab ich es nun hinbekommen.
    Ich nutze zwar die Unirest API um Daten aus dem Web abzurufen, diese bitet aber auch asynchrone Methoden.

    Ist ja am Ende gar nicht so schwer.

    Ich werde mir es aber trotzdem nochmals genauer durchlesen müssen, dass ich es im Detail verstehe.

    Auch noch mal vielen Dank an alle anderen, die mir geholfen haben.

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor

    flori2212 schrieb:

    Ich werde mir es aber trotzdem nochmals genauer durchlesen müssen, dass ich es im Detail verstehe.

    Das ist auch gut so, so soll es sein. Aber ich weis wie es ist, es ist immer ganz gut es anhand eines Praxisbeispielen zu lernen anstatt einer "dummen", nicht Praxisbezogenen Demo.

    Freut mich das du es hinbekommen hast.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##