Get in touch
Thank you
We will get back to you as soon as possible
.pdf, .docx, .odt, .rtf, .txt, .pptx (max size 5 MB)

26.11.2009

5 min read

WPF Model-View Application. Tricky things.

This article from our series of articles about WPF development and MVVM pattern tells about some tricky things when creating WPF MVVM Application. First of all it is “close the window” (you are not allowed to access the window object in WPF MVVM). Second is how to link View and ViewModel. And last is how to make OnPropertyChanged method without string with name of property and add IntelliSense. So, let's start. First, if you don’t use MVVM pattern in your WPF development  yet, you definitely must install WPF Model-View-ViewModel Toolkit 0.1. It creates all you need for MVVM in WPF. And you will love this. (By the way, we can help you in this. If you are looking for reliable wpf outsourcing services check the section about our core expertise). In this article I’d like to tell how you can take in some tricky things when creating WPF MVVM Application.

WPF MVVM Application.

Part 1. Closing window.

First of all it is “close the window”. :-) Yep, it seems very simple but there is one thing: we are not allowed to access the window object. As a result almost each WPF forum has that question. A widespread answer is “you should write handler on code-behind”. Yes, of course you can but my opinion we should use just MVVM. As default MVVM template creates CloseCommand but it shuts down all application. So I suggest solving this task the following way. I created the new WPF Model-View application. After this I added to the ViewModelBase next command with event:

#region [ Fields ]

DelegateCommand _closeCommand;

#endregion

#region [ Constructor ]

protected ViewModelBase() 
{ 
}

#endregion

#region [ CloseCommand ]

public ICommand CloseCommand 
{ 
get 
{ 
if (_closeCommand == null) 
_closeCommand = new DelegateCommand(OnRequestClose);

return _closeCommand; 
} 
}

#endregion

#region [ RequestClose ]

public event EventHandler RequestClose;

void OnRequestClose() 
{ 
EventHandler handler = this.RequestClose; 
if (handler != null) 
handler(this, EventArgs.Empty); 
}

#endregion

  Let’s create simple UI. I created a simple SecondView window and model for it.

<Grid>
<StackPanel Margin="10">
<Label Margin="10" >It's second window!</Label>
<Button Margin="10" Content="Close me!" Command="{Binding CloseCommand}"/>
</StackPanel>
</Grid>

  (there is CloseCommand - it’s object from ViewModelBase). On MainView I added just one button and binded it to a command which opens a second window (I will create it then).

<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Command="{Binding ExitCommand}" Header="E_xit" InputGestureText="Ctrl-X" /> 
</MenuItem>
</Menu>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>

<Button Grid.Column="0" Margin="25" Content="Open second window!" Command="{Binding OpenSecondViewCommand}"/>
</Grid>
</DockPanel>

  This is the main goal. I added the following handler to MainViewModel.cs:

public ICommand OpenSecondViewCommand 
{ 
get 
{ 
if (_openSecondViewCommand == null) 
{ 
_openSecondViewCommand = new DelegateCommand(OpenSecondView); 
} 
return _openSecondViewCommand; 
} 
}

private void OpenSecondView() 
{ 
var model = new SecondViewModel(); 
var view = new SecondView();

EventHandler handler = null; 
handler = delegate 
{ 
model.RequestClose -= handler; 
view.Close(); 
}; 
model.RequestClose += handler;

view.DataContext = model; 
view.ShowDialog(); 
}

  That’s all! Our second window can close and we don’t break MVVM pattern.

Part 2. How to link model and view.

It’s maybe second widespread question. Let’s assume we have model in MainViewModel.cs and we’d like to display its view. We should start by adding the following code to MainViewModel.cs from our previous application:

private ViewModelBase _control; 
public ViewModelBase Control 
{ 
get { return _control; }
set 
{ 
_control = value; 
OnPropertyChanged("Control"); 
} 
} 
public ICommand FirstViewCommand 
{ 
get 
{ 
if (_firstCommand == null)
{ 
_firstCommand = new DelegateCommand(FirstView);
} 
return _firstCommand; 
} 
} 
public void FirstView()
{ 
Control = new FirstControlViewModel(); 
} 
public ICommand SecondViewCommand 
{ 
get 
{ 
if (_secondCommand == null)
{ 
_secondCommand = new DelegateCommand(SecondView);
} 
return _secondCommand; 
} 
} 
public void SecondView()
{ 
Control = new SecondControlViewModel(); 
}

  Here I added such property as base class of ViewModel and two commands which will display simple user controls. At MainView.xaml:

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>

<StackPanel Grid.Column="0">
<Button Margin="25" Content="Open second window!" Command="{Binding OpenSecondViewCommand}"/>
<Button Margin="5" Content="Open first control" Command="{Binding FirstViewCommand}"/>
<Button Margin="5" Content="Open second control" Command="{Binding SecondViewCommand}"/>
</StackPanel>

<ContentPresenter Grid.Column="1" Content="{Binding Control}"/>

</Grid>

  This ContentPresenter will display user control by binding. So after this I created two simple user controls (FirstControlView.xaml and SecondControl-View.xaml). They are very simple, so I didn’t add listening (you can see it in the source code). If you try to run this application now, you’ll get strange behavior! Control presenter showing error. The reason is we should add DataTemplate to resources. So let’s add it to xaml. DataTemplate make links between your model and view for this model.

<Window.Resources>
<!-- Allows a KeyBinding to be associated with a command defined in the View Model --> 
<c:CommandReference x:Key="ExitCommandReference" Command="{Binding ExitCommand}" />

<DataTemplate DataType="{x:Type ViewModels:FirstControlViewModel}">
<Views:FirstControlView />
</DataTemplate>

<DataTemplate DataType="{x:Type ViewModels:SecondControlViewModel}">
<Views:SecondControlView />
</DataTemplate>

</Window.Resources>

  There are links between first model and first user control and between second model and second user control. And now it works as we expect. Control presenter working correctly. And of course, it changes value (because we added OnPropertyChanged("Control");) There are links between first model and first user control and between second model and second user control. MVVM pattern is really good for creating our WPF application!

Part 3. OnPropertyChanged method witout string.

And last thing in this article! As you saw we added:

private ViewModelBase _control; 
public ViewModelBase Control 
{ 
get { return _control; }
set 
{ 
_control = value; 
OnPropertyChanged("Control"); 
} 
}

  Yes, it works… but I absolutely hate this syntax!!! First, I have to write property name as string but I really prefer IntelliSense! And maybe a much stronger reason is that we are destined to make a mistake here (or forget change the new name) after big code refactoring. We won’t be able to catch these mistakes during the build and they will be very difficult to find and fix! Of course, we can add some method to ViewModelBase.cs which will check property name but it won’t fix first reason! :-) In this case it would be really nice to have a symbol-like construct in C#, preferably one checked for correctness by the compiler. Thanks Lambda Expression, in C# 3.0 it is easy to write a function that will return the string representation of a symbol. Now we can change ViewModelBase.cs like this:

#region [ INotifyPropertyChanged Members ] 
public event PropertyChangedEventHandler PropertyChanged;
public static string GetPropertySymbol<TResult>(Expression<Func<TResult>> expr)
{ 
return ((MemberExpression)expr.Body).Member.Name; 
} 
protected virtual void OnPropertyChanged<PropertyType>(Expression<Func<PropertyType>>
propertyExpr) 
{ 
string propertyName = GetPropertySymbol(propertyExpr);
if (PropertyChanged != null)

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
} 
#endregion

  This is just beautiful. IntelliSense works too! IntelliSense And our code in MainViewModel.cs looks like:

private ViewModelBase _control; 
public ViewModelBase Control 
{ 
get { return _control; }
set 
{ 
_control = value; 
OnPropertyChanged(()=>Control); 
} 
}

  As you can see we reached all goals which we wanted to achieve: we have IntelliSense, if we change the property name we’ll have to change OnPropertyMethod too or we’ll get error during the build. That’s all! Hope it was of some help to you!   Artyom G, .NET team, Binary Studio

0 Comments
name *
email *