Archive for October 2010

BackgroundWorker test, this time with Task

Ok, so naturally I don’t want to be old school and do long time running operations with BackgroundWorker. So I wanted to try to clean up the previous code and rewrite it using Task (new in .NET4) instead of BackgroundWorker.

I wanted to recreate the previous example, with the addition of possibility to run multiple tasks at the same time, without having the UI freeze.

This is what I came up with (setup in MVVM fashion).

ViewModel:

public class NumberCounterTaskViewModel : ViewModelBase
{
    private Task<TimeSpan> task;

    private string infoText;
    private double currentProgressValue;

    public NumberCounterTaskViewModel()
    {
        SetupTask();
    }

    private void SetupTask()
    {
        CurrentProgressValue = 0;

        //  Create a delegate to be invoked in the long time running
        //  service method.
        Action<int> progressUpdatedCallback = i => SetProgress(i);

        //  Creates a new task which will hold a TimeSpan as 
        //  Result property. ALongRunningMethod returns TimeSpan value
        task = new Task<TimeSpan>(() => PseudoService.ALongRunningMethod(progressUpdatedCallback));

        //  When the long running task is finished, we 
        //  want to display the result
        task.ContinueWith(t => SetCompletedTime(t.Result));
    }

    private void SetCompletedTime(TimeSpan ts)
    {
        InvokeOnUIThread(() =>
            {
                InfoText = string.Format("Completed in {0}.{1} sec",
                    ts.Seconds.ToString(),
                    ts.Milliseconds.ToString());
                OnPropertyChanged("StartCommand");
            });
    }

    private void SetProgress(int progress)
    {
        InvokeOnUIThread(() =>
        {
            InfoText = progress.ToString();
            CurrentProgressValue = (double)progress;
        });
    }

    /// <summary>
    /// Property for exposing value to View
    /// </summary>
    public string InfoText
    {
        get { return infoText; }
        set
        {
            if (infoText != value)
            {
                infoText = value;
                OnPropertyChanged("InfoText");
            }
        }
    }

    /// <summary>
    /// Property for exposing value to View
    /// </summary>
    public double CurrentProgressValue
    {
        get { return currentProgressValue; }
        set
        {
            if (currentProgressValue != value)
            {
                currentProgressValue = value;
                OnPropertyChanged("CurrentProgressValue");
            }
        }
    }

    /// <summary>
    /// ICommand for binding the start of task to a button
    /// </summary>
    public ICommand StartCommand
    {
        get
        {
            return new DelegateCommand(
                () => StartCommandExecute(),
                () => StartCommandCanExecute());
        }
    }

    /// <summary>
    /// Execute method for StartCommand.
    /// </summary>
    private void StartCommandExecute()
    {
        //  If we've run the operation once, we reset the task
        if (task.IsCompleted)
            SetupTask();
        task.Start();
    }

    /// <summary>
    /// Defines whether StartCommand can be executed. 
    /// Task cannot be started if its alread running
    /// </summary>
    /// <returns></returns>
    private bool StartCommandCanExecute()
    {
        return task.Status != TaskStatus.Running;
    }

    /// <summary>
    /// Invokes an action on the UI-thread. Needed since 
    /// we do work in the background on another thread
    /// </summary>
    /// <param name="action">Delegate to be invoked on UI thread</param>
    private void InvokeOnUIThread(Action action)
    {
        if (Application.Current != null)
        {
            Application.Current.Dispatcher.BeginInvoke(action);
        }
    }
}

and

public class PseudoService
{
    /// <summary>
    /// Very important and long time running stuff that happens in the background
    /// </summary>
    /// <returns></returns>
    public static TimeSpan ALongRunningMethod(Action<int> progressCallback)
    {
        DateTime before = DateTime.Now;
        int check = int.MaxValue / 100;
        for (int i = 1; i < int.MaxValue; i++)
        {
            if (i % check == 0)
            {
                progressCallback(i);
            }
        }
        return new TimeSpan(DateTime.Now.Ticks - before.Ticks);
    }
}

View:

<UserControl x:Class="TaskTest.NumberCounterTaskView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:viewModel="clr-namespace:TaskTest"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             d:DataContext="{d:DesignInstance Type={x:Type viewModel:NumberCounterTaskViewModel}}"
             >
    <UserControl.DataContext>
        <viewModel:NumberCounterTaskViewModel />
    </UserControl.DataContext>
    <Border Background="#E7F7F9" BorderBrush="#A3BDED" 
            CornerRadius="3" BorderThickness="4" Margin="2">
    <DockPanel LastChildFill="True">
        <TextBlock
            DockPanel.Dock="Bottom"
            x:Name="textBlock"
            Text="{Binding Path=InfoText}"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Center"
            FontSize="20"
            
            FontWeight="Bold"/>
        <Button
            DockPanel.Dock="Left"
            Command="{Binding Path=StartCommand}"
            Content="Start"
            Height="23"
            Width="75"
            Margin="2"
            x:Name="startButton"
            />
        
        <ProgressBar
            DockPanel.Dock="Right"
            Value="{Binding CurrentProgressValue}"
            x:Name="progressBar"
            Height="23"
            Margin="2"/>
    </DockPanel>
    </Border>
</UserControl>

Added three of these to a stackpanel in a window:

<Window x:Class="TaskTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow"
        xmlns:local="clr-namespace:TaskTest"
    Height="350" Width="525">
    <StackPanel>
        <local:NumberCounterTaskView />
        <local:NumberCounterTaskView />
        <local:NumberCounterTaskView />
    </StackPanel>
</Window>

And you get this:

Three controls that runs a long time running operation in the background while the rest of the UI is active and responsive.

MVVM detail ponderings

So, I am refactoring some of the ViewModel help code I have here at work, and some things bubbled up to consider.

A common scene is to have an ObservableCollection<T> in the viewmodel which is databound to a DataGrid in the View. T in this case is my entities from EF which are implementing INotifyPropertyChanged.

ObservableCollection<T> is very nifty, implementing ICollectionChanged it tells the view when an item is added or removed from the collection. Since my entities implement INotifyPropertyChanged, when an item in the collection has one of it’s properties edited, the view will be notified.

But, what about values that are dependent on values in the collection but is not directly notified via INotifyPropertyChanged?

Consider the following

public class Customer : INotifyPropertyChanged
{
    public double Balance { get; set; }
}

public class CustomerEditViewModel : INotifyPropertyChanged
{
    public CustomerEditViewModel(IEnumerable<Customer> customerList)
    {
        CustomerList = new ObservableCollection<Customer>(customerList);
    }

    public ObservableCollection<Customer> CustomerList { get; set; }

    public double CustomerAverageBalance
    {
        get
        {
            return CustomerList.Average(cust => cust.Balance);
        }
    }
}

Now the CustomerAverageBalance property will be updated when the collection is modified.
But what about if an item in the collection is edited? Quick solution is to loop through the items in the list and subscribe to their PropertyChanged event.

CustomerList.ForEach(cust => cust.PropertyChanged += (s, e) =>
{
    if(e.PropertyName == "Balance")
    {
        OnPropertyChanged("CustomerAverageBalance"));
    }
};

Will do the trick.

Now we are notifying the view every time an item in the collection is changed.</p>
This works as a start, but it is not very elegant in my opinon. Also, when an item is added to the collection, the view will not be updated of it’s PropertyChanged.

So what I’d like to do, is wrap these two events (CollectionChanged and PropertyChanged) together in a (hopefully) more elegant and reusable way. The idea is to have “anything that affects the outcome
This is my helper class which takes care of this.

public class ListCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
    #region Private fields
    private ICollectionView view;
    #endregion

    #region Public constructors
    public ListCollection() { Setup(); }
    public ListCollection(List<T> list) : base(list) { Setup(); }
    public ListCollection(IEnumerable<T> collection) : base(collection) { Setup(); }
    #endregion

    private void Setup()
    {
        CollectionChanged += (s, e) =>
        {
            if (e.Action == NotifyCollectionChangedAction.Add | e.Action == NotifyCollectionChangedAction.Replace)
            {
                foreach (T item in e.NewItems)
                {
                    item.PropertyChanged += (sender, args) => OnItemPropertyChanged(item, args);
                }
            }
            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (T item in e.OldItems)
                {
                    item.PropertyChanged -= (sender, args) => OnItemPropertyChanged(item, args);
                }
            }
        };
    }

    private void OnItemPropertyChanged(T item, PropertyChangedEventArgs e)
    {
        ItemPropertyChangedHandler handler = ItemPropertyChanged;
        if (handler != null)
        {
            handler((T)item, e);
        }
    }

    public event ItemPropertyChangedHandler ItemPropertyChanged;

    public delegate void ItemPropertyChangedHandler(T item, PropertyChangedEventArgs e);
}

It’s a class which inherits ObservableCollection<T> and after initialization it sets up that when the collection is changed, it adds or removes an event handler on the added/removed items. The class exposes an event “ItemPropertyChanged” which is fired every time an item in the collection fires PropertyChanged.
Usage can look like this:

CustomerList = new ListCollection<Customer>(customerList);
CustomerList.ItemPropertyChanged += (s, e) =>
{
    if (e.PropertyName == "Balance")
    {
        OnPropertyChanged("CustomerAverageBalance");
    }
};

Paul Stovell has written about similar problem (Introducing Observal), which could probably solve the above problem also in a very elegant, but for my purposes, too complex way.

BackgroundWorker simple test

So. I wanted to try the BackgroundWorker since it’s been several years since I used it.

I also happened to be a good project for a first simple blog post. If I ever churn out a third post, remains to be seen.

As for the code, it is as simple as it can be. But if anyone can benefit anything from seeing this, it’s all good.

public partial class MainWindow : Window
    {
        private BackgroundWorker worker;

        public MainWindow()
        {
            InitializeComponent();

            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(OnWorkerDoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnWorkerCompleted);
            worker.ProgressChanged += new ProgressChangedEventHandler(OnWorkerProgressChanged);
            worker.WorkerReportsProgress = true;
            progressBar.Minimum = 0;
            progressBar.Maximum = int.MaxValue;
        }

        private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            textBlock.Text = "Complete";
        }

        private void OnButtonClick(object sender, RoutedEventArgs e)
        {
            if (!worker.IsBusy)
            {
                worker.RunWorkerAsync();
            }
        }

        private void OnWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            textBlock.Text = e.ProgressPercentage.ToString();
            progressBar.Value = e.ProgressPercentage;
        }

        private void OnWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            int check = int.MaxValue / 100;
            for (int i = 1; i < int.MaxValue; i++)
            {
                if (i % check == 0)
                {
                    worker.ReportProgress(i);
                }
            }
        }
    }

And the XAML:

<Window 
    x:Class="BackgroundWorkerTest.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" 
    Height="350" Width="525">
    <StackPanel>
        <TextBlock 
            x:Name="textBlock" 
            Text="" 
            HorizontalAlignment="Center" 
            VerticalAlignment="Center" 
            FontSize="20" 
            FontWeight="Bold"/>
        <Button 
            Content="Start" 
            Height="23" 
            Name="startButton" 
            Width="75" 
            Click="OnButtonClick"/>
        <ProgressBar 
            x:Name="progressBar" 
            Height="30"/>
    </StackPanel>
</Window>