简体   繁体   中英

WPF Best practice for async property

Let's suppose I have a long running Web API call (async method) which returns a string.

Is there a best practice between those 2 solutions to display the result in a WPF property without blocking UI ? Or is there another one ?

Note: both solutions are not freezing UI and I already view posts How to call an async method from a getter or setter? and Async property in c# .

My Wep API

private async Task<string> GetAsyncProperty()
{
    string result = "Async Property Value";

    // Web api call...
    await Task.Delay(TimeSpan.FromSeconds(10));

    return result;
}

Solution A

XAML:

<TextBlock Text="{Binding Path=AsyncPropertyA, UpdateSourceTrigger=PropertyChanged}" />

ViewModel:

public MyConstructor()
{
    Task task = SetAsyncPropertyA();
}

private async Task SetAsyncPropertyA()
{
    this.AsyncPropertyA = await GetAsyncProperty().ConfigureAwait(false);
}

Solution B

XAML:

<TextBlock Text="{Binding Path=AsyncPropertyB, UpdateSourceTrigger=PropertyChanged, IsAsync=True, FallbackValue='Loading B...'}" />

ViewModel:

public string AsyncPropertyB
{
    get
    {
        return GetAsyncPropertyB();
    }
}

private string GetAsyncPropertyB()
{
    return Task.Run(() => GetAsyncProperty()).Result;
}

Note: in solution B, I can add FallbackValue that's not working in solution A and potentially some other UI updates in ContinueWith of the Task.Run.

You should try to use a good framework for that, which already inveted that wheel.

Take a look at ReactiveUI command sample :

LoadTweetsCommand = ReactiveCommand.CreateAsyncTask(() => LoadTweets())

LoadTweetsCommand.ToProperty(this, x => x.TheTweets, out theTweets);
LoadTweetsCommand.ThrownExceptions.Subscribe(ex => /* handle exception here */);

Those extensions work on IObservable, which on itself is very powerfull tool:

Observable.FromAsync(async () =>
            {
                await Task.Delay(100);
                return 5;
            }).ToProperty(x => )

In both cases, you're not catching any errors that might happen while trying to call the Web API. You might want to log it to a file and/or show an error message to the user.

In that case, await makes it easy - you can just use try/catch:

public MyConstructor()
{
    try
    {
        Task task = SetAsyncPropertyA();
    }
    catch (Exception e)
    {
        // log the error or show a message
    }
}

private async Task SetAsyncPropertyA()
{
    this.AsyncPropertyA = await GetAsyncProperty().ConfigureAwait(false);
}

You could also move the try/catch to the async method. In that case, since there's no chance of an error escaping from it, you could make it async void. Sometimes this is necessary for event handlers (at least in Windows Forms - not sure about WPF.)

public MyConstructor()
{
    SetAsyncPropertyA();
}

private async void SetAsyncPropertyA()
{
    try
    {
        this.AsyncPropertyA = await GetAsyncProperty().ConfigureAwait(false);
    }
    catch (Exception e)
    {
        // log the error or show a message
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM