Can I register a custom model binder somewhere other than Global.asax? - asp.net-mvc-2

It would be handy to limit the scope of a custom model binder for just a specific controller action method or its entire controller. Hanselman wrote a sentence that implied alternative locations for custom model binder registration but never seemed to finish the thought:
You can either put this Custom Model Binder in charge of all your DateTimes by registering it in the Global.asax
Is it possible to make these registrations at a smaller scope of the controller system? If so, is there any reason to avoid doing so outside of the Global.asax MvcApplication (e.g., performance reasons)?

As I was closing the tabs I opened for this question that I hadn't reached before giving up, I found someone with an answer. You can assign a ModelBinderAttribute to your view models:
[ModelBinder(typeof(SomeEditorModelModelBinder))]
public class SomeEditorModel {
// display model goes here
}
public class SomeEditorModelModelBinder : DefaultModelBinder {
// custom model binder for said model goes here
}
While it wasn't quite what I was looking for, it is even more specific than registering it for a controller or controller method.
Update
Thanks to Levi's comment pointing out a much better solution. If you are consuming the object with a custom model binder in an MVC action method directly, you can simply decorate that method's parameter with the ModelBinder property.
public ActionResult SomeMethod([ModelBinder(typeof(SomeEditorModelBinder))]SomeEditorModel model) { ... }

Related

Command class in Caliburn Micro

On my first project trying out Caliburn.Micro, I like a lot of the things :-)
One thing I miss (or havn't discovered yet) is how to separate the viewmodel and a command.
CM doesn't support ICommand, as it's way of doing things is superior. I'm sure it's true, so I would love a small push in the right direction to achieve this or perhaps discover a better way.
As I understand you have to put the "Execute" method and "CanExecute" property directly in the viewmodel, named to match the control in the view, to get the magic to work.
I would like to put the "Execute" and "CanExecute" in a different object that is property on the viewmodel and then CM would automatically bind to that object, using the control name and property names as usually.
Repost from the forum on Caliburn Micro, I didn't get any answers so I'm trying my luck here.
You should try to avoid invalidating the Law of Demeter. Therefore, on your view model you can have an execute method, and a CanExecute property (usually calculated), and these can call into the containing model where appropriate, e.g:
public void Save
{
// .. save logic
}
public bool CanSave
{
get
{
return this.model.CanSave ... and other logic etc.
}
}
You must remember to notify a change in the calculated property when the can save state changes, e.g:
public void CodeThatGetsRunWhenAPropertyOfTheModelChanges()
{
this.NotifyOfPropertyChanged(() => this.CanSave);
}
If you have e.g. a Button on your view with x:Name="Save", then Caliburn.Micro will automatically invoke your Save verb on the view model when the button is clicked, and will automatically enable and disable the button when the CanSave property value changes.
To avoid fat ViewModels you also need to avoid fat Views. Caliburn.Micro allows you to compose Views/ViewModels as described in Screens, Conductors and Composition.
The short version is, you can include a "DetailView" and "DetailViewModel" pair in a "MasterView"/"MasterViewModel" shell by defining a DetailViewModel-typed property in MasterViewModel and adding a ContentControl named after it in MasterView. Binding and actions work as usual, so you avoid both fat models/views and routing of commands.
Another option is to bind a MasterView element to a DetailViewModel property or action, by prepending the detail's property to the target's name. I can't find the specific URL yet, so the example is from memory.
Assuming you have the following classes:
public class MasterViewModel:Screen
{
public property DetailViewModel MyDetails{get;set;}
}
and
public class DetailViewModel:Screen
{
public property string SomeText{get;set;}
public void DoTheBoogie(){}
}
You can add a control in you MasterView named 'MyDetails_SomeText' to bind to the DetailViewModel.SomeText. You can also bind to DoTheBoogie the same way.
I prefer to create a separate View though, named DetailView and add a ContentControl named "MyDetails" in MasterView. This results in a cleaner and more modular design

Questions on changing from WinForms to WPF

I have recently been looking for a way to bind data in WinForms using MVVM.
I ended up creating a custom implementation of the MVVM pattern.
However, due to the lack of two-way binding, I have decided to give WPF a try.
I already have the Model, which encapsulates the non-ui functionality. For example the Model reads a configuration file, has a few properties, listens for incoming data on a socket, and saves incoming packets if needed.
Some of the Model's properties throws an exception if they are set out of range. I.e. the validation is done in the Model.
How is validation usually done in WPF? I have read a good deal of articles, and there seems to be some consistency in putting validation in the ViewModel. In fact, most articles only use ViewModel and View. Has the Model been buried?
Glad to see your decision to move away from custom implementations of MVVM when so much already exists that just ... works.
WPF is very strong for two way binding and that gives it its' greatest strengths.
The view model is bound to the view and acts as the mechanism to communicate with the data layer. Also Entity Framework (if you are on framework 4.0) will give you a great data layer for populating your entities in your ViewModel. This basically becomes your Model. It gives you an encapsulated form of UnitOfWork as well as Repository patterns.
While your view model in all examples are usually on a one-to-one basis, if the design calls for it you can have view models that span multiple views. I have a "menu" which displays key identifiers from each item in the list and a detail form that shows all fields for editing from the same object. So I span the view between the two.
You can hard code the view model in the xaml binding it to the datacontext or you can use Unity and inject the viewmodel into the view. Unfortunately the injection requires adding one public property for the purpose of setting the datacontext. So you'd have code like this:
public class MyView:Window
{
public MyView(MyViewModel model)
{
InitializeComponent();
ViewModel = model;
}
public MyViewModel ViewModel
{
set{ this.DataContext = value; }
}
}
So the rest is just TwoWay binding for each field and the setter can encapsulate single value editing. Your error message can even be bound to a text field on the form and it displays when the value is not null.
Also if you dig into Unity you will also get a truly great function called Event Aggregation which basically provides a vehicle for publish/subscribe of events and that ties into your ICommand implementation when getting a button click handled.

MVVM access other view's element from viewModel

I started working with the MVVM pattern in a new project.
Everything is ok, but i came to the following problem.
The implementation looks like this:
I have a MainView, the main app window. In this window i have a telerik RadGroupPanel in wich I host the rest of the app views as tabs.
The rest of the viewModels does not know about this RadGroupPanel which is hosted in MainVIew.
How should i correctly add those views to the RadGroupPanel from the commands in the viewModels?
Thanks.
Have you considered injecting your view into the ViewModel using an interface to maintain separation? I know this breaks MVVM but I've successfully used this on a number of WPF projects. I call it MiVVM or Model Interface-to-View ViewModel.
The pattern is simple. Your Usercontrol should have an interface, call it IView. Then in the ViewModel you have a property with a setter of type IMyView, say
public IMyView InjectedView { set { _injectedView = value; } }
Then in the view you create a dependency property called This
public MyUserControl : IMyView
{
public static readonly DependencyProperty ThisProperty =
DependencyProperty.Register("This", typeof(IMyView), typeof(MyUserControl));
public MyUserControl()
{
SetValue(ThisProperty, this);
}
public IMyView This { get { return GetValue(ThisProperty); } set { /* do nothing */ } }
}
finally in Xaml you can inject the view directly into the ViewModel using binding
<MyUserControl This="{Binding InjectedView, Mode=OneWayToSource}"/>
Try it out! I've used this pattern many times and you get an interface to the view injected once on startup. This means you maintain separation (Viewmodel can be tested as IView can be mocked), yet you get around the lack of binding support in many third party controls. Plus, its fast. Did you know binding uses reflection?
There's a demo project showcasing this pattern on the blog link above. I'd advocate trying out the Attached Property implementation of MiVVM if you are using a third party control.
You can have the list of viewmodels that you need to add controls for in an ObservableCollection in your main window viewmodel. You can then bind the ItemsSource of the RadGroupPanel to that collection and use the ItemTemplateSelector and ContentTemplateSelector of the RadGroupPanel to select the right template to use based on the viewmodel that is bound.

ASP.NET MVC 2. Cannot create an instance of an interface

I use ASP.NET MVC 2 for my project.
I'm trying to edit my product information using next code:
[HttpGet]
public ActionResult Edit(int id)
{
IProduct product = productService.getProductById(id);
return View(product);
}
IProduct and other IEntities are instansing using IoC Castle Windsor. Page for Edit is succesfully loaded. In the top of my page I declared that page must Inherits="System.Web.Mvc.ViewPage〈DomainModel.Entities.IProduct〉. And it is. But when I'm trying to update my changes using next code:
[HttpPost]
public ActionResult Edit(IProduct product)
{
//Whatever i did here i always get the error prevents my any actions
}
then i'm getting the error 'Cannot create an instance of an interface.'
Can anybody clarify why?
Sorry for probably bad English, and thanks for any answer.
This is because MVC needs to know a concrete type to construct for your product parameter.
You cannot instantiate an IProduct, only a concrete type that implements IProduct. The easiest way to make this work, is to use simple concrete DTO types to transfer data from and to the view (sometimes known as the ViewModel).
You can make your example work with the interface type, if you implement a custom model binder for the type. (Basically a way to tell MVC what type to instantiate when it encounters IProduct).

MVVM setup design time services?

I'm working with the MVVM pattern + a simple ServiceLocator implementation, now to my problem how am I supposed to setup the services when the views are running in design time?
Iv tried this but it does not seem to work in VS 2010 or some thing, I know it worked on my old computer but on my new it does not. so does any one know a good alternative?
Edit: (On behalf of Merlyn Morgan-Graham)
Well what I'm trying to do is this, I have my view, ViewModel and services now the difference here is that I have 2 implementations of each service one for design time and one for run time.
for a better explanation look here.
If you want to decouple your view from your viewmodel, and your viewmodel from your model/dal (basically, if you want to use MVVM), then your view model and data model shouldn't know anything about design time. Design time only applies to the view.
This article shows a way to define your design time data via XML/XAML, so your code underneath doesn't have to know anything about it:
http://karlshifflett.wordpress.com/2009/10/21/visual-studio-2010-beta2-sample-data-project-templates/
After Edit: It turns out that you'll still have to use your view model for your existing XAML bindings to work. This will just populate the view model rather than having to create a new data model. I'm not sure, but there might be classes that allow you to use the WPF binding mechanism to take care of this... Views?
Resume Before Edit...:
As far as the solution in the article you linked first, the designer doesn't instantiate anything but your class, and the code it references. That means that assembly attributes won't be instantiated unless your view code somehow directly references them.
If you really want to couple your view models to your views during design time, and make it so that design time services are registered, then you have to place the service registration code in your view class, or a class the view class directly references.
To do that, you could use static constructors of your views to register your design time services. You could also write a static method on some other class (application?) to (conditionally) register the design time services. Then, call that method in the constructor of your views.
Or you could simply register them in the constructor for each of your views.
Basically, what you want to do is possible, but that method linked in the first article isn't. If you read farther in the comments, you'll see that his method is broken.
You may also want to question the idea of hooking your view model to your view during design time, because the MVVM pattern was made to avoid that sort of thing.
You usually don't need to access services at design-time... Typically, you don't even use your real ViewModels at design-time, you use dummy design data, as explained here. If you really need to use your real ViewModels, you can implement dummy versions of your services, and use them instead of the real services :
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
// Design time
ServiceLocator.Instance.Register<IService1>(new DummyService1());
ServiceLocator.Instance.Register<IService2>(new DummyService2());
}
else
{
// Run time
ServiceLocator.Instance.Register<IService1>(new RealService1());
ServiceLocator.Instance.Register<IService2>(new RealService2());
}
Also I do agree to all who have concerns regarding the use of the service locator at design time, I do believe that this is a valid scenario in some use cases.
This is not a discussion on why/why not, this is simple the way it (almost) worked for me.
There is still a problem which I did not solve yet: this only works for one view at a time.
Create a simple bootstrapper for setting up your IoC of choice. Notice the ISupportInitialize interface.
public class Bootstrapper: ISupportInitialize
{
#region ISupportInitialize Members
public void BeginInit() { }
public void EndInit()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
Setup();
}
#endregion
public static void Setup() { SetupServiceLocator(); }
static void SetupServiceLocator()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConfigService>().As<IConfigService>().ExternallyOwned().SingleInstance();
IContainer container = builder.Build();
ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));
}
}
Use the Bootstrapper as before for runtime mode, e.g.:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper.Setup();
}
}
Additionally you need to add it to the application resources for design mode support:
<Application x:Class="MonitoringConfigurator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyBootstrapperNamespace"
StartupUri="MainWindow.xaml">
<Application.Resources>
<local:Bootstrapper x:Key="Bootstrapper" />
</Application.Resources>
</Application>