Difference between different ways of initializing dataContext in WPF - c#

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.

Related

Can I dynamically size dialogs that are opened with IDialogService in Prism?

Is there any way to dynamically size a dialog in code in Prism using IDialogService? I would like to adjust the size of my dialog based on the user's screen resolution.
Here's how I'm opening my dialog:
public class MainViewModel
{
// Gets injected in the constructor
private IDialogService dialogService;
private void OpenDialog()
{
this.dialogService.ShowDialog(
nameof(MyDialog),
new DialogParameters(),
result => { });
}
}
Here's what my dialog looks like in XAML
<UserControl
x:Class="MyApplication.MyDialog"
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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<!-- various controls -->
</UserControl>
The easiest way is to expose Width and Height properties in your view model and bind to them. The drawback is that width and height are purely view related and should not not be accessible in a view model in pure MVVM.
I would like to adjust the size of my dialog based on the user's screen resolution.
If the size adjustment is related to the user's screen resolution, you should consider creating an attached behavior for either your custom dialog window or the dialog user control. This way you can encapsulate the logic for screen resolution adaption in a reusable component that resides in XAML and maintains separation of view and view model concerns. Furthermore, you will have access to the associated window or user control in the behavior, which makes it easier to handle even more compley scenarios without violating MVVM principles.

NullReferenceException in SimpleInjector.Container.Verify when instancing view via XAML

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.

WPF binding to contentcontrol.child called twice

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.

Determine DataContext of a WPF control at design time

How to determine the DataContext of a specific control at design time in a WPF application?
DataContext might be specifically set in XAML, inherited or set somewhere in code and sometimes it is hard to figure out at design time which class the bindings are referring to.
What I usually try to do to find the DataContext class is to search the binding name in VS. For example I see the binding is something like
ItemSource = {Binding Items}
...I will search the text "Items" in order to get to the class but VS sometimes fails to find the searched text (I have several projects in the solution).
I would like to add an approach to StepUpĀ“s listing:
The design instance:
Just like you can define a run time data context, you can add a data context that is specifically for the design time via:
<Usercontrol x:Class="MyUserControl"
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:viewModels="clr-namespace:MyProject.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MyViewModel}"
d:DesignHeight="300"
d:DesignWidth="600"
mc:Ignorable="d">
</UserControl>
In Visual Studio you can then use IntelliSense for bindable properties and if your view model has an uncomplicated or even parameterfree constructor, it will be created in the Designer and you can test trigger or converters without having to start your application.
DataContext of Control is ViewModel. So there are many ways to set ViewModel for DataContext of View, and if you find your ViewModel, but there is no Items property in ViewModel, then it means that you should add such property to get work binding.
Also, I recommend you to see Debug->Windows->Output window where you can see binding info. That is you can know binding errors.
To conclude, I would like to show ways of setting ViewModel to DataContext:
There are many approaches to set DataContext:
The first approach. In view:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
The second approach. You should override OnStartUp() method of App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow app = new MainWindow();
ProductViewModel context = new ProductViewModel();
app.DataContext = context;
app.Show();
}
}
The third approach. In constructor of Windows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext=new MainWindowViewModel();
}
}
The fourth approach. You can set DataContext through DependencyInjection by UnityContainer or another IoC container. But DependencyInjection, Prism and UnityContainer are other questions and goes from this scope of the question. Just for example:
protected override void RegisterTypes()
{
unityContainer.RegisterType<object, ItemControl>("ModuleAUpper");
unityContainer.RegisterType<IViewModelItemControl, ViewModelItemControl>();
unityContainer.RegisterTypeForNavigation<ItemControl>();
}

Multiple views binding to multiple ViewModels

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

Categories

Resources