Click here to Skip to main content
Click here to Skip to main content

How to make WPF behave as if MVVM is supported out of the box

By , 2 May 2012
 

Introduction

I get to teach MVVM pretty often and almost always somebody asks why WPF does not support it "built in".

If we think about it, the CodeBehind and the ViewModel are basically very much alike. They both contain the logic behind the UI, and their sole difference is that since the ViewModel is an all-together a different class, it helps constitute better separation between the logic (ViewModel) and the UI (View).

When you implement MVVM, Visual Studio does not know of the connection between the View and the ViewModel, and therefor you cannot switch between them easily by pressing F7 like you would normally do to switch between the XAML and the CodeBehind. Additionally, it would be helpful if Visual studio would have the ViewModel file "sit" under the View file in solution explorer, the way it does with CodeBehind.

If we think about it, the actual CodeBehind file we don't even need! I would prefer if it wasn't there to begin with so that nobody in my team would be tempted to use it at all. If there's no CodeBehind file, you cannot write CodeBehind, and that's a good thing.

Transforming the CodeBehind to ViewModel  

The following idea I've had for quite a while now, and I think I've come with a nice trick to do exactly that. We'll just transform the CodeBehind file to ViewModel, and be able to hold the stick on both ends! 

For example, let's assume I have a View named FirstView. The original XAML is going to look like this: 

<UserControl x:Class="ViewModelAsCodeBehindTrick.Views.FirstView"
             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>
</UserControl>

This is the interesting line: 

x:Class="ViewModelAsCodeBehindTrick.Views.FirstView"

It makes XAML's parser create a class named FirstView that inherits from UserControl. This class will be joined to the CodeBehind class through the use of 'partial'. Switching to the CodeBehind, we'll see the following code, that as said before – we don't really need:

namespace ViewModelAsCodeBehindTrick.Views
{
    /// <summary>
    /// Interaction logic for FirstView.xaml
    /// </summary>
    public partial class FirstView : UserControl
    {
        public FirstView()
        {
            InitializeComponent();
        }
    }
}

What we have here is the second part of the FirstView class that connects to the first part that was created by XAML. All we need to do is:

  1. Change the name of the class from FirstView to FirstViewModel.
  2. Change the namespace to ViewModels.
  3. Remove the constructor that calls InitializeComponent (this method obviously has no place in the ViewModel).
  4. Implement INotifyPropetyChanged, so that our ViewModel will be prepared for DataBinding later on.

Eventually, our file would look like this:

namespace ViewModelAsCodeBehindTrick.ViewsModels
{
    /// <summary>
    /// Interaction logic for FirstView.xaml
    /// </summary>
    public class FirstViewModel : INotifyPropertyChanged
    {
 
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

For the sake of demonstration, we'll add a property to our ViewModel so we'll be able to bind to it from the View later on, so that we could see if everything is working correctly. The following property is just a textual property, with its value being "Hello MVVM".

public class FirstViewModel : INotifyPropertyChanged
{
    private string someText;
    public string SomeText
    {
        get { return someText; }
        set
        {
            someText = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("SomeText"));
        }
    }
 
    public FirstViewModel()
    {
        SomeText = "Hello MVVM";
    }
 
 
    public event PropertyChangedEventHandler PropertyChanged;
}

All that remains now is to connect the View to the ViewModel. This can be done in all the standard ways (usually I use a ViewModelLocator), but for this example I used the simplest way possible:

<UserControl ...  >
    <UserControl.DataContext>
        <vm:FirstViewModel />
    </UserControl.DataContext>
    <Grid>
        <TextBlock Text="{Binding SomeText}" />
    </Grid>
</UserControl>

Additionally, I added a TextBlock that is bound to "SomeText" so that we could see if everything is working correctly.

If we now look at the designer preview, we'll see that evidently it does:

image

Not so fast…

This is where it gets complicated. Without paying too much notice we've created a nasty bug that will be seen only in runtime. If we put our View ("FirstView") on the main window:

<Window x:Class="ViewModelAsCodeBehindTrick.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:v="clr-namespace:ViewModelAsCodeBehindTrick.Views"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <v:FirstView x:Name="firstView1" />
    </Grid>
</Window>

And run the application, we'll see… nothing. It doesn't work for some reason…

image

And this is despite the fact that in design time, it *does* work:

image

So what is it? How come it doesn't work on run time although id *does* work on design time?!

The first instinct of every veteran WPF programmer is to immediately check the Output window to see if there are any Bidning Errors, but that's not going to help here. There are no binding errors and the Output window is going to be clean. It is an entirely different problem.

(I suggest taking a few minutes to try and figure out yourselves. It took me some time as well... :-)

.

.

.

.

.

.

.

.

.

.

.

Understanding the XAML parsing process 

From the XAML file two files are created during compilation:

1. FirstView.g.cs – where the class FirstView sits. This class loads the second file – 

2. FirstView.Baml which is our XAML after some sort of compilation (it's actually pre-tokenization – parsing the file in advance so that the loading in runtime would be faster than loading a non-parsed XML file)

The loading and connecting of those two files are being performed in the method InitializeComponent that resides within FirstView.g.cs, only… now that we've got rid of the CodeBehind nobody calls this method. What happens is that nothing loads the BAML file and hence everything stays completely empty. We don't see binding errors because even the Binding is not loaded.

The interesting bit here is that during design time, Visual Studio knows automatically to load and run the BAML file, and this is why in design time is does work.

So all we need to do to fix the problem is to make sure that this method does get called during runtime. But how?

Creating a UserControl that automatically calls InitializeComponent 

The solution I found is a nice trick. Instead of having our View implement UserControl, let's make it implement a new class named ViewBase.

Let's create a new class named ViewBase that inherits from UserControl:

public class ViewBase : UserControl
{
}

Instead of having the View inherit directly from UserControl, it will now inherit from ViewBase:

<v:ViewBase x:Class="ViewModelAsCodeBehindTrick.Views.FirstView"
            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" 
             xmlns:vm="clr-namespace:ViewModelAsCodeBehindTrick.ViewsModels"
             xmlns:v="clr-namespace:ViewModelAsCodeBehindTrick.Views"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <vm:FirstViewModel />
    </UserControl.DataContext>
    <Grid>
        <TextBlock Text="{Binding SomeText}" />
    </Grid>
</v:ViewBase>

(When we change UserControl to v:ViewBase Visual Studio is not going to like it at first and it won't provide intellisense for a few seconds. That's OK.)

All that is left now is to make our newly created class (ViewBase) call InitializeComponent in the constructor.

The problem is that if we try and invoke InitializeComponent we'll get an error – the method doesn't exist yet. This method is being created in compile time by the XAML Parser…

image

So we need to call the method in runtime. The method exists at runtime but not at design-time, so we can't call it because it won't compile. Nothing that won't be solved with some Reflection!

this.GetType().GetMethod("InitializeComponent").Invoke(this, null);

If we run the application now we'll see that it's working! 

image

Only if we return to Visual Studio we'll see that in now works in runtime, but... not in design time.

 image

What happens now is that we're trying to call InitializeComponent, which works in runtime, but fails in design time. This is going to easily solved – we'll just make sure not to call it in design time:

public class ViewBase : UserControl
{
    public ViewBase()
    {
        if (!DesignerProperties.GetIsInDesignMode(this))
            this.GetType().GetMethod("InitializeComponent").Invoke(this, null);
    }
}

And now it'll work perfectly for design time AND runtime!

Was it worth the effort? 

Bottom line, after writing ViewBase for the first time – it's going to be real easy to use it for the second time. This gives us a few very cool and handy features:

  1. Our ViewModel now resides under the View in Solution Explorer. Very helpful IMO.
  2. No more redundant CodeBehind!
  3. If we're in the XAML file, all we need to do to go to the ViewModel file is just press F7! (Regretfully it doesn't work the other way around, but it's still quite a lot.)

In my opinion it's definitely worth the effort. A huge part of MVVM is aimed toward making the Tools work better, and this article goes in the same direction.

I suggest downloading the code and playing with it.  Is it useful to you?

The full code can be downloaded HERE.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Elad R. Katz
Architect Sela
Israel Israel
Member
Elad Katz is Senior Architect at Sela. He specializes in WPF, HTML5 and ASP.NET, with more than 10 years of experience in the industry in various .NET technologies.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionSeems a lot of effortmvpSacha Barber18 Nov '12 - 22:48 
When if you really want your View to have no code behind and bind to a ViewModel.
 
Isn't that simply a DataTemplate for the ViewModel. Which has no code behind, is bound to the ViewModel. Its kind of exactly what you want isn't it.
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralMy vote of 4memberIFFI9 Nov '12 - 3:22 
Nice approach, however, I'm not sure if the same technique could be used for lookless controls, or does it give any meaning to compare these two?
GeneralNice!memberOmar Gamil28 Jul '12 - 1:19 
I don't really feel your pain, the necessity to go to the viewmodel with a touch of F7.
In MVC for example there's no way to hop from the view to the controller, yet everybody's happy...
 
Nevertheless, this is a very interesting approach, showing not just imaginative thinking but also high skills and complete control of your tools.
 
have 5 Wink | ;)
GeneralRe: Nice!memberElad R. Katz28 Jul '12 - 5:05 
Thanks Smile | :)
 
I feel this is a problem in MVC as well. Most programmers don't realize that the coupling between the View and the Controller/ViewModel is very high no matter what. You would never divide work so that one programmer writes the View and another the Controller. It's always together, and being an asp.net mvc programmer as well I think it's the same deal there also.
QuestioninterestingmemberCIDev9 May '12 - 7:42 
A well written and interesting article.
Just because the code works, it doesn't mean that it is good code.

AnswerRe: interestingmemberElad R. Katz9 May '12 - 12:21 
Thanks! Smile | :)
GeneralMy vote of 4memberandrew45827 May '12 - 8:29 
Reflection? Hack
GeneralObviously. It is not supported out of the box...memberElad R. Katz9 May '12 - 12:20 
But what do u think VS itself does to show design time? (hint, it uses reflection Smile | :) )
QuestionMy vote of 4memberAnkush Bansal4 May '12 - 2:51 
Hi,
 
Its a good technique, but i have some doubts:
1. Isn't it messy to have a file name different from class name? a view model in a view file?
2. Isn't a view in MVVM suppose to be ViewModel agnostic and vise-versa?
3. What if in future you need a View code behind (does there exist any such need?.. may be )?
4. Doesn't this supports "ViewModel is a new code behind" philosophy"?
 
Thanks,
ankush
AnswerRe: My vote of 4memberElad R. Katz4 May '12 - 4:53 
Hey Ankush,
 
1. Define messy? This is the extent of my trick... I agree I would prefer to have the VM's name, but I don't think it's a big issue.
 
2. They're both still completely agnostic (btw, the View is not agnostic to the VM in MVVM), Nothing got ruined in the way.
 
3. There is quite often a use for VIEW code, but putting it in the code behind constitutes bad MVVMing, since codebehind is a really fragile place. It's really easy to put logic there also and have your logic tightly coupled to the view.
This is where Behaviors come in to play. Whenever I need view code I'm using behaviors or custom controls. IMHO you *never* need codebehind.
 
4. Basically yes. As far as I understand the pattern, MVVM is basically taking the code behind and making it completely separated from the view. (by using two classes instead of two partial classes). IMHO, In that aspect, MVVM is a bad choice of name for the pattern. I always tell my students to think of MVVM as Separated Code Behind (not a catchy name as MVVM though Wink | ;-) )
 
Thanks for the feedback!
Elad.
GeneralRe: My vote of 4memberAnkush Bansal4 May '12 - 21:09 
Hi Elad,
 
Thanks for your detailed answers.
 
1. For me its messy to have a class under different file name. This does have impact in real life scenarios where source code is maintained under some repositories such as SVN or ClearCase. And think of a scenario where you saw a SVN log that shows the View file was modified but actually a ViewModel class was modified. Isn't it messy!
 
2. In our project we tend to keep view in a different folder (or may be in a different DLL all together coming from UI designers). This make life easy for Unit testing as well.
 
3. Completely agree on your point 3 above. Point taken sir! Smile | :)
 
As far as I understand the motive here is to group a ViewModel and View files under same node in VS solution explorer for easy navigation. And this can be achieved by changing the cs.proj file by adding a "DependentUpon" node:
 
<Compile Include="View.cs">
      <Generator>MSBuild:Compile</Generator>
      <SubType>
      </SubType>
    </Compile>
    <Compile Include="View.xaml.cs">
      <DependentUpon>View.cs</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="ViewModel.cs" >
    <DependentUpon>View.cs</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
 
How about writing an AddIn that provides a new context menu to add a file under given node? This might help for starters:
 
http://www.code-magazine.com/article.aspx?quickid=0710082[^]
 
Would be another nice article!! Big Grin | :-D
 
Thanks agian!
-ankush
GeneralRe: My vote of 4memberElad R. Katz9 May '12 - 8:13 
changing the csproj file feels harder for me, although writing a plugin to do that sounds like a cool idea Smile | :)
I do, however, like not having a codebehind file, and that's where i this method a bit better.
 
Elad
Question*Cough ahem*protectorPete O'Hanlon2 May '12 - 5:24 
You typed in InitilizeComponent, not InitializeComponent. No wonder it's asking you to generate a method stub.

*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington

"Mind bleach! Send me mind bleach!" - Nagy Vilmos

CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier

AnswerRe: *Cough ahem*memberElad R. Katz2 May '12 - 10:09 
My point still standing Smile | :) This is what happens when you don't have intellisense (since there is no such method at design-time).

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 2 May 2012
Article Copyright 2012 by Elad R. Katz
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid