Class Skeleton
Class Skeleton

Reputation: 3083

How to set the model in a viewmodel (MVVM)

I have an HelloWorldWPFApplication class with the following method:

public override void Run()
{
    var app = new System.Windows.Application();
    app.Run(new ApplicationShellView());
}

The ApplicationShellView has the following XAML:

<winbase:ApplicationShell x:Class="HelloWorldWPFApplication.View.ApplicationShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:winbase="clr-namespace:Framework.Presentation.Control.Window;assembly=Framework"
    xmlns:vm="clr-namespace:HelloWorldWPFApplication.ViewModel"
    Title="{Binding WindowTitle, Mode=OneWay}">

<Window.DataContext>
    <vm:ApplicationShellViewModel  />
</Window.DataContext>

</winbase:ApplicationShell>

If my ViewModel (ApplicationShellViewModel) has the following method, the window will have the title set to "Test":

    public string WindowTitle
    {
        get
        {
            return "Test";
        }
    }

My problem is that I want to set the title based on properties within the HelloWorldWPFApplication class. I added the following to the HelloWorldWPFApplication's base class (which uses the INotifyPropertyChanged interface):

private WpfApplicationBase<WpfApplicationDataBase> applicationModel;

public WpfApplicationBase<WpfApplicationDataBase> Application
{
    get { return this.applicationModel; }
    set { this.Set<WpfApplicationBase<WpfApplicationDataBase>>(ref this.applicationModel, value); }
}

So effectively, I plan on reusing the existing HelloWorldWPFApplication object as the model (in MVVM).

I changed the WindowTitle property as follows:

public string WindowTitle
{
    get
    {
        return String.Format("{0} {1}",
              this.applicationModel.Data.FullName,
              this.applicationModel.Data.ReleaseVersion).Trim();
    }
}

Of course, at this stage my project creates a window without a title, as the application field has not been set. I don't want to create a new application object within the view model as one already exists. I want to use this existing object. What is the best way to achieve this?

I am very new to MVVM/WPF - and from my basic understanding of MVVM I don't want to put any code-behind in the view. I could have a static field set on a static class to the application object, and then assign this field in my view model (this works, but not sure having "global" variables is the best approach).

I have also tried creating the view model before showing the window, but have encountered a problem I have yet to solve. In this implementation my run method appears as follows:

public override void Run()
{
    var window = new ApplicationShell();  // inherits from System.Windows.Window
    var vm = new ApplicationShellViewModel();
    vm.Application = this;  // this line won't compile
    window.DataContext = vm;

    this.Data.WpfApplication.Run(window);
}

I get a compile error:

Error 1 Cannot implicitly convert type 'HelloWorldWPFApplication.Program.HelloWorldApplication' to 'Framework.Business.Logic.Program.Application.WpfApplicationBase'

I'm confused with the error as my HelloWorldWPFApplication class inherits from WpfApplicationBase:

    public class HelloWorldApplication<T> : WpfApplicationBase<T>
    where T : HelloWorldApplicationData

Additionally, HelloWorldApplicationData inherits from WpfApplicationDataBase.

I get the pretty much the same problem with the following implementation:

public override void Run()
{
    var window = new ApplicationShell();
    var vm = new ApplicationShellViewModel();
    var app = new HelloWorldApplication<HelloWorldApplicationData>();
    vm.Application = app;  // Cannot implicitly convert type error again
    window.DataContext = vm;

    this.Data.WpfApplication.Run(window);
}

Exact error:

Error 1 Cannot implicitly convert type 'HelloWorldWPFApplication.Program.HelloWorldApplication' to 'Framework.Business.Logic.Program.Application.WpfApplicationBase'

Upvotes: 0

Views: 1288

Answers (1)

BradleyDotNET
BradleyDotNET

Reputation: 61379

First off, the "Application" class in WPF should be used for one thing, and one thing only: starting the program. It is not a model.

That said, I would just pass everything in sequence (this can apply to a proper model as well):

MyViewModel viewmodel = new MyViewModel(this);
var app = new System.Windows.Application();
app.Run(new ApplicationShellView(viewmodel));

Of course, remove the data context set from XAML. This does require modifying your code behind to accept the VM object and set it to the DataContext in your constructor, but thats a standard way of passing the VM to the View.

You could also use a Service Locator to find your model, or a number of other ways. Unfortunately, its hard to say which one is right, since your model is so weird.

As a complete aside; the title of your program is very much a part of the View, and probably doesn't need to be bound at all (your name is static, so making your application class the model isn't buying you anything).

Upvotes: 2

Related Questions