tags:

views:

118

answers:

3

New to WPF, so please bear with me...

Suppose I have 2 tables in SQL Thing OtherThing

Both have the exact same fields: ID (int) Name (string) Description (string) IsActive (bit/bool) DateModified (DateTime)

So, I want to create one Model (not two) and do something like this:

BaseModel<T>() 
{
   public int ID {get;set;}
   ...
}

etc. (of course, using the INotifyPropertyChanged, just trying to keep the code simple).

I want to be able to create a BaseView and BaseViewModel that would work with whatever model conforms to the Thing/OtherThing.

I am really at a loss as to what to do here to make this generic, so I don't have to have a ThingView/ThingViewModel and a OtherThingView/OtherThingViewModel...

It seems that this should be simple, but I cannot seem to figure it out. Does anyone have a code example where they could interchange various items from a dropdown list using one view, one ViewModel, and one base datamodel (and switching out the type from a dropdown)?

For example, a combobox has 3 identical table structures for Thing OtherThing SomeThing

and on selection changed, I want to pull the data from whatever table was selected, to be able to do standard CRUD operations on any of these 3 tables, without having to create concrete classes for each view/viewmodel.

+1  A: 

Can you actually just use a limited case and bind to just an interface definition? Then it won't matter which in the try item in the DataContext, only that it implements the properties you need?

Preet Sangha
I agree. If you have control over `Thing` and `OtherThing`, giving them a common interface and working through that is the way to go in this specific case.
Greg D
How would this be done? I believe this is heading in the right direction, but not quite sure how to get there. If both Thing/OtherThing have the IThing interface, how would the View and ViewModel work with an IThing?
Investor5555
I don't understand as I I see it, the you bind to the view model it implements IThing
Preet Sangha
+1  A: 

I am really at a loss as to what to do here to make this generic

No you're not. You've just described exactly what you should do:

public class ThingBase
{
   protected abstract string TableName { get; }
   public int ID { get; set; }
   public string Description { get; set; }
   ...
}

public class Thing : ThingBase 
{
   protected override string TableName { get { return "Thing"; } }
}

public class OtherThing : ThingBase
{
   protected override string TableName: { get { return "OtherThing"; } }
}

Now you have two classes that are identical in everything except in what table they store data in. You need only decide which one you want to use when creating an instance.

You can pull the same trick with your view models, except you probably don't need to implement any members in the view model subclasses. And now you can present Thing and OtherThing in totally different views by creating a DataTemplate for the ThingViewModel and OtherThingViewModel types. Or, if you want them to have the same view, just create a template for the ThingViewModelBase type. (You might not even need the subclasses in the view model if they have the very same UI, though it's trivial to change your mind later.)

I started a project from scratch at the start of last week, and by Friday it had 53 classes in it. This actually has resulted in code that is considerably less complex, more reliable, and easier to understand than it would be if I used fewer classes.

Robert Rossney
But I don't want a ThingViewModel and OtherThingViewModel.... I want an BaseViewModel that can operate on anyThing...
Investor5555
Well, like I said, you don't actually have to create subclasses of your base view model if Thing and OtherThing have the same UI. But why do you want to avoid subclasses, anyway?
Robert Rossney
A: 

I wouldn't say this is really a WPF situation - you're just talkin' about some Model View Controller type stuff...none of this really pertains to WPF, as I came here looking for WPF tutorial recommendations.

What you need is interfaces! PS - sorry about the code paste, it's NOT working for me today...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Things
{
    public interface IThing
    {
        //define here properties that are shared between Thing & Other Thing
        public {type} Property1 { get; }
        public {type} Property2 { get; }
    }

    public class Thing : IThing
    {
        //this could even be an existing model - you just need to implement the interface on it as defined below.
        //define properties & methods only appropriate to this 

        Property1 { get {return what you need to here from your model }
        Property2 { get {return what you need to here from your model }
    }

    public class OtherThing : IThing
    {
        //this could also be an existing model - you just need to implement the interface on it as defined below.
        //define properties & methods only appropriate to this 

        Property1 { get {return what you need to here from your model }
        Property2 { get {return what you need to here from your model }

    }

    public class SomeThingViewModel
    {
      IThing _thing;

      public ThingViewModel
      {
          //you'll need something to get the "IThing" stored as a member.  You may want to take it in the constructor and force it to use it
          // or maybe you asign it throuhg a property - your situation should choose which!
          //If you REALLY need to you can detect the type (Thing or OtherThing) in your functions usign Typeof and cast
          //to that type to use functions & properties NOT found in the interface...

          Public ThingViewModel(IThing thing)
          {
              _thing = thing;         
          }


       // here you would define any properties the view model needs to expose from the interfaced model.
       public Property1 
       {
            Get { return _thing.Property1; }
       }

       public Property2
       {
           Get { return _thing.Property2; }
       }

       public void FunctionToBeCalledOnSelectedIndexChanged()
       {
           //use _thing's properties here and do what you gotta do.
       }
    }
}

Edit: I Just saw this part :

I want to pull the data from whatever table was selected, to be able to do standard CRUD operations on any of these 3 tables, without having to create concrete classes for each view/viewmodel.

To do this, you'll need to create setters on the common properties in the interface that set the underlying model's property.

Ergo, IThing will define both a Get and a Set for each property that is common across the items. In the set on the concrete implementation (Thing and OtherThing) of the property set the appropriate property on the model it's representing.

I just used this technique the other day to take a bunch of models from different tables that all needed to be represented in drop downs using a "Name / ID". So I just interfaced a "Name" and "ID" property and bound my drop down list value / text to those items from an object of that interface. The "Set" part wasn't necessary. Now instead of having to make a bunch of different "BindDropDowNList" stuff to diff. types of viewmodels., I just created a funciton called BindDropDown(DropDownList ddl, INamedIDOBject nameObject)

Perplexed
>>I just used this technique the other day to take a bunch of models from different tables that all needed to be represented in drop downs using a "Name / ID".<<Do you have a code example of this?
Investor5555