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>();
}
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.
I am new to WPF and the MVVM pattern and a little confused on how to achieve data-binding to pre-existing instances. I understand that in MVVM you have the Model="business logic", ViewModel="presentation logic", View="actual presentation".
Unfortunately I am currently unable to figure out how you would go about binding your View to existing instances of the ViewModel and corresponding Model. I have found for example this MVVM tutorial, which I liked, but it also just creates the Model inside the ViewModel.
So how would I go about using previously instantiated Models from my underlying application?
EDIT: So I am getting around to trying out the method proposed by Gusdor in a small test project, but cannot get it to work. I am getting an error 'WpfBindingTesting.App.MyViewModel' is a 'field', but used like a 'type' in App.xaml.cs (see below). I hope somebody catches what I am doing wrong. Here is my code:
ViewModel:
namespace WpfBindingTesting
{
class ViewModel
{
private List<string> names;
public List<string> Names
{
get { return names; }
set { names = value; }
}
public ViewModel()
{
Names = new List<string> { "string1", "string2", "string3" };
}
}
}
MainWindow.xaml.cs:
namespace WpfBindingTesting
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindow.xaml:
<Window x:Class="WpfBindingTesting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext = "{Binding RelativeSource={RelativeSource Mode=Self}}"
>
<StackPanel>
<ListBox
ItemsSource="{Binding Path=Names}"
Height="50"
>
</ListBox>
</StackPanel>
</Window>
App.xaml.cs:
namespace WpfBindingTesting
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App(){
MainWindow View = new MainWindow();
ViewModel MyViewModel = new ViewModel();
View.DataContext = MyViewModel; // this give error: 'WpfBindingTesting.App.MyViewModel' is a 'field', but used like a 'type'
//view.DataContext = new ViewModel();
//view.Show();
}
}
}
Generally speaking, we assign view models to the view's DataContext property.
You can do this from code as long as you have a reference to the view. Here is the code:
C Sharp
Window view = new Window();
// add a view model
view.DataContext = new ViewModel() { Title = "View Model 1" };
view.Show();
// add another view model to demonstrate the binding
view.DataContext = new ViewModel() { Title = "View Model 2" };
XAML
The XAML for window might look something like this.
<Window Title={Binding Title}>
</Window>
Notice how the Source or ElementName properties remains unspecified in the binding declaration? This instructs the Binding to use the current DataContext as the source.
Model = business.
View = what the user see, and really ain't about logic (this is a lie ofcourse, but this is what you strive for).
ViewModel = what glues them together. It's your adapter that gives the View the info it needs from the Model you have.
I suggest having a look at an MVVM platform (like MVVM Light), which gives you lot of the stuff you need to do "for free" when you begin.
As a guide, here are 3 options for doing your binding:
1.1 On the XAML, set DataContext = "{Binding relativeSource={RelativeSource self}}
1.2 have a property on the code behind (your xaml.cs)
1.3 Initialize the property BEFORE the InitializeComponent() in the constructor.
2.1 Igonre DataContext on the XAML
2.2 In the code behind, start with InitializeComponent();
2.3 have a line like DataContext = this; in the constructor
2.4 Initialize your properties
3.1 Give the window or UserControl a name: Name = "MyWindow" (in the xaml)
3.2 Bind to an element: ItemSource={Binding ElementName = MyWindow, path =... }"
3.3 Initialize the property BEFORE the InitializeComponent() in the constructor.
And last but not least, have a look at my article "The Big MVVM Template", which you can follow and will give you a good handle of the basic, plus you'll have a running fully documented example on top :) .
Your question is more about design then coding. I think if you have existing instances of viewmodels already and you just want to reuse/reassign them in new views. then probably you need to have a look at IOC Containers( as actually you want some sort of dependency injection here)
To be very simple use an IOC container to maintain the lifetime of your viewModel.
Configure the container to give you the required ViewModel according to your need i.e a new object or an existing view model object.
Unity Container
In unity Container you can just register your objects and resolve them at future as you need them. using below syntax simplay:
Register:
1.MyUnityConatiner.RegisterInstance<IMyService>(myDataService);
or
2.MyContainer.RegisterInstance<IMyService>("Email", myEmailService);
Resolve :
1.MyUnityConatiner.Resolve<IMyService>();
or
2.MyConatiner.Resolve<IMyService>("Email");
I know this will seem to you a little deviated solution but if you learn to use this you will have a different perspective on your problem and how to reuse the objects(model/viewmodel).
I'd like to reuse a view for 2 different viewmodels, in my example MyEntityEditViewModel and MyEntityCreateViewModel. The view is basically just a form with a Save button, so pretty common layout.
I created both view models along with a parent view / view model (MyEntitySummaryViewModel) and now I'd like to define the form view using a ContentControl.
Summary view:
<ContentControl x:Name="ActiveItem" cal:View.Model="{Binding ActiveItem}" cal:View.Context="MyEntityDetailView" />
MyEntitySummaryViewModel:
public MyEntity SelectedEntity {
get { return _selectedEntity; }
set {
_selectedEntity = value;
NotifyOfPropertyChange();
ActivateItem(new MyEntityEditViewModel(_selectedEntitity));
}
}
public void Create() {
ActivateItem(new MyEntityCreateViewModel(new MyEntity()));
}
My problem is now that Caliburn tries to locate a 'MyEntityEditView' due to it's view locating conventions, even if I strictly defined the context of the ContentControl as a custom view. Is there a way around this? Or am I doing something completely wrong here?
If my understanding is right, You want 2 type of ViewModel to point on the same view. If so juste create a base classe for your Entity (EntityBaseViewModel) and Create a View (EntityBaseView).
To Bind a ContentControl set his x:Name so the name match a Property of your ViewModel.
Example:
View (ShellView):
<ContentControl x:Name="SelectedEntity"/>
ViewModel (ShellViewModel):
public EntityBaseViewModel SelectedEntity
{
get
{
return this._selectedEntity;
}
set
{
this._selectedEntity = value;
this.NotifyOfPropertyChange(() => SelectedEntity);
}
}
And Caliburn will find the View for the ViewModel and bind the DataContext if you did create your ViewModel / View along the naming convention like you said.
A little late to the party, but perhaps this will help someone. This video helped a lot for me - (Tim Corey, WPF and Caliburn with MVVM)
Setting up the ShellView with a control that points to ActiveItem as you mentioned, allows that control to display whatever view you tell it from the ShellViewModel code. I was also using Fody with this project, so that took care of the change notifications so you won't see those listed in code.
ShellView -
<Button x:Name="LoadMainPage" />
<Button x:Name="LoadSecondPage" />
<ContentControl x:Name="ActiveItem"/>
The ShellViewModel -
public class ShellViewModel : Conductor<object>.Collection.OneActive
{
public MainPageViewModel MainPageVM = new MainPageViewModel();
public SecondPageViewModel SecondPageVM = new SecondPageViewModel();
public ShellViewModel()
{
LoadMainPage(); // auto load main page on startup
}
public void LoadMainPage()
{
ActivateItem(MainPageVM);
}
public void LoadSecondPage()
{
ActivateItem(SecondPageVM);
}
}
Instead of creating a new instance of a ViewModel when using ActivateItem, you're just re-using the initial ones created. Or, if you DO prefer to create another instance each time that particular view is launched, then simply use the ActivateItem as you already have.
In your SecondPageViewModel for the view, which will occupy the space in the ContentControl for ActiveItem -
public class SecondPageViewModel : Screen
SecondPageView.xaml added as a User Control (and any other sub/child views you want to create) -
<UserControl x:Class="MyNamespace.Views.SecondPageView"
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:MyNamespace.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</UserControl>
This will allow you to flip back and forth between whatever views you want from a base view/viewmodel, and display the contents of the child views (however many you want) that you choose within the ContentControl box.
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.