Archives for : TPL

Part One: Starting with MVVM
Part Two: The MVVM solution structure and basic framework
Part Three: Base Classes

I want to go over the main view (SamplerView), view model (SamplerViewModel) and model (Sampler) classes next. I’ve gone over the ItemsControl that populates the TPL samples but I wanted to cover the other MVVM aspects as well as what isn’t MVVM here. There are two other primary MVVM components to this. One is the image chooser, the other is the log that is populated as the samples run. Remember that the DataContext of the SamplerView is a SamplerViewModel. This means that any binding is done on this object.

The Image Chooser:
Near the top of SamplerView is a TextBox that contains the path to the image that is used when a sample needs an image. Next to that is a Button that opens an OpenFileDialog to choose an image.

<Border Grid.Row="0" Grid.ColumnSpan="2" >
	<Grid>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*" />
			<ColumnDefinition Width="Auto" />
		</Grid.ColumnDefinitions>
		<TextBox Grid.Column="0" Grid.Row="0" VerticalAlignment="Bottom" Text="{Binding Sampler.ImagePath, Mode=TwoWay}" />
		<Button Grid.Column="1" Content="Change Image">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="Click">
					<ei:CallMethodAction TargetObject="{Binding}" MethodName="ChangeImagePath" />
				</i:EventTrigger>
			</i:Interaction.Triggers>
		</Button>
	</Grid>
</Border>

Like the sample view model, the Sampler model is directly exposed in the SamplerViewModel. As such the binding of the TextBox is to the ImagePath property of the Sampler model. The mode is set to two-way so that if a user wishes they could just paste a path into the TextBox directly. What’s new here is the Interaction.Trigger. This allows you to define a method to call for the EventTrigger, in this case the “Click” event.

Here is the SamplerViewModel.ChangeImagePath():

public void ChangeImagePath()
{
	OpenFileDialog ofd = new OpenFileDialog();
	ofd.CheckFileExists = true;
	ofd.CheckPathExists = true;
	ofd.Multiselect = false;
	if (ofd.ShowDialog() == true)
	{
		Sampler.ImagePath = ofd.FileName;
	}
}

This has the standard OpenFileDialog code but when the user chooses a file, that path is set to the model’s ImagePath. Since the ImagePath is bound to the TextBox in the view, setting the path will update the TextBox to the correct path. Setting this image path has the side-effect that the images within the application get updated.

Sampler.ImagePath:

string imagePath = "";
public string ImagePath
{
	get { return this.imagePath; }
	set
	{
		if (this.imagePath != value)
		{
			this.ValidateProperty("ImagePath", value);
			this.imagePath = value;
			this.RaisePropertyChanged("ImagePath");
			SetImageSources();
		}
	}
}

There are three important aspects to this.

One is the ValidateProperty:

protected override void ValidateProperty(string propertyName, object value)
{
	if (propertyName == "ImagePath")
	{
		var errors = new List();
		string path = value as string;
		if (string.IsNullOrEmpty(path))
		{
			errors.Add("Image path must be set.");
		}
		else if (!File.Exists(path))
		{
			errors.Add("The image indicated does not exist.");
		}

		this.ErrorsContainer.SetErrors(propertyName, errors);
	}
	else
	{
		base.ValidateProperty(propertyName, value);
	}
}

When ValidateProperty is called it verifies that there is an image path and that the file exists. If either of these are not true than an error is added to the error container in the base domain object that allows the application to provide feedback to the user that there is an error.

Two is the RaisePropertyChanged event. This is pretty standard when binding as this notifies any observers that are bound to this object that the property has changed, that way they can change the value as they need to.

Finally is the Sampler.SetImageSources():

private void SetImageSources()
{
	if(!File.Exists(ImagePath))
		return;

	BitmapImage sourceBitmapImage = new BitmapImage();
	sourceBitmapImage.BeginInit();
	sourceBitmapImage.UriSource = new Uri(ImagePath);
	sourceBitmapImage.EndInit();
	SourceImage = sourceBitmapImage;

	ResetDestinationImage();
}

public void ResetDestinationImage()
{
	if(!File.Exists(ImagePath))
		return;

	if (Application.Current.Dispatcher.Thread != Thread.CurrentThread)
	{ 
		Application.Current.Dispatcher.Invoke(() => 
		{
			ResetDestinationImage();
		});
		return;
	}
	BitmapImage destBitmapImage = new BitmapImage();
	destBitmapImage.BeginInit();
	destBitmapImage.UriSource = new Uri(ImagePath);
	destBitmapImage.EndInit();
	DestinationImage = destBitmapImage;
}

This is the side-effect of updating the image path. It causes the two images in the view, which are bound to the SourceImage and DestinationImage, to get updated. Since the ValidateProperty takes care of providing the user with the feedback that a file may not exists we don’t have to do any error handling beyond just making sure the image exists.

The last MVVM component of interest is the Submit button.

<Button  Content="Submit" Height="23" IsEnabled="{Binding CanSubmit}" Margin="5"  HorizontalAlignment="Right" Width="75">
	<i:Interaction.Triggers>
		<i:EventTrigger EventName="Click">
			<ei:CallMethodAction TargetObject="{Binding}" MethodName="Submit"/>
		</i:EventTrigger>
	</i:Interaction.Triggers>
</Button>

Like the ChangeImage button above, this is bound to a method in the SamplerModelView.

SamplerModelView.Submit()

public async void Submit()
{
	CurrentState = "Running";
	await Sampler.RunSamples();
	CurrentState = "Completed";
}

This method is run with async/await so the UI isn’t locked when the samples are run. I’ll discuss async/await in detail later in this series. It just runs the method to run the samples in the Sampler model. In the next post I’ll go more into depth on running samples.

Finally I want to discuss what is not MVVM here. The images are defined in SamplerView as:

<Border MinHeight="100" Grid.Row="1" Grid.Column="0" BorderBrush="Black" BorderThickness="2">
	<Image ClipToBounds="True" Source="{Binding Sampler.SourceImage}">
		<Image.RenderTransform>
			<TransformGroup>
				<ScaleTransform x:Name="imgScaleTransformSource" ScaleX="1" ScaleY="1" />
				<TranslateTransform x:Name="imgTranslateTransformSource"  />
			</TransformGroup>
		</Image.RenderTransform>
	</Image>
</Border>
<Border MinHeight="100" Grid.Row="1" Grid.Column="1" BorderBrush="Black" BorderThickness="2">
	<Image ClipToBounds="True" Source="{Binding Sampler.DestinationImage}">
		<Image.RenderTransform>
			<TransformGroup>
				<ScaleTransform x:Name="imgScaleTransformDest" ScaleX="1" ScaleY="1" />
				<TranslateTransform x:Name="imgTranslateTransformDest"  />
			</TransformGroup>
		</Image.RenderTransform>
	</Image>
</Border>
<StackPanel Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom">
	<RepeatButton x:Name="btnPlus" Width="25" Margin="5" Click="btnPlus_Click">+</RepeatButton>
	<RepeatButton x:Name="btnMinus" Width="25" Margin="5" Click="btnMinus_Click">-</RepeatButton>
	<RepeatButton x:Name="btnUp" Width="25" Margin="5" Click="btnUp_Click">?</RepeatButton>
	<StackPanel Orientation="Horizontal">
		<RepeatButton x:Name="btnLeft" Width="25" Margin="5" Click="btnLeft_Click">?</RepeatButton>
		<RepeatButton x:Name="btnRight" Width="25" Margin="5" Click="btnRight_Click">?</RepeatButton>
	</StackPanel>
	<RepeatButton x:Name="btnDown" Width="25" Margin="5" Click="btnDown_Click">?</RepeatButton>
</StackPanel>

These are bound to the SourceImage and DestinationImage properties of the Sampler model, standard MVVM stuff. What’s not MVVM are the repeat buttons that move/zoom the images around. I opted for this approach, using regular code-behind, because the moving/zooming of the images doesn’t really have anything to do with our models. It doesn’t have anything to do with running samples or any data associated with the samples. Because of this it didn’t make sense to bind the buttons that move the images using an event trigger to methods in the SamplerViewModel. In this case it would have convoluted our ModelViews just for the sake of using MVVM. This is what I meant in the first post where I referred to this as a composite application. MVVM for the sake of MVVM is counter-intuitive and counter-productive. The purpose of our ModelViews and Models is to focus on running TPL samples and giving the results of those samples back to the user.

Up next is running samples, getting the data to the samples and getting the results back from the samples.

Thanks,
Brian

The Task Parallel Library Sampler – Part Three: Base Classes

Part One: Starting with MVVM
Part Two: The MVVM solution structure and basic framework

Let’s take a look at SampleView, SampleViewModel and the base Sample class all of our models extend from so we can get a better understanding of how this works.

In the first set of samples included with the code the only controls we need are a checkbox to identify which samples should be run. As we expand the capabilities of the samples to demonstrate more functionality of the TPL we’ll need to increase that. For now, however, you’ll see that the SampleView.xaml is really as simple as can be.

<UserControl x:Class="TPLSamples.Views.SampleView"
             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" 
             >
    <Grid>
        <CheckBox IsChecked="{Binding Sample.IsEnabled, Mode=TwoWay}" Content="{Binding Sample.SampleName}" ToolTip="{Binding Sample.SampleName}" />
    </Grid>
</UserControl>

We have the boilerplate WPF code with the standard references and <Grid> as well as our checkbox. The code-behind is the boilerplate created code with nothing added. Important here is how the checkbox data gets populated. Remember that in the ItemsControl of the SamplerView, each ViewModel that is added to the observable list the framework will bind to a view which, conversely, has as it’s data context the view model it’s bound to. Additionally, when constructing the ViewModels, they take an instance of a sample. This is how we get our View <-> ViewModel <-> Model. Let’s take a look at our SampleViewModel so we can understand what is happening in it.

public class SampleViewModel : NotificationObject
{
	private readonly Sample sample;
	public Sample Sample
	{
		get { return sample; }
	}

	public SampleViewModel(Sample sample)
	{
		if(sample == null)
			throw new ArgumentNullException("Sample is null");

		this.sample = sample;
	}
}

That’s it. It just exposes our sample directly. Looking back at the xaml for our view, you can see that the content of the checkbox is bound to “Sample.SampleName”. Since the view has the view model as it’s data context that means we are bind the content of the checkbox to the name of the sample model. Note that there are instances when you’re not dealing in clear-cut models that it makes sense to simply incorporate the aspects of a model into the view model. It also makes sense when you want to limit the exposure of your model that you may not expose the model directly but may wrap properties in calls to the model (for example if you need to handle multi-threaded scenarios where you have to lock on properties because there may be only one instance of the model). In this case we are dealing with very obvious models and there is no reason here to limit the exposure of the model so the model is just exposed directly for binding.

One of the critical aspects of this binding is the IsChecked property. You can see that we have a “Mode=TwoWay” parameter on the binding. This means that not only does the value of the IsChecked property get set from the model, but if the value of the IsChecked property changes it will update the model. In terms of the purest implementation of MVVM this is what is problematic with exposing the model directly. The view model is supposed to act as a controller for the interface from the view and to the model. Exposing the model directly means the the view model is no longer controlling the model but instead the view is. With our samples we could have rolled the model into the model view. To simplify what is already a fairly complex set of code for something so simple as well as showing how view models work in MVVM I created view models but exposed the model.

Finally we need to look at the Sample base model that all samples derive from.

public abstract class Sample : DomainObject
{
	public bool IsEnabled { get; set; }
	public string Results { get; set; }
	public TimeSpan RunTime { get; set; }

	public abstract string SampleName { get; }
	public abstract bool ImageRequired { get; }
	public abstract void Run(System.Drawing.Bitmap bmp = null, Action<string> updateLog = null);
}

The vast majority of my samples need a consistent set of data to work on so we can compare times of execution. The easiest way to do this is just take an image on the Run method. This way, if we want to test our samples under different loads we can just use a bigger or smaller image. The Results property is unused and will be removed from future sets of the code. Originally I had envisioned that as the sample needed to display something to the user it would just update the Results property. Obviously this doesn’t work as the user doesn’t get immediate feedback but instead will only get updated when the sample completes its run. I replaced this with an Action that takes a string and in my SamplerViewModel the action updates the results in the Sampler model. This way the user gets continuous feedback from the sample as it updates.

As the samples run, if they use an image they modify that image. The ImageRequired property is important because it is potentially expensive on the front-end to reload the image if a sample doesn’t need it. Especially in cases where a large image is used.

And that’s it for our Sample model. I realize that the initial set-up of MVVM can be complex. Even some of the implementation of the View through ViewModel to Model work-arounds that have be put into place to handle things that are fairly simple when working with code-behind can be downright ugly. The pay-off is that supporting long-term maintenance should be a lot easier. That’s the tough part to get across to a lot of people. You will have a lot more overhead when initally spinning up an MVVM solution but in the end you will find that it is all worth it during maintenance as you fix bugs and add features. If nothing else then because of how cleanly separate your tiers are.

Up next is a more in-depth explanation of our SamplerView, SamplerViewModel and Sampler model.

Thanks,
Brian

In the previous post I wrote a brief intro to MVVM along with some pros and cons. Next up in this series is an explanation of the layout of the solution and some of the rudimentary framework. Don’t forget you can download all the code here.

TPLSamplesSolutionLayout

In the image above you can see the layout of the solution with Views, ViewModels and Models. MainWindow.xaml has our SamplerView.xaml. SamplerView.xaml has as it’s DataContext a SamplerViewModel.

<UserControl.DataContext>
	<ViewModels:SamplerViewModel />
</UserControl.DataContext>

Looking at the constructor of the SamplerViewModel:

public SamplerViewModel()
{
	Samples = new ObservableCollection();
	Sampler = new Sampler();
	Sampler.Samples.Add(new LineSample());
	Sampler.Samples.Add(new LineParallelSample());
	Sampler.Samples.Add(new GreyScaleSample());
	Sampler.Samples.Add(new GreyScaleParallelSample());
	Sampler.Samples.Add(new GreyScaleDoubleParallelSample());
	ResetSampler();
}

private void ResetSampler()
{
	this.Samples.Clear();
	foreach (var sample in Sampler.Samples)
	{
		this.Samples.Add(CreateSampleViewModel(sample));
	}
}

private SampleViewModel CreateSampleViewModel(Sample sample)
{
	SampleViewModel vm = SamplerViewModelFactory.GetViewModelForSample(sample);
	return vm;
}
  1. we set up our observable collection for our sample view models
  2. construct our Sampler model
  3. construct all our samples
  4. using a Factory pattern get the view model for each of our samples

Now let’s take a step back to the SamplerView.xaml. If you look at the code you’ll see there is a lot going on in the xaml I’m not going to cover here. There are, however, two very important parts to the SamplerView.xaml that must be covered. The first is the DataContext mentioned above that creates a SamplerViewModel. The other is the ItemsControl that binds to the Samples collection.

<ItemsControl Grid.Row="1" IsTabStop="False" ItemsSource="{Binding Samples}">
	<ItemsControl.ItemsPanel>
		<ItemsPanelTemplate>
			<WrapPanel Orientation="Horizontal" IsItemsHost="True" Utility:MarginUtilities.Margin="5" />
		</ItemsPanelTemplate>
	</ItemsControl.ItemsPanel>
	<ItemsControl.ItemTemplate>
		<DataTemplate>
			<Views:SampleView DataContext="{Binding}" />
		</DataTemplate>
	</ItemsControl.ItemTemplate>
</ItemsControl>

When you set the data context in WPF you are exposing that object and it’s properties to binding within that control. In the code above you can see we are binding the ItemsControl to the Samples property of our view model which is an observable collection that contains all of the view models for the samples. In addition to that we are setting the ItemTemplate of the ItemsControl to a view entitled “SampleView” basically saying, “Anything we bind to will use the SampleView as our item” (note that this will be changed later to allow for more SampleView types). It’s this initial setup that can be rather complex but once the framework is in place adding new models (in this case samples) to the application is pretty straight forward.

So let’s walk through from the beginning how we get all of our samples into the application:

  1. MainWindow.xaml contains a user control, “SamplerView”
  2. SamplerView.xaml has as its data context a “SamplerViewModel”
  3. SamplerViewModel.cs has a “Sampler” model that we add all the samples to
  4. SamplerViewModel.cs has a list of “SampleViewModel”, called “Samples” with a factory that maps sample models to sample model views
  5. SamplerViewModel.Samples binds to the ItemsControl in “SamplerView”
  6. SamplerView.xaml ItemsControl has an ItemTemplate that maps those samples to “SampleView”

You’ll notice that I add all the samples to the Sampler model in the SamplerViewModel and not in the Sampler model constructor. Why do I do this? It’s really just a matter of making it easier to have a single place to go to for adding samples to the application. I’d like to keep to the SamplerViewModel as much as possible since that contains the Factory for mapping those samples to their respective view models.

In the next post I’ll go over the SampleView, SampleViewModel and the base Sample class all the samples extend from.

Thanks,
Brian

The Task Parallel Library Sampler – Part One: Starting with MVVM

About a year ago I did a session here at work going over the Task Parallel Library (TPL), it’s benefits and where it works and doesn’t work. To illustrate a lot of the TPL I wrote a simple WPF application with standard code-behind that contained a ton of code samples that demonstrated the concepts I was discussing. I’ve been thinking of doing a basic introduction to MVVM as well as expanding on some of the posts I’ve written here regarding the TPL and finally decided to just write a TPL Sampler application utilizing MVVM. The application could be generalized more than it is but since I wanted to work specifically with the TPL there are some choices I made that may seem a bit odd in general but work given the scope of the application.

So what is MVVM? MVVM stands for Model-View-View Model and derives from the MVC pattern. Wikipedia has a pretty good write up of it that goes fairly in depth into the theory of the pattern. MVVM grew from MVC to take advantage of the data binding abilities of WPF. As a generalization you can consider the Model, whether it’s a data object derived from a database abstraction layer or, in my case, a bit of sample code to run, the object that you are working with. The View is all the GUI components. Finally, the View Model works as the controller to manage interactions between the Model and the View.

So what are the advantages of MVVM? The biggest is a separation of concerns. By this I mean that your data layer can cleanly exist separate from your GUI. This allows for easy integration of unit test on the models and view models as well as easily allowing development to proceed in different areas of the application. All standard advantages to an n-tiered approach to application development apply.

So what are the disadvantages of MVVM? It can get complex pretty easy. There are so many things we take advantage of being able to do right in code-behind that aren’t “proper” within MVVM. While you can do anything in MVVM that you can do in regular code-behind, sometimes you have to go to pretty great lengths to do so. As an example, specifying custom click-events on leaf nodes in a tree where each branch has an arbitrary depth. This may seem contrived but this is a problem I’ve had to solve.

I would argue that MVVM isn’t the end-all, be-all solution to application development in WPF and I think most people would agree with me. I would also argue that you can blend both MVVM and the standard observer pattern with code-behind (which I’ll discuss when going over the sample application. It’s called a composite application and happens fairly often).

It’s an invaluable tool that can greatly ease development and maintenance but it is just one tool. That being said, I can’t think of many application scenarios where it isn’t applicable. Applications are generally data driven. For the most part we’re getting data, manipulating it and then doing something with the result. So why not have a framework in place that takes advantage of the binding that WPF presents?

The sample application (which is here and linked below) is probably one of the simplest MVVM projects you will find. It’s even a bit simpler than Microsoft’s MVVM QuickStart sample application.

Starting with References:
The following references were added beyond the standard references VS adds when creating a default project.
Microsoft.Expression.Interactions
Microsoft.Practices.Prism
Microsoft.Practices.Prism.Interactivity
Microsoft.Practices.ServiceLocation
System.Windows.Interactivity

The Prism libraries are included in the lib directory of the project and are from the Microsoft patterns & practices: Prism site. The Prism libraries aren’t an MVVM framework unto themselves but make utilizing a general MVVM approach easier. I had to add the following redirect in my app.config to compile without warnings because of how the Prism libraries are compiled:

<runtime>
	<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
		<dependentAssembly>
			<assemblyIdentity name="System.Windows.Interactivity" publicKeyToken="31bf3856ad364e35" />
			<bindingRedirect oldVersion="4.0.0.0" newVersion="4.5.0.0"/>
		</dependentAssembly>
	</assemblyBinding>
</runtime>

The ServiceLocation reference is needed by the Prism libs. It too is included in the lib directory but can also be downloaded from Microsoft patterns & practices: CommonServiceLocator

If you look at the MainWindow.xaml you’ll see that the only thing in it is:

<Grid>
    <Views:SamplerView />
</Grid>

The SamplerView contains all our GUI elements for enabling/disabling samples, adding an image for samples to use and the buttons for running the sample. In the next post I’ll work on explaining the SamplerView in more detail.

Thanks,
Brian

TPLSamples Application