Business has evolved. What about your technology solutions?
Get a Quote
EN
 
 
Binary Studio
Binary is valuable ideas for your business

HomeBlogTechnical

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!

Img3

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.

Img4

And of course, it changes value (because we added OnPropertyChanged("Control");)

Img5

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!

Img6

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. (You can download source code here).

That’s all! Hope it was of some help to you!

 

Artyom G, .NET team,
Binary Studio

4 comments for “WPF Model-View Application. Tricky things.”

  1. Gravatar of AbhiAbhi
    Posted Sunday, September 18, 2011 at 4:55:59 AM

    Thanks for this excellent article. The link to your source code is broken, it will be really helpful if you can fix it.

  2. Gravatar of MichaelMichael
    Posted Thursday, October 06, 2011 at 2:28:21 AM

    Thanks for visiting our blog! We've fixed the issue, so you can download the file now. Feel free to join other discussions!

  3. Gravatar of Stuart WellsStuart Wells
    Posted Wednesday, November 30, 2011 at 9:38:04 AM

    I need to do exactly what you are describing but it just 'feels plain wrong'. I can't quite stop that nagging feeling that my having the ViewModel know about the View we have just broken MVVM.

    The issues that come to mind are:

    1) the View knows about the ViewModel via DataContext so this is effectivel cyclic

    2) if ViewModel were in a different DLL it wouldn't be able to do this

    3) the second window appears whereever it fancies as it has no idea who its parent is to centre itself - and to achieve this the parent ViewModel would have to retain a handle to the parent View

    I want to achieve this, without any code behind the XAML and without needing to use Blend SDK (as Actions/Behaviours/Triggers might be the answer but this is another dependency I don't want at the moment).

    Can you help me understand why this does not break MVVM as it seems to to me?

  4. Gravatar of J. MorganJ. Morgan
    Posted Thursday, February 23, 2012 at 4:15:16 PM

    Thanks this was extremely helpful. I had a question though I have listbox in my second window that I want it's Itemsource to be a property in my MainViewModel. I have the listbox binded and am calling my RaisePropertyChanged event however the box is empty. I think it has something to do with the binding but not sure what to do. Any ideas?

Leave a comment

 

We are more than happy to hear from you

Privacy policy Binary Studio will not sell or rent your information to any 3rd party vendors.
Read our privacy policy.

Subscribe to our newsletter

 

Recent Comments

Archives

Meta

 
  • Testimonials

  • Showcase

    Showcase
  • Blog

    Blog
 
 
Home page      About Us      Services      Case Studies      Blog      Prices      Contact us      Sitemap      Our Prices
Email: info@binary-studio.com    Tel: +380.62.206.84.61 Copyright 2005-2012 Binary Studio
Social