I'm still in the process of getting into WPF and also decided to give Simple Injector a spin. I created a very simple test project based on the WPF integration example provided in the Simple Injector documentation. My current code is pretty much identical to that example or arguably even simpler as I do not yet have any service objects.
What is different is that I do have a simple View with an accompanying ViewModel that is currently the only thing that is on the MainWindow:
<Window x:Class="WpfPlayground.MainWindow"
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"
xmlns:local="clr-namespace:WpfPlayground"
xmlns:view="clr-namespace:WpfPlayground.View"
mc:Ignorable="d"
Title="MyApp" Height="450" Width="800">
<Grid>
<view:MyView />
</Grid>
</Window>
My MainWindowViewModel class exists but is currently still empty. Its constructor has no parameters.
This is what I have so far of MyView (the XAML is still just an empty <Grid />):
public partial class MyView : UserControl
{
public MyView(MyViewModel myViewModel)
{
InitializeComponent();
DataContext = myViewModel;
}
}
This is the Bootstrap() method from my Program class:
private static Container Bootstrap()
{
var container = new Container();
container.Register<MainWindow>();
container.Register<MainWindowViewModel>();
container.Register<MyView>();
container.Register<MyViewModel>();
container.Verify();
return container;
}
Now, I get a NullReferenceException inside the MainWindow constructor when InitializeComponent() is called ("Object reference not set to an instance of an object"). The call stack points back to the call to container.Verify() in Program.Bootstrap() (see above). The constructors of neither MyViewModel nor MyView are ever reached.
If I were to take a guess I'd say that the application is not going through the Container to get the instance of MyView. One of the reasons I did this experiment was actually because I was interested to see how Simple Injector would accomplish this as the documentation seemed to imply that this would somehow happen automatically. Could it be there is a step missing from the documentation? Something to register some sort of interceptor? Is this maybe not the right way to use Simple Injector with WPF/XAML after all? Hopefully I won't have to create controls in code?
If I were to take a guess I'd say that the application is not going through the Container to get the instance of MyView
Yes, SimpleInjector is not able to resolve a view that you define inline in the XAML markup like this.
When the InitializeComponent() method is called at runtime, the built-in parser tries to create an instance of the view that you have defined in your XAML markup wihout any knowledge about or reference to any container.
So your code fails when trying to resolve an instance of the MainWindow, because an exception is thrown from the call to InitializeComponent() in its constructor.
The example in the docs that you refer to doesn't define any inline views. This only works for views that have a parameterless constructor.
Related
I have an WPF UserControl in which I try to initialize the DataContext in two ways:
First way
<UserControl x:Class="my.UI.Views.MyControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:my.UI.Views"
xmlns:vm="clr-namespace:my.UI.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:MyControlViewModel}"
d:DesignHeight="450" d:DesignWidth="800">
I have noticed that using this way I still need to initialize the DataContext in the subyacent code (xaml.cs) in the constructor as following:
private readonly MyControlViewModel viewModel = new MyControlViewModel();
public MyControlView()
{
InitializeComponent();
this.DataContext = this.viewModel;
}
Otherwise, if I don't initialize it in the constructor as well, then when I try to get access to the DataContext by doing the following I get a null:
MyControlViewModel vm = (MyControlViewModel) this.myControlView?.DataContext;
Note:
this.myControlView makes reference to the correct WPF usercontrol. This is embedded in an elementhost.
So in this way I need to initialize the DataContext in two places, in the view (xaml) and also in the subyacent code (xaml.cs). I am wondering, Why do I need to initialize it in both places and not only in one instead?
Second way
<UserControl x:Class="my.UI.Views.MyControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:my.UI.Views"
xmlns:vm="clr-namespace:my.UI.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:MyControlViewModel/>
</UserControl.DataContext>
Using this second way, initializing the DataContext in the view (xaml) is enough, no need to initialize it again in the subyacent code (xaml.cs). In this case when I do the same, I mean, I try getting access to the DataContext is working:
MyControlViewModel vm = (MyControlViewModel) this.myControlView?.DataContext;
So:
What's the difference between the two ways? Which is best?
In the first way, Why do I need to initialize it in both places and not only in one instead?
d:DataContext is a design time data context, to enable you to visualise your data and page layout.
The code behind example is where it has been set at runtime, but isn't advisable in this form (although it works) as its inflexible in a final product.
Use of dependency Injection is a worthwhile goal, although it takes a little more effort initially as it would ultimately allow for a quick substitution of the ViewModel, especially if you wished to test an application page layout and navigation without interaction with final application resources.
Consider:
private readonly IMyControlViewModel viewModel;
public MyControlView(IMyControlViewModel viewModel)
{
InitializeComponent();
if(viewModel==null) viewModel = new MockMyControlViewModel(); //example of optional code
this.DataContext = viewModel;
}
MockMyControlViewModel can be used for design time data context and at runtime until such time as a real implementation has been produced.
You could use a Dependency Injection container or utilize the Factory Pattern to code much of the functionality yourself.
For example a concrete instance of IMyControlViewModel can then be injected into the View by a NavigationService that controls the actual switching between application pages.
I am developing a MVVM application
I want to bind to a WindowsFormHost in my application.But WindowsFormHost is not a dependancyproperty.
So in my view model create a new WindowsFormHost and bind it to a child of a contentcontrol.But i encountered that binding called twice when i run the programe.
Any suggestions??
My XAML
<Window x:Class="Demo.View.area"
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"
xmlns:local="clr-namespace:Demo.View"
mc:Ignorable="d"
Title="area" Height="300" Width="300">
<ContentControl Content="{Binding myWindow}" />
</Window>
my viewModel.cs contain following implementation
public WindowsFormsHost myWindow
{
get
{
return new WindowsFormsHost() { Child = newWindow };
//newWindow defined in another place
}
}
The root issue here is that you create a new object every time the property getter is called. There are very few scenarios in which that would be a good idea, and it certainly isn't in your case.
We can fix the immediate problem by just not doing that. For example:
private readonly Lazy<WindowsFormsHost> _myWindow =
new Lazy<WindowsFormsHost>(() => new WindowsFormsHost() { Child = newWindow });
public WindowsFormsHost myWindow
{
get { return _myWindow.Value; }
}
That will defer creation of the object until the first time the property getter is called, but after that will always return the same value.
But that doesn't really solve your broader problem. There's not enough context in your question to understand why you think this code is useful, but I think it very unlikely to be the code you really want.
Your view model should not be creating view objects at all. You can and should be declaring the WindowsFormsHost and its child in the XAML, just like you would any other view component.
It also doesn't make sense to contain a WindowsFormsHost component as the child of a ContentControl. It's a perfectly valid control all by itself, and does not need a container.
This answer should address the specific issue you're asking about. But I strongly encourage you to rethink your design. It seems all wrong. If you want help with it, post a new question that includes a good [mcve] that has enough detail that shows exactly what broader goal you're actually trying to solve.
This question already has answers here:
What do I need to further qualify the DataContext for a binding?
(2 answers)
Closed 7 years ago.
I can specify DataContext like this:
<Window ... >
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
...
</Window>
And in this case WPF will create an object of type MainViewModel and assign it to the window's DataContext property (this happens inside Window's InitializeComponent() method).
But what if my ViewModel doesn't have a default constructor. Or what if I want to initialize ViewModel and assign it to DataContext after Window.InitializeComponent() is executed (inside Window's constructor or from the same code which instantiates the window) - in this case WPF creates a ViewModel (inside InitializeComponent()), assigns it to window's DataContext and then I overwrite it with another instance of ViewModel (I'm worried about unnecessary object instantiation here).
I would like to be able to specify just a type of ViewModel, so I would get design-time warning if I misspell a property name inside {Binding} (or after renaming the property), or I could Go To Declaration by clicking (in XAML) on a property name inside {Binding PropertyName}.
That's the tricky part if you do the do-it-yourself MVVM.
Your options, basically:
Use Dependency Injection
You could inject the ViewModel in your Page/Window's constructor and assign it within it.
This has a few downsides though.
harder to use design-time view models
Views can't be instantiated from XAML anymore
ViewModel First with Navigation Service
You'd resolve your ViewModels and do all your navigation via a navigation service. In your ViewModels you pass the an INavigationService. You could navigate to a view by using ViewModel type. Inside it instantiate the ViewModel via Dependency Injection, then instantiate the View (based on naming conventions or via DI configuration)
That's a bit better, but still won't allow you to instantiate the Views within XAML. Big plus is, it allows you easily to pass parameters to the ViewModel (having the ViewModels implement INavigationAware property with NavigatedTo method, which is called after instantiation and passing the parameter to)
ViewModelLocator / Attached Property/Behavior
With this one, you would create an attached property, which you either set to true (i.e. autowire) or to a ViewModel type (to have more control over the ViewModel instantiated) and the find and resolve the ViewModel and assign it.
It basically gives all of the advantages above plus instantiation form View.
Last one is basically what Microsoft's MVVM framework "Prism" does (navigation service navigationService.Navigate("MyPage", myParameterForViewModel), DataContext instantiation and assignment from XAML via autowireing (In XAML: prism:ViewModelLocator.AutoWireViewModel="True").
That being said, it's best to use an matured MVVM Framework which does this parts of your wiring (even if you decide not to use the base classes such as BindableBase or whatever it's called in said framework).
As for design-time ViewModel/auto-completition for ViewModels:
You can use Blend's Design-Time attributes to do this. You need to add the Blend assembly references first. Then you can add xmlns:d="http://schemas.microsoft.com/expression/blend/2008" namespace into your page/view.
Then you can bind it into your page via d:DataContext="{d:DesignInstance my:DesignTimeViewModel, IsDesignTimeCreatable=True}. Notice the d: before the DataContext, this is important. This DataContext will only be used in the Designer (Visual Studio XAML Designer or in Blend). This is to prevent interfering with the normal DataContext (without the prefix).
Example:
<Window x:Class="WpfApplication1.Window2"
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:myApp="clr-namespace:WpfApplication1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance myApp:Window2ViewModel, IsDesignTimeCreatable=True}">
<Grid>
<TextBlock Text ="{Binding Test}"/>
</Grid>
</Window>
If you use Interfaces for your ViewModels, it's pretty fast to create the Design Instance, by simply having Visual Studio implement all the Interface property and give it some default values (for property so you have example data in your ViewModel to verify bindings work correctly).
This requires you to create separate design-time ViewModels and your actual ViewModels, which isn't as bad as it sounds. This gives your UI designer the chance to work with it, even if the real ViewModel isn't finished/implemented yet.
So I'm pretty new to WPF and MVVM, and while I understand the premise, a lot of this stuff is like trying to read hieroglyphs for me.
Basically, my situation is this: I'm using Activiz, a c# wrapper for VTK, which is an image processing/visualization library. So, in this library, there's a WinForms control called vtk:RenderWindowControl, which is an opengl control containing the class that handles all of the visualization functionality. I think it'd be easier to just use WinForms, but that's not really an option for me.
So, to use vtk:RenderWindowControl in a WPF application, I just need to shove it into a WindowsFormsHost and then I can use it more or less just like the example code, in the code behind (if that's the correct term for the .xaml.cs file)
That's fine for a test app, but in practice, I'd like to follow MVVM if possible. This is where I've run into a wall. If "renderControl" lives in the View class, how can I reference it and use it from the ViewModel? I think binding is the answer to that question, but I only really know how to do that for simple types and commands.
Following ideas in another thread I found, I managed to set up something like this answer
My codebehind looks like this:
public partial class RenderPanel_View : UserControl
{
public static readonly new DependencyProperty RWControlProperty =
DependencyProperty.Register("RWControl", typeof(RenderWindowControl), typeof(RenderPanel_View), new PropertyMetadata(null));
public RenderWindowControl RWControl
{
get { return (RenderWindowControl)GetValue(RWControlProperty); }
set { SetValue(RWControlProperty, value); }
}
public RenderPanel_View()
{
// This is necessary to stop the rendercontrolwindow from trying to load in the
// designer, and crashing the Visual Studio.
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) {
this.Height = 300;
this.Width = 300;
return;
}
InitializeComponent();
this.RWControl = new RenderWindowControl();
this.RWControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.WFHost.Child = this.RWControl;
}
}
My .xaml looks like this
<UserControl x:Class="vtkMVVMTest.RenderPanel.RenderPanel_View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vtk="clr-namespace:Kitware.VTK;assembly=Kitware.VTK"
xmlns:rp="clr-namespace:vtkMVVMTest.RenderPanel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
RWControl="{Binding VMControlProperty}">
<Grid>
<WindowsFormsHost x:Name ="WFHost"/>
</Grid>
</UserControl>
So two things. One, That last line of the xaml header is an error, "The member 'RWControl' is not recognized or accessible". I don't really understand why. Second, for what I'm guessing is the ViewModel half of the equation, how is VMControlProperty supposed to be defined?
Am I at least on the right track here, or is this way off base?
Some controls are not MVVM friendly and you have make the ViewModel aware of View interface and allow interact with it directly. Do not open the whole control to the ViewModel it will ruin the ability to write tests, put an interface on top for example IRenderPanelView and open in the interface only the functionality you need to access from ViewModel. You can then create a DP property of this type in the view, set it in the constructor and bind it to ViewModel.View property in xaml.
I want to create multiple instances of a SearchTagView and bind each of those to its own SearchTagViewModel.
I'm using the WAF framework for WPF which follows the MVVM-concept and furthermore Dependency injection (IoC). The application is developed for the SUR40 using the SurfaceSDK.
This means that the views are instantiated like this:
TagVisualizationDefinition tagDefinition = new TagVisualizationDefinition();
tagDefinition.Source = new Uri("Views/SearchTagView.xaml", UriKind.Relative);
tagVisualizer.Definitions.Add(tagDefinition);
tagVisualizer is a control element of type TagVisualizer in SearchView. So multiple SearchTagViews are placed in one SearchView. This works.
The problem is that because of dependency injection all SearchTagViews use the same SearchTagViewModel:
xmlns:vm="clr-namespace:Applications.ViewModels;assembly=Applications"
How can I use a different ViewModel for each View following the MVVM?
Every IoC framework that I've seen allows you to register types in two ways:
As a single instance (the same instance of the class is returned
each time)
As a new instance each time it is resolved / injected
You'll need to figure out how to do #2 within your IoC framework.
The other MVVM-centric option is to declare a DataTemplate in your UI and add the ViewModels directly to the UI and have WPF automatically create the Views for it.
edit:
It looks like WAF is using MEF for IoC. You'll need to supply the [Export] attribute and specify it as non-shared.
Here's how to set a PartCreationPolicy with MEF from a previous question
You can use a view model locator for this purpose. Check out this.
Configure the view model locator to return a new instance of view model every time.
An example is given below, using mvvm light(even though you need not use mvvm light to use view model locator).
public class ViewModelLocator
{
public ViewModel1 VM1
{
get
{
return new ViewModel1();
}
}
}
In app.xaml, define a key for view model locator.If you nuget mvvmlight, this will happen automatically
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:WpfApplicationmvvmlight.ViewModel" />
</Application.Resources>
In the view, use the locator
<UserControl x:Class="WpfApplicationmvvmlight.View2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding Source={StaticResource Locator}, Path=VM1}">
</UserControl>
Alan's hint concerning the non-shared attribute was good, but I couldn't use it to solve my problem. The problem was that the MEF is working before I init my TagVisualizationDefinitions.
The only solution was to set the binding in code-behind of the parent user control in the method for the event TagVisualization_Loaded