Collegamento alla proprietà Task e impostazione Async su true

0

Sto cercando di capire un buon modo per associare la vista alle proprietà viewmodel che devono essere popolate usando un metodo async . L'ultimo che ho ottenuto utilizza IsAsync come tale.

ViewModel.cs

public class ViewModel
{
    public Task<string> Name { get; set; }

    public ViewModel()
    {
        Name = GetNameAsync();
    }

    public async Task<string> GetNameAsync()
    {
        return await Task.Run(async () =>
        {
            await Task.Delay(5000);
            return "Foo";
        });
    }
}

View.xaml

<Label Content="{Binding Name.Result, IsAsync=true}" />

Sembra funzionare ma non sono sicuro se sto usando IsAsync come dovrebbe essere usato. La documentazione non riporta nulla relativo a Task e c'era anche questo commento su uno stackoverflow che dice che IsAsync non è correlato a C # async .

Che cosa può andare storto con tale implementazione?

    
posta Paolo Go 24.11.2018 - 05:20
fonte

2 risposte

0

Il modo più pulito per gestire questo tipo di proprietà asincrone "una tantum" consiste nell'utilizzare NotifyTaskCompletetion dalla libreria Nito.Asyncex di Stephen Cleary (disponibile su Nuget).

Il tuo ViewModel diventerebbe quindi

public class ViewModel
{
    public INotifyTaskCompletion<string> Name { get; set; }

    public ViewModel()
    {
        Name = NotifyTaskCompletetion.Complete(GetNameAsync());
    }

    private async Task<string> GetNameAsync()
    {
        return await Task.Run(async () =>
        {
            await Task.Delay(5000);
            return "Foo";
        });
    }
}

Dovresti quindi eseguire il binding a Name.Result come associazione standard, senza bisogno di IsAsync.

Maggiori dettagli qui .

    
risposta data 24.11.2018 - 15:03
fonte
0

Correggere l'opzione IsAsync non ha nulla a che fare con l'attività, dice semplicemente all'interfaccia utente di mostrare il valore di fallback e non di bloccare mentre viene eseguita la chiamata get.

Cosa può andare storto con il tuo codice. Hmmmmm penso che dovrei provare per capire se qualcosa è andato storto. Hai un numero di cose "cattive".

  1. Codice nel costruttore. Mantieni il riferimento all'attività in modo che non sia tutto negativo, ma generalmente non dovresti avere un codice come questo in un costruttore

  2. Chiamare .Result su un'attività. Questo può causare deadlock

  3. Avere un'attività asincrona ma non in attesa.

  4. Utilizzo di Task.Run all'interno di un'attività? Puoi semplicemente chiamare attendere Task.Delay direttamente

Ristrongrà il tuo codice come segue

ViewModel

public class ViewModel : INotifyPropertyChanged
{
    private string name
    public string Name {
        get {return name;}
        set {
               name = value;
               PropertyChanged("Name");
        }
    public ICommand GetNameAsync;

    private void PropertyChanged(string prop)
    {
       if( PropertyChanged != null )
       {
          PropertyChanged(this, new PropertyChangedEventArgs(prop);
       }
    }


    public ViewModel()
    {
         Name="Loading...";
         GetNameAsync =  new AsyncCommand(async () => {Name = await GetNameAsync()});
    }

    private async Task<string> GetNameAsync()
    {
        await nameService.getName();
    }
}

XAML

<Window xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding GetNameAsync}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <Label Content="{Binding Name}" />
</Window>

Ora il tuo Xaml chiamerà la funzione GetName quando la finestra verrà caricata, l'etichetta mostrerà "Caricamento in corso ..." fino a quando la funzione GetName non sarà completata, quando aggiornerà il nome

Sfortunatamente

  1. WPF non ha un comando AsyncCommand.

    Devi crearne uno tu o usarne uno da un framework di terze parti (vedi sotto)

  2. INotifyPropertyChanged è una seccatura da digerire.

    Raccomando di usare Fody con il suo plugin INotifyPropertyChanged link

AsyncCommand

link

using System;  
using System.Threading.Tasks;  
using System.Windows.Input;  

namespace OpenWeather.Command  
{  
    internal class AsyncCommand : ICommand  
    {  
        private readonly Func<Task> _execute;  
        private readonly Func<bool> _canExecute;  
        private bool _isExecuting;  

        public AsyncCommand(Func<Task> execute) : this(execute, () => true)  
        {  
        }  

        public AsyncCommand(Func<Task> execute, Func<bool> canExecute)  
        {  
            _execute = execute;  
            _canExecute = canExecute;  
        }  

        public bool CanExecute(object parameter)  
        {  
            return !(_isExecuting && _canExecute());  
        }  

        public event EventHandler CanExecuteChanged;  

        public async void Execute(object parameter)  
        {  
            _isExecuting = true;  
            OnCanExecuteChanged();  
            try  
            {  
                await _execute();  
            }  
            finally  
            {  
                _isExecuting = false;  
                OnCanExecuteChanged();  
            }  
        }  

        protected virtual void OnCanExecuteChanged()  
        {  
            if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs());  
        }  
    }  
}
    
risposta data 24.12.2018 - 17:01
fonte

Leggi altre domande sui tag