WPF UI multitasking

Ok. Delete all your code and start all over.

This is how you do that in WPF:

<Window x:Class="WpfApplication14.ItemsControlSample2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ItemsControlSample2" WindowState="Maximized">
    <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border Background="DarkGray" BorderBrush="Black" BorderThickness="1" CornerRadius="5"
                        Width="100" Height="100" Margin="10" >
                    <Grid>
                        <Image x:Name="img" Source="{Binding ImageSource}" Margin="2"/>

                        <TextBlock x:Name="txt" Text="Loading..." FontWeight="Bold"
                               VerticalAlignment="Center" HorizontalAlignment="Center"
                               Visibility="Collapsed" Foreground="AliceBlue"/>
                    </Grid>
                </Border>

                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsLoading}" Value="True">
                        <Setter TargetName="img" Property="Source" Value="{x:Null}"/>
                        <Setter TargetName="txt" Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

        <ItemsControl.Template>
            <ControlTemplate TargetType="ItemsControl">
                <ScrollViewer>
                    <WrapPanel IsItemsHost="True"/>
                </ScrollViewer>
            </ControlTemplate>
        </ItemsControl.Template>
    </ItemsControl>
</Window>

Code Behind:

public partial class ItemsControlSample2 : Window
{
    public ItemsControlSample2()
    {
        InitializeComponent();

        //Make sure you change this path to a valid path in your PC where you have JPG files
        var path = @"F:\Media\Images\My Drums";

        var images = Directory.GetFiles(path,"*.jpg")
                              .Select(x => new ImageViewModel()
                                           {
                                               Path = x,
                                           });
        DataContext = images.ToList();
    }
}

Data Item:

public class ImageViewModel : INotifyPropertyChanged
{
    private bool _isLoading;
    public bool IsLoading
    {
        get { return _isLoading; }
        set
        {
            _isLoading = value;
            OnPropertyChanged("IsLoading");
        }
    }

    private ImageSource _imageSource;
    public ImageSource ImageSource
    {
        get { return _imageSource; }
        set
        {
            _imageSource = value;
            OnPropertyChanged("ImageSource");
        }
    }

    private string _path;
    public string Path
    {
        get { return _path; }
        set
        {
            _path = value;
            OnPropertyChanged("Path");

            LoadImageAsync();
        }
    }

    private void LoadImageAsync()
    {
        IsLoading = true;

        var UIScheduler = TaskScheduler.FromCurrentSynchronizationContext();

        Task.Factory.StartNew(() =>
        {
            var bmp = new BitmapImage();
            bmp.BeginInit();
            bmp.UriSource = new Uri(Path, UriKind.Relative);
            bmp.CacheOption = BitmapCacheOption.OnLoad;
            bmp.EndInit();
            bmp.Freeze();
            return bmp;
        }).ContinueWith(x =>
        {
            ImageSource = x.Result;
            IsLoading = false;
        },UIScheduler);
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

Result:

enter image description here

  • Notice how I’m declaratively defining the UI in XAML as opposed to procedurally creating it in C# code. This is a much cleaner approach because it lets WPF do it’s job

  • I’m using an ItemsControl which is the appropiate approach for all “items”-based UIs in WPF. Regardless of their visual appearance.

  • Also notice how I’m leveraging DataBinding in order to populate the UI with actual data and also DataTriggers in order to create a basic stateful behavior.

    • While the image is loading (IsLoading == true), the Image.Source is null and the TextBlock is shown.
    • When the image finished loading (IsLoading == false), the Image.Source is bound to the ViewModel data and the TextBlock is hidden.
  • See how I’m using a WrapPanel for layout instead of manually placing the items. This gives you an “Explorer-like” behavior. Try resizing the Window to see the results.

  • I strongly suggest you read the above linked documentation, mostly the ItemsControl stuff and also Rachel’s WPF Mentality post.

  • WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.

  • I’m Using C# 4.0 and .Net 4.0, so I don’t have async/await. You should be able to remove all the Task based code and replace that by this newer, cleaner async approach.

  • Let me know if you need further help.

Leave a Comment