I'm trying to implement Unity in a WPF MVVM application, but I'm missing the big picture.
At this moment I have created a bootstrapper like this:
public class MainBootstrapper : Bootstrapper<MainViewModel>
{
private UnityContainer container;
protected override void Configure()
{
container = new UnityContainer();
container.RegisterType<IServiceLocator, UnityServiceLocator>(new ContainerControlledLifetimeManager());
container.RegisterType<IWindowManager, WindowManager>(new ContainerControlledLifetimeManager());
container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
}
protected override object GetInstance(Type service, string key)
{
if (service != null)
{
return container.Resolve(service);
}
if (!string.IsNullOrWhiteSpace(key))
{
return container.Resolve(Type.GetType(key));
}
return null;
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.ResolveAll(service);
}
protected override void BuildUp(object instance)
{
container.BuildUp(instance);
}
}
How what is the best way to use this?
This code currently works:
public class MainViewModel : PropertyChangedBase
{
public MainViewModel()
{ }
[Dependency]
public Sub1ViewModel Sub1VM { get; set; }
[Dependency]
public Sub2ViewModel Sub2VM { get; set; }
}
the MainView has this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Name="Sub1VM" />
<ContentControl Grid.Row="1" Name="Sub2VM" />
</Grid>
First of all: the code that I shared, is this the correct way of using Unity + Caliburn?
Now let's say that my Sub1VM uses a model 'M1', but Sub2VM needs to use the same model to display information but not by making another instance of model M1. (singleton)
How does this work now? Show I use a IServiceLocator in each viewmodel constructor? Could somebody share a code sample to explain it?
First of all i agree with McDonnellDean that you should read the article about the Screens, Conductors and Composition (if i were you i would read all the articles before that too to understand how Caliburn.Micro works.). Besides that, you implemented Unity correctly and you can check Unity as IoC Container for Caliburn.Micro for more information. On the other side you are mixing two concepts here, namely Dependency Injection and MVVM. Regarding your question about the model, i would also prefer constructor injection, and if you want a single instance of the model, perhaps you can inject a Factory that creates that model for you and wrap it into two different view models and expose it through the two different properties. At last i really encourage you to read the tutorials (start here), at least the basic topics.
I don't know Unity in particular but your configuration looks correct.
As for your injection points. I would say that rather than doing property injection you should do constructor injection. What you are doing is fine, however you may want to look up screens and conductors, these allow you to add life-cycle to your ViewModels. Typically it would look like this:
Bootstrapper opens ShellViewModel
ShellViewModel takes in MainViewModel via Ctor injection as an IConductorOneActive
MainViewModel takes a collection of IScreens.
ShellViewModel calls MainViewModels activate method on MainViewModel.
See Screens, Conductors and Composition. As I stated above, your way is fine but it is a little on the manual side and means you have to wire everything by hand.
Related
I'm attempting to put together a multi-project application, wherein one of the sub-projects has multiple views for a single presenter. I am using Gin to inject views into my presenters.
The sub-project contains the presenter and the 2 different views. I have 2 separate gin modules, each binding one of the views to the view interface.
As per Thomas Broyer's suggestion on the answer to this post, my Ginjectors are wrapped in a "holder" class that calls the GWT.create on the particular ginjector. The appropriate holder is configured in the gwt.xml file using a replace-with statement.
When I run my project in Dev Mode, I see the alternate view appear as I expect it to. However, when I compile the project, I still only get the default view. Also, only 6 permutations (I would expect more on account of the replace-with logic), and I do not get the view I expect in the different scenarios.
Here is some code to illustrate.
Subproject.gwt.xml contains this:
<replace-with class="com.example.GinjectorDesktopHolder">
<when-type-is class="com.example.GinjectorHolder" />
</replace-with>
<replace-with class="com.example.GinjectorTabletHolder">
<when-type-is class="com.example.GinjectorHolder" />
<when-property-is name="formfactor" value="tablet" />
</replace-with>
The "formfactor" variable is defined in a gwt.xml copied verbatim from GWT's mobilewebapp sample project.
The Holder classes look like this:
public abstract class GinjectorHolder {
public abstract Ginjector getGinjector();
}
public class GinjectorTabletHolder extends GinjectorHolder {
#Override
public Ginjector getGinjector() {
return GWT.create(GinjectorTablet.class);
}
}
public class GinjectorDesktopHolder extends GinjectorHolder {
#Override
public Ginjector getGinjector() {
return GWT.create(GinjectorDesktop.class);
}
}
My Ginjectors look like this:
public interface MyGinjector {
MyView getView();
EventBus getEventBus();
}
#GinModules({ModuleDesktop.class})
public interface GinjectorDesktop extends Ginjector, MyGinjector {}
#GinModules({ModuleTablet.class})
public interface GinjectorTablet extends Ginjector, MyGinjector {}
My modules look like this:
public class ModuleDesktop extends AbstractGinModule {
#Override
protected void configure() {
bind(MyPresenter.View.class).to(DesktopView.class);
}
}
public class ModuleTablet extends AbstractGinModule {
#Override
protected void configure() {
bind(MyPresenter.View.class).to(TabletView.class);
}
}
And finally, in my presenter proxy, basically the entry point into this particular sub-project, I have this line:
GinjectorHolder holder = GWT.create(GinjectorHolder.class);
MyGinjector ginjector = holder.getGinjector();
As mentioned earlier, when I run in Dev Mode and put in breakpoints, I can see the appropriate GinjectorHolder is created. The FormFactor.gwt.xml (linked above) provides a switch for using a URL param to switch to the context you'd like to see. So I can do formfactor=tablet in the URL and the Tablet Ginjector Holder is created.
As mentioned in the comments, removing the line
<collapse-property name="formfactor" values="*"/>
leads to the expected increase in the number of permutations.
Still, it's mysterious, why this is necessary, because usually it should be possible to collapse any properties you like - it just means, that each browser has to download more code, but should still get everything it needs. Could be a bug.
First of all instead of mapping view to viewimpl you can bind it to viewprovider, and then based on user-agent values you can return the appropriate instance to bind to.
My application uses the MVVM pattern. My TextBox is bound to a property of my ViewModel (type string).
When ever the content of the TextBox changed via the user typing, I want to perform some validation.
So, currently, my code is
<TextBox Text="{Binding XmlContentAsString, UpdateSourceTrigger=PropertyChanged}" />
and my ViewModel has this property and field:
private string _xmlContentAsString;
public string XmlContentAsString
{
get { return _xmlContentAsString; }
set
{
if (_xmlContentAsString == value)
return;
_xmlContentAsString = value;
PerformValidiationLogic(value);//This is where I am unsure
}
}
Now, this works but, and I don't know why, I don't like this! It some how feels 'hacked' to include the method in the property.
Can some one please tell me if this is the correct approach when using the MVVM pattern?
There's different type of validations.
For simplistic validating string lengths or allowed characters etc you can use DataAnnotations and put the validation in attributes on your properties. You'll need to include
using System.ComponentModel.DataAnnotations;
then for example to keep string to 9 characters:
[StringLength(9)]
public string StringValue
{
get
{
return stringValue;
}
set
{
this.stringValue = value;
}
}
Then there is validation that is a bit more complex and is effectively enforcing your business logic.
There seem to be many views on how to do this. Ideally it should belong on the model, so that the validation can be reused, but obviously called via the viewmodel.
Personally I will put method calls in the property setters occasionally, to me thats the whole reason for having the ability to create setters and getters - otherwise there's very little point in having anything other than auto properties.
But if it's complex or asynchronous then you can hit issues.
I'd be very careful doing it with UpdateSourceTrigger=PropertyChanged, as that means you'll be firing it every character.
In your example, you perform validation logic on the value, but what would be the result of the validation if it fails? Typically you would want to notify the user of a validation failure. If that is the case, then I suggest IDataErrorInfo (examples can be found here:
http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/).
If you plan on overriding the value without notifying the user, then validating in the setter is acceptable (though still not a fan for more personal reasons).
In my opinion thats the correct approach. I would write a base class for your ViewModel that contains a method that sets the property, call PropertyChanged and validate if some validation rule is attached to that property.
For example:
public abstract class ValidableViewModel
{
private List<ValidationRule> _validationRules;
public ValidableViewModel()
{
_validationRules = new List<ValidationRule>();
}
protected virtual void SetValue<T, T2>(Expression<Func<T>> expression,
ref T2 backend, T2 value)
{
if (EqualityComparer<T2>.Default.Equals(backend, value))
return;
backend = value;
OnPropertyChanged(expression);
Validate(expression.Name, value);
}
protected void Validate(string propertyName, object value)
{
foreach(var validationRule in _validationRules)
{
if(validationRule.PropertyName == propertyName)
validationRule.Execute(value);
}
}
}
The code is not complete, there is missing a lot. But it could be a start ;-)
I personally don't advise putting so much logic in your property. I would use a command bound to an event, ie the lostfocus event of the textbox, and perform your validation there.
I would use something like this:
<TextBox Text="{Binding XmlContentAsString, UpdateSourceTrigger=PropertyChanged}">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="LostFocus">
<interactivity:InvokeCommandAction Command="{Binding LostFocusCommand, Mode=OneWay}"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</TextBox>
then have a command in your view model that is LostFocusCommand wiht your validation logic.
I use mvvm-light and can give a more detailed example for that. (you will need to include the blend interactivity declaration at the top of your xaml)
I'm inexperienced, especially at MVVM, but trying to use ReactiveUI, and I'm not understanding the examples that I'm finding that demonstrate ReactiveCommand. I have used ICommand / DelegateCommand one time before, but this is different, and I'm not getting it.
What I'm trying to do is really simple. Click a button in the view, and have that execute a method in the view model. The examples that I'm finding all involve IObservable<>, and I don't get that, as they don't explanations that are geared to the total noob that I am.
Basically, I'm trying to use this as a learning experience, and what I'd ideally like to do is bind the button's Command property in xaml to a command (however that works, I don't know), which causes a method to execute. No collections, I'd just be passing a single int variable.
Thanks for the help. I really appreciate it.
Edit - Below appears code using Paul Betts' suggestions:
C#
public ReactiveCommand AddToDailyUsed { get; protected set; }
public MainPageVM()
{
Initialize();
AddToDailyUsed = new ReactiveCommand();
AddToDailyUsed.Subscribe(AddToTodayUsedAction => this.AddToDailyUsedExecuted());
}
private object AddToDailyUsedExecuted()
{
MessageBox.Show("AddToDailyUsedAction");
return null;
}
private void AddToDailyUsedAction(object obj)
{
MessageBox.Show("AddToDailyUsedAction");
}
XAML
<Button Content="{Binding Strings.add, Source={StaticResource LocalStrings}}"
Command="{Binding AddToTodayUsed}"
Margin="-5,-10, -10,-10"
Grid.Row="3"
Grid.Column="2" />
Obviously I'm missing something. I inserted break points at the AddToDailyUsedExecuted and AddToDailyUsedAction methods, and they are never reached.
Edit Constructor for code behind the view:
MainPageVM mainPageVM = new MainPageVM();
public MainPage()
{
InitializeComponent();
Speech.Initialize();
DataContext = mainPageVM;
ApplicationBar = new ApplicationBar();
TaskRegistration.RegisterScheduledTask();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
//Shows the rate reminder message, according to the settings of the RateReminder.
(App.Current as App).rateReminder.Notify();
}
So, ReactiveCommand is itself an IObservable<object> - in this case, you can conceptualize IObservable as an Event - this Event fires when the command is invoked (i.e. when the button is pressed). So, in your constructor, you might write:
MyCommand = new ReactiveCommand();
MyCommand.Subscribe(param => this.MyCommandHasExecuted());
However, what's neat about IObservable that isn't true about regular events, is that you can use LINQ on them:
// Now, MyCommandHasExecuted only gets run when the UserName isn't null
MyCommand.Where(param => this.UserName != null)
.Subscribe(param => this.MyCommandHasExecuted());
Update: Your Xaml binds to AddToTodayUsed but your ViewModel command is called AddToDailyUsed. Could that be it?
I am currently working on finding a good project structure for a 3-tier solution to avoid unnecessary work in a new project.
The project will consist of a core product which has
model project which holds the EF code first models
project which has the business logic and communication logic on the server
repository project on the client
silverlight project with views and view models (would like to use caliburn.micro here)
The problem is now that a customer could have some special requirements which could lead to changes in all the above projects. So my thought was that I could just use the base structure and create the same structure for a customer. If there are no changes I would just have empty classes that just extend the base class and add nothing.
Which brings me to the following problems:
Is it a problem in Entity Framework (code first) to have base classes in one project (which is already fully functional) and have another project which may extend the model classes with new field?
Is it a problem in XAML to change a user control? For example if I have a user control that consists of five textboxes in my core and I want to change the second box to a radio button, but nothing else.
I will also accept changes to the project structure, if there is a better way to handle customer specific changes.
Edit: I will probably use the following approach to solve the problem.
Entity Framework:
With code first it seems to be possible to have one model project extend another project. This means I could write something like:
public class CoreAddress{
[Key]
public int AdrId{get; set;}
public string Street {get;set;}
}
public class CustomerAddress : CoreAddress{
public string StreetNumber {get; set;}
}
Only thing that is needed in order to make that work is a line inside the DbContext:
(this as IObjectContextAdapter).ObjectContext.MetadataWorkspace.LoadFromAssembly(typeof(<entity from other assembly>).Assembly);
XAML
To get a similar behavior in XAML I had to use Caliburn.Micro (in combination with MEF) which is a great help here.
I would create UserControls that include ContentControl elements which are dynamically fetched by using MEF. That means I have again a core project with all the views and ViewModels. If I need to exchange a special control somewhere for a customer I change the control to a ContentControl and create a core view and ViewModel for it (which is the same as it was before the change request).
This ViewModel for the ContentControl is annoted with an export interface and ExportMetadata to set a priority of 1.
Now I create another project with another UserControl that has some other control instead of the core control and annotate it again as an export with the same interface but set my priority higher and so the customer specific control is loaded.
Short example for this:
Main user control and view model:
<UserControl x:Class="SilverlightApplication5.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<ContentControl x:Name="Item"/>
<TextBox x:Name="TextItem" Text="asdf"/>
</StackPanel>
</Grid>
</UserControl>
public class TestViewModel : Screen
{
private object viewModel;
private Lazy<IMyViewModel, IPluginMetadata>[] _orderEditorFactory;
[ImportMany(typeof(IMyViewModel), AllowRecomposition = true)]
public Lazy<IMyViewModel, IPluginMetadata>[] OrderEditorFactory
{
get { return _orderEditorFactory; }
set
{
_orderEditorFactory = value;
Item = _orderEditorFactory.OrderByDescending(lazy => lazy.Metadata.Priority).First().Value;
}
}
private object _item;
public object Item
{
get { return _item; }
set
{
_item = value;
NotifyOfPropertyChange(() => Item);
}
}
}
Core Control:
<UserControl x:Class="SilverlightClassLibrary2.MainControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<StackPanel>
<TextBlock x:Name="Test" Text="Text from Core control"/>
</StackPanel>
</UserControl>
[Export(typeof (IMyViewModel))]
[ExportMetadata("Name", "Pluginc")]
[ExportMetadata("Priority", 30)]
public class MainControlViewModel : Screen, IHarnessAware, IMyViewModel
{
}
Customer specific control:
<UserControl x:Class="SilverlightClassLibrary1.CustomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<RadioButton x:Name="Test" Content="{Binding Path=Test}"/>
</Grid>
</UserControl>
[Export(typeof(IMyViewModel))]
[ExportMetadata("Name", "Plugind")]
[ExportMetadata("Priority", 2)]
public class CustomViewModel : MainControlViewModel, IHarnessAware, IMyViewModel
{
}
Export interface:
public interface IMyViewModel
{
}
ExportMetadata interface:
public interface
IPluginMetadata
{
string Name { get; }
[DefaultValue(0)]
int Priority { get; }
}
I did use this to answer the question, because I am still interested in input from other people who may have already solved a simlilar problem.
About project structure:
You can create a base project, with every concern layer you need. Model, business, views, repositories and so on.
Also create some basic flow, for example, a single view with its controller up to the repository. Save it in your codebase, then fork it whenever you need to create a new project.
Now instead of using time to set up, you'll just need some time to customize as your project require.
About XAML: IMHO, if you change the component, you should be sure it returns the same datatype as your control expects. If you swap a textbox for a checkbox, be sure the check returns a string to the controller.
Hi all I'm trying to get to grips with using MVVM, but I'm having a hard time :(, firstly for my question, I'm using the code provide in this MVVM article as a template for my learning.
My question is simple how do expose independent commands, in this case he has create a list of hyperlinks, but how do i create a single button that's fixed and does the same as the 'create new customer' link.
I created something like this(was added to the MainWindowViewModel.cs):
public CommandViewModel exposedCommand
{
get
{
return new CommandViewModel(
Strings.MainWindowViewModel_Command_CreateNewCustomer,
new RelayCommand(param => this.CreateNewCustomer())
);
}
}
and then in the xaml document i created a new button, this was added to the MainWindow.xaml
<Button
Content="Button"
Height="23"
HorizontalAlignment="Left"
Margin="6,303,0,0" Name="button1" VerticalAlignment="Top" Width="150"
Command="{Binding Path=exposedCommand}"
/>
I am not to sure if I'm missing something, or what I am where going wrong,
Soz if I sounding a bit naive I have only just started using MVVM and routed commands and so.
Oh another thing it does load the link it just doesn't create the tab, in other words if you would to add
Console.Writeline("HERE");
to the exposedCommand method
It would print out 'HERE' it just won't do anything when you click the button.
Thanks Any Help would be so appreciated.
Your XAML code is correct.
I also started off with Josh Smith's MVVM article.
Below is a stripped down example of how I implement Commands in my ViewModels:
public class ProjectViewModel : ViewModelBase
{
// Private variable for holding save command
private RelayCommand _saveCommand;
// Other fields here
// Constructors and properties and stuff here
// Command Property for Save button. Bind XAML to "SaveCommand"
public ICommand SaveCommand
{
get
{
if (_saveCommand == null) // Init command on first get
_saveCommand = new RelayCommand(param => this.SaveChanges(), param => this.CanSave);
return _saveCommand;
}
}
/// <summary>
/// Method called when save command is executed
/// </summary>
private void SaveChanges()
{
// Save logic here...
}
/// <summary>
/// Predicate for determining if SaveCommand is enabled
/// </summary>
private bool CanSave
{
get
{
return true; // Replace with SaveCommand predicate logic
}
}
}
If it still does not work, check you runtime output for BindingErrors. If there is a BindingError that implies that the View cannot find the SaveCommand, then your ViewModel is not correctly set to be the DataContext of the View. Let me know in the comments if this is the problem.
You can bind command to only those objects that has an implementation of ICommand interface.
In your example you are binding with view model object.
Instead of this create a property in view model that is a type of RelayCommand and bind this with button.
It should work.
The first thing that concerns me is your the code inside the getter of you property. You're returning a new object EVERY TIME the exposedCommand is accessed. That's not really recommended, you should store that in a backing property like so:
CommandViewModel _exposedCommand;
public CommandViewModel exposedCommand
{
get
{
if (_exposedCommand == null)
{
_exposedCommand = new CommandViewModel(
Strings.MainWindowViewModel_Command_CreateNewCustomer,
new RelayCommand(param => this.CreateNewCustomer()));
}
return _exposedCommand;
}
}
That being said the typical way to present your desired ICommand property is something like this:
RelayCommand _exposedCommand;
public ICommand exposedCommand
{
get
{
if (_exposedCommand == null)
_exposedCommand= new RelayCommand(param => this.CreateNewCustomer());
return _exposedCommand;
}
}