Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling Awaitable Methods in Property Accessor [Windows Store Apps/Metro Apps]

I have a custom class (to simplify stuff, I have stripped down the codes):

public class AlbumItem
{
   public StorageFile AlbumThumbnail { get; set;}
}

When I bind this to a ListView:

<ListView.ItemTemplate>
            <DataTemplate>
                <Grid Height="100" Background="#FFabe372">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="80" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Image Source="{Binding AlbumSource}" Margin="10" Stretch="Uniform"></Image>
                    <TextBlock Margin="20 5" Grid.Column="1" Style="{StaticResource  AlbumListViewItemStyle}" Text="{Binding AlbumDisplayText}"></TextBlock>
                </Grid>

            </DataTemplate>
</ListView.ItemTemplate>

The Image's source is set to AlbumSource, as I have thought to implement AlbumSource as a Get-only property:

public class AlbumItem
{
   public StorageFile AlbumThumbnail { get; set;}

   public BitmapImage AlbumSource
   {
       get
       {
          BitmapImage bmp = new BitmapImage();
          bmp.SetSource(await AlbumThumbnail.OpenReadAsync());
          return bmp;
       }
   }
}

As expected I can't use the await keyword in the accessor method, at the same time I cannot declare a property stub as an async method.

Can somebody point me in the correct direction? I may be trying a Converter later. Thanks!

like image 504
VT Chiew Avatar asked Nov 29 '12 14:11

VT Chiew


2 Answers

IMHO property accessors should always return almost immediately. Long running operations should not execute inside a property accessor as this can have significant perfromance impacts because of the widely held assumption that an accessor is basically a variable read operation (i.e. properties can be accessed a lot, in loops, not cached for subsequent access etc).

I'd do something like this:

private async Task SetAlbumSourceAsync()
{
    bmp = new BitmapImage();
    var source = await AlbumThumbnail.OpenReadAsync();
    bmp.SetSource(source);
    RaisePropertyChanged("AlbumSource");
}

BitmapImage bmp;
public BitmapImage AlbumSource
{
    get
    {
        if (bmp == null) // might need a better sync mechanism to prevent reentrancy but you get the idea
            SetAlbumSourceAsync();

        return bmp;
    }
}
like image 80
dkackman Avatar answered Oct 16 '22 17:10

dkackman


If you use await, the return type will have to be Task<BitmapImage>. If you want to be able to bind to your XAML, you need to return BitmapImage, and therefore can't use await. Use Task.Result instead:

public BitmapImage AlbumSource
{
    get
    {
       BitmapImage bmp = new BitmapImage();
       bmp.SetSource(AlbumThumbnail.OpenReadAsync().GetResults());
       return bmp;
    }
}
like image 39
Flavien Avatar answered Oct 16 '22 15:10

Flavien