Contents
Cinch Article Series Links
Introduction
Last time we started looking at the second part of Cinch internals, and this time we are going to look at what a typical Model and ViewModel might contain when using Cinch.
In this article, I will be looking at the following:
Prerequisites
The demo app makes use of:
- VS2008 SP1
- .NET 3.5 SP1
- SQL Server (see the README.txt in the MVVM.DataAccess project to learn what you have to setup for the demo app database)
Special Thanks
So I guess the only way to do this is to just start, so let us get going, shall we? But before we do that, I just need to repeat the special thanks section, with one addition, Paul Stovell who I forgot to include last time.
Before I start, I would specifically like to say a massive thanks to the following people, without whom this article and the subsequent series of articles would never have been possible. Basically, what I have done with Cinch is studied most of these guys, seen what's hot, what's not, and come up with Cinch. Which I hope addresses some new ground not covered in other frameworks.
Mark Smith (Julmar Technology), for his excellent MVVM Helper Library, which has helped me enormously. Mark, I know I asked your permission to use some of your code, which you most kindly gave, but I just wanted to say a massive thanks for your cool ideas, some of which I genuinely had not thought of. I take my hat off to you mate.
Josh Smith / Marlon Grech (as an atomic pair) for their excellent Mediator Implementation. You boys rock, always a pleasure.
Karl Shifflett / Jaime Rodriguez (Microsoft boys) for their excellent MVVM Lob tour, which I attended, well done lads.
Bill Kempf, for just being Bill and being a crazy wizard like programmer, who also has a great MVVM framework called Onyx, which I wrote an article about some time ago. Bill always has the answers to tough questions, cheers Bill.
Paul Stovell for his excellent delegate validation idea, which Cinch uses for validation of business objects.
All of the WPF Disciples, for being the best online group to belong to, IMHO.
Thanks guys/girl, you know who you are.
Developing Models Using Cinch
I know that there are some readers that will find my next statement controversial and some may even say it is downright wrong, but I will go into all of this in a minute. For now, let me just say what I want to say, which is this:
The way I do MVVM is to have an instance of a particular Model (say currentPerson) inside my ViewModel (say PeopleViewModel) which is exposed to the View (say PeopleView). The View binds and edits the Model directly.
This definitely flies in the face of what most people consider to be the holy grail of MVVM pattern, but it's a fairly new pattern, so people are still finding their way with it every day, and this works for me very well. The reason I do what I do, is for the following reasons:
- I have always had the luxury of being able to write my own UI specific Model classes. I would even do this if I was using some other Model classes first, such as LINQ to SQL or LINQ to Entity Framework, as these classes don't have everything a proper WPF Model class needs in my humble opinion. Though, they are pretty good, as they are Partial classes and use
INotifyPropertyChanged/DataContract
etc.
- I am a pragmatist and I do not like writing code for the sake of writing code. I have seen some MVVM apps where the author has had a Model with 50 properties on it, that are simply repeated in the ViewModel abstraction, where the ViewModel added nothing. On that day I decided I would never do that unless I have to.
- I honestly see no harm in writing directly to the Model from the View, just so long as if the Model is invaid, its data never makes its way to the database. I honestly see no problem with that at all.
So now that I have told you this, let's say I did not have the luxury of being able to create my own Model layer. Let's say you had another team developing the Model layer, and they did things their own way. Well, that's fine; what you would do in that case is treat this section of this article as if I was actually talking about doing this work to the ViewModel that wraps the Model rather than directly to the Model. Basically, apply what I am going to talk about next to your ViewModel (say PeopleViewModel) and leave the other team's provided Model (which presumably you have no control over) alone.
OK, argument over (hopefully). What we now need to decide is what base class you want to use for your Model (or maybe ViewModel if you have no control over your Model). Cinch has two choices.
Cinch.EditableValidatingObject
(or EditableValidatingViewModel
if you need to do this work in ViewModel): An editable (IEditableObject
), validatable (IDataErrorInfo
) object. You would need to supply logic to support both the editing and the validation within your own Model (or ViewModel if you have no control over your Model).
Cinch.ValidatingObject
(or ValidatingViewModel
if you need to do this work in the ViewModel): A validatable (IDataErrorInfo
) object. You would need to supply logic to do validation within your own Model (or ViewModel if you have no control over your Model).
So what I am going to do now is show you en example Model (but this code could be applied directly to a ViewModel, if you need to do that using either ValidatingViewModel
or EditableValidatingViewModel
).
Inheriting From Cinch.EditableValidatingObject
This is the hardest Cinch base class to inherit from (though it's not that hard) as Cinch does most of the work. You will need to do the following:
- Decide if you want to use the
DataWrapper<T>
helpers that allow your ViewModel to place every individual property into edit mode independent of any other property. This is your decision; you must decide. If you choose not to use a DataWrapper<T>
object for your properties, instead of something like this private Cinch.DataWrapper<Int32> orderId = new DataWrapper<Int32>()
, you would declare the property as normal, like private Int32 orderId = 0
.
- You must also provide validation rules so that the native
IDataErrorInfo
interface does its magic and shows the errors on the View.
- You must override
IsValid
, again the example below shows you how.
- You must override the
BeginEdit()/OnEndEdit()
and OnCancelEdit()
methods inherited from the IEditableObject
interface that we get by inheriting from Cinch.EditableValidatingObject
. Again the code below shows you how to do this.
using System;
using Cinch;
using MVVM.DataAccess;
namespace MVVM.Models
{
public class OrderModel : Cinch.EditableValidatingObject
{
#region Data
private Cinch.DataWrapper<Int32> orderId = new DataWrapper<Int32>();
private Cinch.DataWrapper<Int32> customerId = new DataWrapper<Int32>();
private Cinch.DataWrapper<Int32> quantity = new DataWrapper<Int32>();
private Cinch.DataWrapper<Int32> productId = new DataWrapper<Int32>();
private Cinch.DataWrapper<DateTime> deliveryDate = new DataWrapper<DateTime>();
private IEnumerable<DataWrapperBase> cachedListOfDataWrappers;
private static SimpleRule quantityRule;
#endregion
#region Ctor
public OrderModel()
{
#region Create DataWrappers
OrderId = new DataWrapper<Int32>(this, orderIdChangeArgs);
CustomerId = new DataWrapper<Int32>(this, customerIdChangeArgs);
ProductId = new DataWrapper<Int32>(this, productIdChangeArgs);
Quantity = new DataWrapper<Int32>(this, quantityChangeArgs);
DeliveryDate = new DataWrapper<DateTime>(this, deliveryDateChangeArgs);
cachedListOfDataWrappers =
DataWrapperHelper.GetWrapperProperties<OrderModel>(this);
#endregion
#region Create Validation Rules
quantity.AddRule(quantityRule);
#endregion
DeliveryDate.DataValue = DateTime.Now;
}
static OrderModel()
{
quantityRule = new SimpleRule("DataValue", "Quantity can not be < 0",
(Object domainObject)=>
{
DataWrapper<Int32> obj = (DataWrapper<Int32>)domainObject;
return obj.DataValue <= 0;
});
}
#endregion
#region Public Properties
public Cinch.DataWrapper<Int32> OrderId
{
get { return orderId; }
private set
{
orderId = value;
OnPropertyChanged(() => OrderId);
}
}
public Cinch.DataWrapper<Int32> CustomerId
{
get { return customerId; }
private set
{
customerId = value;
OnPropertyChanged(() => CustomerId);
}
}
public Cinch.DataWrapper<Int32> ProductId
{
get { return productId; }
private set
{
productId = value;
OnPropertyChanged(() => ProductId);
}
}
public Cinch.DataWrapper<Int32> Quantity
{
get { return quantity; }
private set
{
quantity = value;
OnPropertyChanged(() => Quantity);
}
}
public Cinch.DataWrapper<DateTime> DeliveryDate
{
get { return deliveryDate; }
private set
{
deliveryDate = value;
OnPropertyChanged(() => DeliveryDate);
}
}
#endregion
#region Overrides
public override bool IsValid
{
get
{
return base.IsValid &&
DataWrapperHelper.AllValid(cachedListOfDataWrappers);
}
}
#endregion
#region EditableValidatingObject overrides
protected override void OnBeginEdit()
{
base.OnBeginEdit();
DataWrapperHelper.SetBeginEdit(cachedListOfDataWrappers);
}
protected override void OnEndEdit()
{
base.OnEndEdit();
DataWrapperHelper.SetEndEdit(cachedListOfDataWrappers);
}
protected override void OnCancelEdit()
{
base.OnCancelEdit();
DataWrapperHelper.SetCancelEdit(cachedListOfDataWrappers);
}
#endregion
}
}
Inheriting From Cinch.ValidatingObject
This is the a slightly easier Cinch base class to inherit from (it is basically almost the same as what we just saw without the BeginEdit()
/ OnEndEdit()
and OnCancelEdit()
methods inherited from the IEditableObject
), as there is no support for editability. There is, of course, still the availability to allow the individual Model (or ViewModel) fields to be editable/read only by using the DataWrapper<T>
helpers, but that is your choice. Basically, BeginEdit()
copies the object's current values to a temporary storage, which has nothing to do with the DataWrapper<T>
helpers. The DataWrapper<T>
helpers simply allow the ViewModel to tell the View (through bindings) that a particular Model/ViewModel field should not be editable. They are two different things. There is some work to do to store the state of the DataWrapper<T>
helpers if you choose to use EditableValidatingObject
but that is mainly taken care of for you, as long as you use the static methods on the DataWrapperHelper
class as demonstrated above.
You will need to do the following:
- Decide if you want to use the
DataWrapper<T>
helpers that allow your ViewModel to place every individual property into edit mode independent of any other property. This is your decision; you must decide. If you choose not to use a DataWrapper<T>
object for your properties instead of something like this private Cinch.DataWrapper<Int32> orderId = new DataWrapper<Int32>()
, you would declare the property as normal, like private Int32 orderId = 0
.
- You must also provide validation rules so that the native
IDataErrorInfo
interface does its magic and shows the errors on the View.
- You must override
IsValid
, again the example below shows you how.
using System;
using Cinch;
using MVVM.DataAccess;
namespace MVVM.Models
{
public class OrderModel : Cinch.ValidatingObject
{
#region Data
private Cinch.DataWrapper<Int32> orderId = new DataWrapper<Int32>();
private Cinch.DataWrapper<Int32> customerId = new DataWrapper<Int32>();
private Cinch.DataWrapper<Int32> quantity = new DataWrapper<Int32>();
private Cinch.DataWrapper<Int32> productId = new DataWrapper<Int32>();
private Cinch.DataWrapper<DateTime> deliveryDate = new DataWrapper<DateTime>();
private IEnumerable<DataWrapperBase> cachedListOfDataWrappers;
private static SimpleRule quantityRule;
#endregion
#region Ctor
public OrderModel()
{
#region Create DataWrappers
OrderId = new DataWrapper<Int32>(this, orderIdChangeArgs);
CustomerId = new DataWrapper<Int32>(this, customerIdChangeArgs);
ProductId = new DataWrapper<Int32>(this, productIdChangeArgs);
Quantity = new DataWrapper<Int32>(this, quantityChangeArgs);
DeliveryDate = new DataWrapper<DateTime>(this, deliveryDateChangeArgs);
cachedListOfDataWrappers =
DataWrapperHelper.GetWrapperProperties<OrderModel>(this);
#endregion
#region Create Validation Rules
quantity.AddRule(quantityRule);
#endregion
DeliveryDate.DataValue = DateTime.Now;
}
static OrderModel()
{
quantityRule = new SimpleRule("DataValue", "Quantity can not be < 0",
(Object domainObject)=>
{
DataWrapper<Int32> obj = (DataWrapper<Int32>)domainObject;
return obj.DataValue <= 0;
});
}
#endregion
#region Public Properties
public Cinch.DataWrapper<Int32> OrderId
{
get { return orderId; }
private set
{
orderId = value;
OnPropertyChanged(() => OrderId);
}
}
public Cinch.DataWrapper<Int32> CustomerId
{
get { return customerId; }
private set
{
customerId = value;
OnPropertyChanged(() => CustomerId);
}
}
public Cinch.DataWrapper<Int32> ProductId
{
get { return productId; }
private set
{
productId = value;
OnPropertyChanged(() => ProductId);
}
}
public Cinch.DataWrapper<Int32> Quantity
{
get { return quantity; }
private set
{
quantity = value;
OnPropertyChanged(() => Quantity);
}
}
public Cinch.DataWrapper<DateTime> DeliveryDate
{
get { return deliveryDate; }
private set
{
deliveryDate = value;
OnPropertyChanged(() => DeliveryDate);
}
}
#endregion
#region Overrides
public override bool IsValid
{
get
{
return base.IsValid &&
DataWrapperHelper.AllValid(cachedListOfDataWrappers);
}
}
#endregion
}
}
Note: You can do all of this directly in your ViewModels. There are a few simple things to follow to do this work directly in your ViewModel.
- You must either inherit from
ValidatingViewModelBase
or from EditableValidatingViewModelBase
which gives you the functionality outlined above. Both of these classes also inherit from ViewModelBase
so you will have things like services and View lifecycle events in there already.
- Make sure you do the work outlined above in the Developing Models Using Cinch section to the ViewModels instead of to the Model.
Developing ViewModels Using Cinch
There is actually not much you need to think about if you want to use Cinch to develop ViewModels with. Most of the functionality can be done via inheriting from one of the Cinch ViewMode base classes, and using the exposed services, which is described below. If you want the validation functionality or the editing functionality, just follow the advice I gave above for Developing Models Using Cinch, but this time inherit from ValidatingViewModelBase
or EditableValidatingViewModelBase
.
Choosing a ViewModel Base Class
As discussed in the Developing Models Using Cinch section, what I typically do is have a Model that I bind my View to directly, where the Model is exposed through a currentXXX
property in my ViewModel. I realise that not everyone will be able to do this, so Cinch provides a number of base classes which are:
ViewModelBase
: The main base class which is also the base class to the two other ViewModel base classes mentioned below. That provides all the services and Window lifecycle events etc. This is discussed in more detail below.
ValidatingViewModelBase
: Which basically inherits from ViewModelBase
and provides support for validation via the same rules you saw demonstrated above (Developing Models Using Cinch) for the Model I showed being developed.
EditableValidatingViewModelBase
: Which basically inherits from ValidatingViewModelBase
and provides support for editing via the same rules IEditableObject
support you saw demonstrated above (Developing Models Using Cinch) for the Model I showed being developed.
The decision as to which one to use is up to you.
ViewModelBase Class
The easiest way to get started with Cinch is to inherit from the Cinch ViewModelBase
class, as this gives you lots of pre made support for things like:
INotifyPropertyChanged
- Window lifecycle events
- Services
This class is also the base class for the other two possible base classes you could use for your ViewModels in Cinch. That decision is up to you.
How to Use the Exposed Services
This section will highlight how to use the services exposed by the ViewModelBase
(if you inherited from Cinch ViewModelBase
that is) class. You should also read how the services are designed, which was covered in Part 3 of the Cinch article series.
Note: I am acutely aware there is going to be quite a bit of repetition here from the last article, but the way I explain it this time may be a little different; there may also be some of you that did not read the previous article, that may actually find this description of the services better/more useful, whilst others may go back to the previous article, but at least you now have options. I leave it up to you. Think of it as reinforced learning.
Logging service
As we saw in the previous article, there is a Simple Logging Facade (SLF) that is part of Cinch. How do we go about using this logging facade? Well, it's very easy (if you inherited from Cinch ViewModelBase
that is); here is what you do:
private void LogSomething()
{
var logger = this.Resolve<ILogger>();
if (logger != null)
logger.Error("Some error occurred");
}
Or as the SLF.ILogger
is also exposed as a public property on the ViewModelBase
class, you could do this in your custom ViewModels, providing they inherit from Cinch.ViewModelBase
.
private void LogSomething()
{
Logger.Error("Some error occurred");
}
It is really just a matter of using the ViewModelBase.Resolve()
method (which is really using the ServiceProvider
class as mentioned in Part 3) to locate the service and then just use the service.
One thing to note is that the logger is a special case and must be configured through an App.Config, where as if you are happy with all the other Cinch default services, you do not need to do anything to configure them, as the defaults will be used in the case of no configuration details.
Here is an example of an App.Config that configures the Cinch logging:
="1.0" ="utf-8"
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
<section name="slf" type="Slf.Config.SlfConfigurationSection, slf"/>
</configSections>
<slf>
<factories>
-->
-->
<factory type="SLF.Log4netFacade.Log4netLoggerFactory, SLF.Log4netFacade"/>
</factories>
</slf>
-->
<log4net>
-->
<appender name="MainAppender" type="log4net.Appender.FileAppender">
<param name="File" value="log.txt" />
<param name="AppendToFile" value="true" />
-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger: %date [%thread] %-5level - %message %newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="MainAppender" />
</root>
</log4net>
</configuration>
MessageBox Service
As we saw in the previous article, there is a MessageBox service that is part of the core Cinch services. How do we go about using this MessageBox service? Well, it's very easy (if you inherited from Cinch ViewModelBase
that is); here is what you do:
private void ShowMessageBox()
{
var messager = this.Resolve<IMessageBoxService>();
if (messager != null)
{
messager.ShowError("There was an error");
messager.ShowInformation("Nice day for MVVVM");
messager.ShowWarning("Something could be going down");
if (messager.ShowYesNo("Do it?",
CustomDialogIcons.Question) == CustomDialogResults.Yes)
{
}
if (messager.ShowYesNoCancel("Do it?",
CustomDialogIcons.Exclamation) == CustomDialogResults.Cancel)
{
}
}
}
It is really just a matter of using the ViewModelBase.Resolve()
method (which is really using the ServiceProvider
class as mentioned in Part 3) to locate the service and then just use the service.
Open File Service
As we saw in the previous article, there is an OpenFile service that is part of the core Cinch services. How do we go about using this OpenFile service? Well, it's very easy (if you inherited from Cinch ViewModelBase
that is); here is what you do:
private void OpenFile()
{
var openFileServ = this.Resolve<IOpenFileService>();
if (openFileServ != null)
{
openFileServ.InitialDirectory = @"C:\";
openFileServ.Filter = ".txt | Text Files";
var result = openFileServ.ShowDialog(null);
if (result.HasValue && result.Value == true)
{
FileInfo file = new FileInfo(openFileServ.FileName);
}
}
}
It is really just a matter of using the ViewModelBase.Resolve()
method (which is really using the ServiceProvider
class as mentioned in Part 3) to locate the service and then just use the service.
Save File Service
As we saw in the previous article, there is a SaveFile service that is part of the core Cinch services. How do we go about using this SaveFile service? Well, it's very easy (if you inherited from Cinch ViewModelBase
that is); here is what you do:
private void SaveFile()
{
var saveFileServ = this.Resolve<ISaveFileService>();
if (saveFileServ != null)
{
saveFileServ.InitialDirectory = @"C:\";
saveFileServ.Filter = ".txt | Text Files";
saveFileServ.OverwritePrompt = true;
var result = saveFileServ.ShowDialog(null);
if (result.HasValue && result.Value == true)
{
var messagerServ = this.Resolve<IMessageBoxService>();
if (messagerServ != null)
messagerServ.ShowInformation(
String.Format("Successfully saved file {0}",
saveFileServ.FileName));
}
}
}
It is really just a matter of using the ViewModelBase.Resolve()
method (which is really using the ServiceProvider
class as mentioned in Part 3) to locate the service and then just use the service.
Popup Window Service
As discussed in Part 3, the handling of popups is a bit of a strange case, as Cinch doesn't know about the actual popup types as these are typically not within the Cinch project, they are in the WPF UI project. So the WPF UI project must supply the pop windows to the Cinch contained IUIVisualizerService
service within the WPF app's main window, say. Part 3 talked about this in more detail.
However, providing the WPF UI project does its job and provides the popup instance and types somewhere to the IUIVisualizerService
service, it really is very easy to show a popup from a ViewModel using Cinch. All we do is something like the following:
var uiVisualizerService = this.Resolve<IUIVisualizerService>();
bool? result = uiVisualizerService.ShowDialog("AddEditOrderPopup",
addEditOrderVM);
if (result.HasValue && result.Value)
{
CloseActivePopUpCommand.Execute(true);
}
In the code above, the AddEditOrderPopup
popup that is available within the WPF UI project is shown and passed a ViewModel instance variable called addEditOrderVM
which will be used as the DataContext for the popup window. Using this technique and the IEditableObject
technique discussed in Part 2, it is very easy to have popups that are able to save state (OK button) and also cause an object that is editable (that presumably was edited by the popup) to be rolled back to its previous state using the popup Cancel button, which would cause the IEditableObject
to have its CancelEdit()
method called to restore the previous state.
When this all works, it is rather lovely. There is a nice example of how this works in the demo app's "AddEditCustomerViewModel
" code.
Background Tasks
Threading is hard, there is no doubt about it. There are also loads of different ways of doing things, but if you have read the Cinch article series so far, you will be aware that I have introduced a convenient threading helper class called BackgroundTaskManager<T>
that does help out (at least I think so). This class is discussed in Part 3. So I will not go over how it works in more detail as that has already been done in the previous article.
This time I just wanted to show you how you might use the BackgroundTaskManager<T>
class inside a ViewModel of your own to do some background task. So without further ado, here is an example. This example is highly fictitious and simply gets a range of future dates. The point is that it is done in the background using the BackgroundTaskManager<T>
class inside a ViewModel, so that is what to take away from the following code.
using System;
using ystem.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Data;
using Cinch;
using System.IO;
namespace MVVM.ViewModels
{
public class ExampleViewModelWithBgWorker : Cinch.WorkspaceViewModel
{
#region Data
private BackgroundTaskManager<DispatcherNotifiedObservableCollection<DateTime>>
bgWorker = null;
private DispatcherNotifiedObservableCollection<DateTime> futureDates
= new DispatcherNotifiedObservableCollection<DateTime>();
private SimpleCommand getFutureDatesCommand;
#endregion
#region Ctor
public ExampleViewModelWithBgWorker()
{
getFutureDatesCommand = new SimpleCommand
{
CanExecuteDelegate = x => CanExecuteGetFutureDatesCommand,
ExecuteDelegate = x => ExecuteGetFutureDatesCommand()
};
}
#endregion
#region Public Properties
public SimpleCommand GetFutureDatesCommand
{
get { return getFutureDatesCommand; }
}
public BackgroundTaskManager<DispatcherNotifiedObservableCollection
<DateTime>> BgWorker
{
get { return bgWorker; }
set
{
bgWorker = value;
OnPropertyChanged(() => BgWorker);
}
}
public DispatcherNotifiedObservableCollection<DateTime> FutureDates
{
get { return futureDates; }
set
{
futureDates = value;
OnPropertyChanged(() => FutureDates);
}
}
#endregion
#region Private Methods
private void SetUpBackgroundWorker()
{
bgWorker = new BackgroundTaskManager
<DispatcherNotifiedObservableCollection<DateTime>>(
() =>
{
var dateRanges = new
DispatcherNotifiedObservableCollection<DateTime>();
for (int i = 0; i < 10000; i++)
{
dateRanges.Add(DateTime.Now.AddDays(i));
}
return dateRanges;
},
(result) =>
{
FutureDates = result;
});
}
#endregion
#region Command Implementation
#region GetFutureDatesCommand
private Boolean CanExecuteGetFutureDatesCommand
{
get
{
return true;
}
}
private void ExecuteGetFutureDatesCommand()
{
try
{
GetFutureDatesCommand.CommandSucceeded = false;
bgWorker.RunBackgroundTask();
GetFutureDatesCommand.CommandSucceeded = true;
}
catch (Exception ex)
{
Logger.Log(LogType.Error, ex);
var messager = this.Resolve<IMessageBoxService>();
if (messager != null)
{
messager.ShowError(
"Failed to fetch future dates");
}
}
}
#endregion
#endregion
}
}
Mode Support
Some of you may recall from Part 2 that there was a little helper class called DataWrapper<T>
which allows the ViewModel to put each piece of bindable data within either the Current Model (the way I do it) or the actual ViewModel bindable properties into an edit/read only state. This was done using the DataWrapper<T>
helpers, which you may or may not decide to use. That is entirely up to you. If you do, here is what I suggest you do. I am assuming you have a CurrentXXX
Model object exposed as a bindable property from the current ViewModel (basically the way I do it), but read on even if this is not what you do as I have some advice even if you repeat all your Model properties in the ViewModel.
Note: This code only applies if you are using the Cinch DataWrapper<T>
helpers. If you are using standard types such as Int32
/Double
etc., please ignore this section entirely.
public ViewMode CurrentViewMode
{
get { return currentViewMode; }
set
{
currentViewMode = value;
switch (currentViewMode)
{
case ViewMode.AddMode:
CurrentCustomer = new CustomerModel();
this.DisplayName = "Add Customer";
break;
case ViewMode.EditMode:
CurrentCustomer.BeginEdit();
this.DisplayName = "Edit Customer";
break;
case ViewMode.ViewOnlyMode:
this.DisplayName = "View Customer";
break;
}
DataWrapperHelper.SetMode(
CurrentCustomer.CachedListOfDataWrappers,
currentViewMode);
OnPropertyChanged(() => CurrentViewMode);
}
}
Final Word
I just wanted to say I have spent a lot of time thinking of things that could go wrong and how to make things better and to think of good approaches, but things get missed, ideas are not as good as they were thought to be etc.
Anyway to cut a long story short...The idea behind Cinch, is if it works for you in its entirety, great, if not just pick the parts that work for you. That's the way I tried to write it all.
So please just use the bits you like.
What's Coming Up?
In the subsequent articles, I will be showcasing it roughly like this:
- How to Unit test ViewModels using the Cinch app, including how to test background worker threads which may run within Cinch ViewModels
- A demo app using Cinch
That's It Hope You Liked It
That is actually all I wanted to say right now, but I hope from this article you can see where Cinch is going and how it could help you with MVVM. As we continue our journey, we will be covering the remaining items within Cinch and then we will move on to show you how to develop an app with Cinch.
History
- xx/xx/09: Initial release.
- 05/12/09: Added new code sections to show users how to add validation rules using the new validation methods within Cinch.
- 24/12/09: Replaced
ILoggingService
with Simple Logging Facade.
Thanks.
As always, votes / comments are welcome.