Sunday, November 6, 2011

WPF: TabControl and DataTemplate

This example show how to use DataTemplate to render TabControl pages from a collection of objects based on the data type of each object. This code can be used to create a simple tabbed shell for an MVVM application.

Model Views

The MainViewModel is the application's shell view model. It instantiates the tab page view models collection that shall be rendered by the TabControl on the MainView.

namespace TabControlDataTemplate
{
   public class MainViewModel
   {
      private TabPageViewModel[] pages;
 
      public TabPageViewModel[] Pages
      {
         get
         {
            if (this.pages == null)
            {
               this.pages = new TabPageViewModel[]
               {
                  new FooPageViewModel(), 
                  new BarPageViewModel() };
               }
            }
            return this.pages;
         }
      }
   }
}
Now we'll create an ancestor class for our tab page view models. I called it TabPageViewModel.
namespace TabControlDataTemplate
{
   public abstract class TabPageViewModel
   {
      public virtual string DisplayName { get; protected set; }
   }
}
There will be two sample model views, named FooPageModelView and BarPageModelView
namespace TabControlDataTemplate
{
   public class FooPageViewModel : TabPageViewModel
   {
      public string Message
      {
         get { return "FOO Content" }
      }
      public override string DisplayName
      {
         get { return "Foo Header"; }
      }
   }
}
namespace TabControlDataTemplate
{
   public class BarPageViewModel : TabPageViewModel
   {
      private string message;
       
      public BarPageViewModel()
      {
         this.message = "BAR Content";
      }
 
      public string Message
      {
         get { return this.message; }
      }
       
      public override string DisplayName
      {
         get { return "BAR Header"; }
         protected set { base.DisplayName = value; }
      }
   }
}

Page Views

The page views are very simple. There is only one TextArea which binds its Text property to the Message property of each view model.
<UserControl 
   HorizontlAlignment="Stretch" VerticalAlignment="Stretch"
   x:Class="TabControlDataTemplate.FooPageView"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
   <Grid>
      <TextBlock FontSize="16" ForeGround="Red" MinWidth="100" 
      Text="{Binding Path=Message}" />
   </Grid>
</UserControl>

<UserControl 
   HorizontlAlignment="Stretch" VerticalAlignment="Stretch"
   x:Class="TabControlDataTemplate.BarPageView"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
   <Grid>
      <TextBlock FontSize="16" ForeGround="Blue" MinWidth="100"
      Text="{Binding Path=Message}" />
   <Grid>
<UserControl>

Application

The application object is responsible for initializing the MainView and the MainViewModel and bind them by setting the DataContext property of the MainView to the MainViewModel. Finally, it displays the window.
namespace TabControlDataTemplate
{
   using System.Windows;
   ///
   /// Interaction logic for App.xaml
   ///
   public partial class App : Application
   {
      protected override void OnStartup(StartupEventArgs e)
      {
         base.OnStartup(e);
          
         MainWindow mainView = new MainWindow();
         MainViewModel mainViewModel = new MainViewModel();
         mainView.DataContext = mainViewModel;
         mainView.Show();
      }
   }
}

MainView

<Window x:Class="TabControlDataTemplate.MainWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml\"
   Title="TabControl and DataTemplate Example - YShDev" 
   Height="150" Width="350"
   WindowStartupLocation="CenterScreen">
    
   <Window.Resources>
      <ResourceDictionary Source="MainViewRD.xaml" />
   <Window.Resources>
   <DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
            <TabControl ItemsSource="{Binding Pages}">
            </TabControl>
        </DockPanel>
<Window>   

You can see that the ItemSource of the TabControl is set to the Pages property of the DataContext object. The TabControl has two templates: ItemTemplate for pages headers and ContentTemplate for pages contents. When the WPF renderer renders the TabControl, it traverses every page model in the Pages collection and foreach page model it creates a TabPage and sets its header text to the DisplayName of the page model. But what about the ContentTemplate of the TabControl? If you look closely, you can see that no ContentTemplate is mentioned in the TabControl's XAML. How can it be? When the WPF renderer tries to render the contents of a page model, since there is no explicit template given, it will search its database for a proper template by the page type. You can see in the MainView XAML code, that a ResourceDictionary is included in the window resources. Let's see what it contains:
<ResourceDictionary
   xmlns:my="clr-namespace:TabControlDataTemplate"   
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml\" 
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
   <DataTemplate DataType="{x:Type my:BarPageViewModel}"> 
      <my:BarPageView> 
   </DataTemplate>
   <DataTemplate DataType={x:Type my:FooPageViewModel}">
      <my:FooPageView>>  
   </DataTemplate>
<ResourceDictionary>
You can see that the resource dictionary contains matches between a tab page view models and their corresponding page views. This is how the WPF renderer knows what template to choose for each page model view type. So, like I said in the begining, this example can be used as a simple shell for a tabbed application. I order to add more functionality, all you have to do is create a TabPageViewModel and page view and add a type to DataTemplate match in the resource dictionary.

Tuesday, September 14, 2010

Fix: "Configuration system failed to initialize" error on Visual Studio Express 2010

Yesterday, after copying a project to different location, I got this annoying error:

Configuration system failed to initialize.



After digging in the Exception Text, I found that the problem is in the user settings saved file.
These files are stored in the directory  
C:\Documents and Settings\user-name\Local Settings\Application Data\application-name

Deleting the directory fixed the problem, although saved user settings were deleted too.



Wednesday, August 4, 2010

Windows XP: Automatic account login

To enable or disable autoamtic account login in Windows XP:

1. Goto Start->Run.
2. Type "control userpasswords2" and press OK.
3. To disable automatic account login, check "User must enter a user name and password to user this computer." or uncheck it to enable automatic login.
4. Press OK.

Another way to disable automatic login:
1. Goto Start->Run.
2. Type "rundll32 netplwiz.dll,ClearAutoLogon" and press OK.

Thursday, April 22, 2010

VS.Net 2010 C# Code Snippet for throwing ArgumentNullException

When you receive an argument in a public method, you often need to throw an ArgumentNullException if its value is null. Currently there is no snippet in VS.Net 2010 to do that. Luckily, it is very easy to construct one:
The snippet above will insert the text
if (argName == null)
   throw new ArgumentNullException("argName");
into the current cusror position, when you write the snippet title xnull. Changing the argument name in one place, will change it in the second place too. In order to use it, create a file named xnull.snippet in one of the snippets subdirectories (usually, C:\Program Files\Microsoft Visual Studio 10.0\VC#\Snippets\1033\*) and paste the code into it.

Monday, March 22, 2010

How to add WPF Control Library Template to Visual Studio 2008 Express Edition

Taken from dotUpdate

Visual C# Express 2008 does not come with a project template for creating custom controls (.dll files). You can create your own template by following these steps:

1. Open Visual C# Express 2008.
2. Create New WPF Application with name: MyLibrary
3. Project > Add User Control
4. In Solution Explorer, Delete Window1.xaml and App.xaml (the corresponding .cs files should be deleted by this step)
5. Project > MyLibrary Properties > Application > Output Type = Class Library
6. Build Solution. There should be no errors and the bin/Release folder should have a MyLibrary.dll file
7. File > Export Template
8. Select Project Template then Next
9. Under Template Options change name to: WPF Control Library and Description: Create WPF Control .dll

The next time you open VS, you will find your newly created template ready to use.
Hello World!
This is my first Blog. Its main purpose is to store programming ideas and solutions I find on the net. Maybe even write some words of my own - I am not a man of words.
A little about me -
I am 33 years old, from Israel, working as a programmer in a little company.
We develope a new satellite tracking system for yachtes. I program in C#, C and SPIN - which is a programming interpreted language for Propeller processors.
I live with my girlfriend, not married yet, and with a dog named Una.
Well, thats it for now. I gotta go home. Bye