Ok, I'll try to provide a detailed answer, so this might get a little long, but bear with me.
First of all, if you're working with WPF, it is extremely important to leave behind anything you might be used to from "traditional" technologies such as winforms, and instead, understand and embrace The WPF Mentality.
in WPF, you don't "inherit from a base Window" in order to define the application's functionality, because The UI is NOT the application. The UI is just a nice way to have the end user interact with the application.
Instead, the application's interaction logic and functionality is manifested in some classes called ViewModels, which basically define all the contents and actions to be exposed by the UI.
After that, the UI is "connected" to the ViewModel via DataBinding.
This allows a huge level of reusability, scalability, maintainability, and even testability, because it actually completely separates UI from application/business logic and data.
So, these are the basic steps you would need to perform what you're describing, in WPF:
1 - Basic DataBinding support
since Two-way databinding in WPF requires Property Change Notification, the first thing we do is to create a base "bindable" class to support this basic functionality:
public class Bindable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
2 - Parent / Child ViewModels
Your application will require that the parent Window functionality be defined in a base class, while leaving the opportunity to inherit and customize the "child" part for each specific Window type, therefore we will create a base generic ViewModel to define all the common reusable functionality, while also defining a generic "Child" property:
public class ViewModelBase<T>: Bindable where T: Bindable
{
private T _child;
public T Child
{
get { return _child; }
set
{
_child = value;
OnPropertyChanged("Child");
}
}
}
- Notice how this is structure is suitable for your requirement:

- Also notice the basic example of a property with Change Notification, where you raise the
OnPropertyChanged()
in the property setter.
3 - Commands and Buttons
after that, you need to define the basic functionality for your Toolbar Button
s.
Instead of using the traditional Click event handler approach, these are manifested as Commands
that are decoupled from the UI, and whose execution logic is defined in the ViewModel rather than code behind.
For that purpose, we will define a basic reusable DelegateCommand
:
//Dead-simple implementation of ICommand
//Serves as an abstraction of Actions performed by the user via interaction with the UI (for instance, Button Click)
public class Command : ICommand
{
public Action Action { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}
Using this command class, we can now define all of our commands in the main ViewModel:
public class ViewModelBase<T>: Bindable where T: Bindable
{
private T _child;
public T Child...
public Command SearchCommand { get; private set; }
public Command NewCommand { get; private set; }
public Command EditCommand { get; private set; }
public Command ViewCommand { get; private set; }
public Command DeleteCommand { get; private set; }
}
These commands require an Action
delegate to indicate what will be performed when they are executed (when the Buttons
are clicked). Notice how we begin to actually define functionality, while we haven't even touched any part of the UI yet.
This is how you define the actions:
public class ViewModelBase<T>: Bindable where T: Bindable
{
//... all of the above.
//in the constructor:
public ViewModelBase()
{
SearchCommand = new Command(Search);
NewCommand = new Command(New);
//... And so on...
}
//Actions for the commands:
private void Search()
{
//... your search logic here.
}
private void New()
{
//... your New logic here...
}
//... And so on...
}
Enabling and Disabling Commands: You also mentioned that there will be a DataGrid
in the second tab, which will contain the search results, and that will enable/disable some of the buttons.
Notice that the Command
class defines and IsEnabled
property, which in turn raises the System.Windows.ICommand.CanExecuteChanged
event. WPF's commanding engine is capable of listening to this event, and enabling/disabling UI elements accordingly. Therefore, at any time in your application, you can toggle your buttons' state by doing:
NewCommand.IsEnabled = false; //disables the "New" Button
DeleteCommand.IsEnabled = true; //enables the "Delete" Button
4 - The DataGrid
This is the most interesting part.
In abstraction, DataGrid
, ListBox
, ListView
, ComboBox
, and all ItemsControl
s in WPF are controls that display items from a collection, eventually allowing the user to select one or more items.
In WPF, it is common to use the ObservableCollection<T>
, because it is a specialized collection type that raises events whenever items are added / removed / cleared from it. WPF's binding engine listens for such events and updates the UI accordingly.
Since we do not know up front what type of items will be shown in the DataGrid, and our ultimate goal is reusability, it is time to add another generic type parameter to our parent ViewModelBase
:
public class ViewModelBase<T, TItems>: Bindable where T: Bindable where TItems: class
{
//... All of the above...
}
Now we can define our Collection property:
public class ViewModelBase<T, TItems>: Bindable where T: Bindable where TItems: class
{
//... All of the above...
private ObservableCollection<TItems> _searchResults;
public ObservableCollection<TItems> SearchResults
{
get { return _searchResults; }
private set
{
_searchResults = value;
OnPropertyChanged("SearchResults");
}
}
We also need one property to store the selected item, which in turn will cause the Button
s to be enabled when an item is selected, and disabled when the selection is cleared:
private TItems _selectedItem;
public TItems SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
NewCommand.IsEnabled = value != null;
ViewCommand.IsEnabled = value != null;
DeleteCommand.IsEnabled = value != null;
}
}
}
At this point, you do realize to what extent we're actually "programming the UI" without even touching the UI. The above code will make the New, View and Delete Button
s enabled when an item is selected in the DataGrid
, and disabled when it is not. However all this functionality is completely decoupled from the UI.
5 - Child widgets (TabItem 1)
At this point we have a completely generic, reusable, base functionality that can be adapted to work with any Item type, also allowing any Child "widget" to be placed inside the parent one.
Also notice how this is a thousand times better than the traditional winforms approach, because:
- 1 - It is Unit Testable.
- 2 - the Items collection for the DataGrid is actually generic, which means that you get strongly-typed, compile-time verifiable, Intellisense-enabled coding (as opposed to just a
List<object>
or the like) without the need for casting or any other poor coding practices.
- 3 - the UI has not been even defined, which means that you get a huge opportunity for customization. The code we have written so far
will work
, regardless if the UI contains a DataGrid
, or a 3D projection of a flying pink elephant.
Now, it's just a matter of creating the Child widgets that will be placed in each of the 30 different Windows
Say, for example, that you need to show something like this:

The first thing you need to do is to define the ViewModel
for that, which would be something like:
public class PersonViewModel: Bindable
{
public string LastName { get; set; }
public string FirstName { get; set; }
public string City { get; set; }
public string PostalCode { get;set; }
}
Notice that we're NOT raising property change notifications here, because we don't really need Two-way DataBinding. OneWay
will suffice. Still, we inherit from Bindable
because the Parent ViewModel has a type constraint in the T
type parameter.
Then, you need to Create a UserControl:
<UserControl x:Class="WpfApplication1.PersonView"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label HorizontalAlignment="Right" Grid.Row="0" Grid.Column="0" Content="Last Name:" />
<Label HorizontalAlignment="Right" Grid.Row="1" Grid.Column="0" Content="First Name:" />
<Label HorizontalAlignment="Right" Grid.Row="2" Grid.Column="0" Content="City:" />
<Label HorizontalAlignment="Right" Grid.Row="3" Grid.Column="0" Content="Postal Code:" />
<TextBox Grid.Row="0" Grid.Column="1" Margin="2" Text="{Binding LastName}"/>
<TextBox Grid.Row="1" Grid.Column="1" Margin="2" Text="{Binding FirstName}"/>
<TextBox Grid.Row="2" Grid.Column="1" Margin="2" Text="{Binding City}"/>
<TextBox Grid.Row="3" Grid.Column="1" Margin="2" Text="{Binding PostalCode}"/>
</Grid>
</UserControl>
Notice how the last thing you actually do in WPF is to create the UI.
6 - DataTemplates
In order to let WPF know "which View to use for which ViewModel" you can define DataTemplate
s at the application-level resources, simply open the App.xaml
and add these inside Application.Resources
:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<DataTemplate DataType="{x:Type local:PersonViewModel}">
<local:PersonView/>
</DataTemplate>
</Application.Resources>
Notice that you will need to Import your Namespaces in order to be able to reference your classes in XAML.
Ok, I have to go now, go ahead and try these steps. I'll finish my post when I get some more time.