How do I bind wpf with specific datacontext from different classes? - wpf

Hi guys I am very new to WPF. I have two datacontexts in two different classes which are being binded by the elements in the View producing datatriggers, and one or the other wouldn't work as they cannot bind both datacontexts together. How do I bind xaml from two different classes using datacontext? Is there any alternative way could make it easier?
Class A
public Window1()
{
InitializeComponent();
Appointments = new Appointments();
DataContext = Appointments;
}
Class B
private void FilterAppointments()
{
this.DataContext = this;
...

Firstly, you should never use DataContext = this; in any UserControl in a serious WPF Application. Secondly, you should look up the MVVM design pattern, which provides the idea of a view model for each view. Your Window or UserControl are the 'Views' and your view models are simply classes that contain all of the data properties that you need to display in your view.
Therefore, you should declare a view model class (that implements the INotifyPropertyChanged interface) and put whatever you wanted to data bind into that. Finally, you should set that object as the DataContext property value. In that way, you'll have access to all the data that you need.
Looking again at your question, it just occurred to me that you may have set the DataContext to this so that you could use properties that you declared in your Window or UserControl. If this is the case, then you should not set the DataContext to this, instead using a RelativeSource Binding to access the properties. That would free up the actual DataContext to be set however you like. Try this Binding within the Window or UserControl:
<TextBlock Text="{Binding PropertyName, RelativeSource={RelativeSource
AncestorType={x:Type YourPrefix:YourWindowOrControl}}}" />

Related

WPF MVVM and passing viewmodels to a view

I am pretty new to WPF and right now I am trying to get used to the MVVM pattern. Right now I have a simple application in which I have a collection of ViewModels that I display in a grid. When I doubleclick on the row in the grid I want to show a details View of the ViewModel.
The problem I am having right now is that I already have a fully instanced ViewModel, but I can't seem to pass it into the view. When I try to load that View it turns up empty. I already found out that this is due to the fact that when a View gets loaded it creates it's own instance of the backing ViewModel. So obviously I need to get around this behaviour and somehow pass the instanced ViewModel into the View when it is created. I could use a constructor in the View that takes a ViewModel and set the datasource in there. However, taking this approach but would mean that I need to construct the View in the ViewModel and thus making the ViewModel aware of the View. This I something I would like to avoid since I am trying to uphold the MVVM pattern.
So what should I do in this case? Should I just break the MVVM pattern or are there some nice and clean sollutions for this that fit in the MVVM pattern?
There are many ways of passing a view model to a view, as you call it, or setting a view model as the DataContext of either a Window or UserControl, as others may call it. The simplest is just this:
In a view constructor:
public partial class SomeView
{
InitializeComponent();
DataContext = new SomeViewModel();
}
A more MVVM way might be to define DataTemplates in App.xaml for each view model that defines which view each will use:
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourViewModel">
<YourViewsPrefix:YourView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type YourViewModelsPrefix:AnotherViewModel">
<YourViewsPrefix:AnotherView />
</DataTemplate>
Now whenever the Framework comes across an instance of these view model classes, it will render the associated view. You can display them by having a property of the type of your view model using a ContentControl like this:
<ContentControl Content="{Binding YourViewModelProperty}" />
Or even in a collection like this:
<ListBox ItemsSource="{Binding YourViewModelCollectionProperty}" />
"Should I just break the MVVM pattern?"
Well, please consider to learn more about the pattern, to know what it is to "break it". The main purpose of this pattern is to keep responsability clear, thus to obtain testable and maintainable code. There are a lot of ressource for that as show in this question:
MVVM: Tutorial from start to finish?
Anyway to be more specific about your question, what you are looking for is how to set the DataContext.
"somehow pass the instanced ViewModel into the View when it is created"
Yes, you get it, if you assign the dataContext with a viewModel in the constructor of your view, it could work but it it is acceptable only if the viewModel has the responsability to create the view (which could be acceptable in really few situation). You could even write something like that to directly set DataContext from outside your view:
var l_window = new MyView { DataContext = new MyViewModel() };
l_window.Show();
Of course the main drawback is that this code is not testable. If you would like to test it you should use a mockable service to manage the view creation.
A more common solution is to inject the dataContext with an IOC container (like prism). You create all required ViewModel when the software started and you store them in this IOC container. Then, when the view is created, you ask this container to get you an instance of your viewModel.
An example could be: export your viewModel in PRISM:
[Export]
public class MyViewModel {...}
And then Import it in your view:
[Import]
private MyViewModel ViewModel
{
set { this.DataContext = value; }
get { return this.DataContext as MyViewModel; }
}
Hope it helps.
I agree with #Sheridan's answer and would only like to add another way to instantiate a view with a view model: you could use the Factory Pattern, maybe like this:
public class ViewFactory
{
public UIElement Create(object context)
{
// Create the view model
// You can pass in various information by parameters
// as I do with context (Constructor Injection)
var viewModel = new ViewModel(context);
// Create the view and set the view model as data context
var view = new View { DataContext = viewModel };
return view;
}
}
You can call this factory from within a method of your view model and then assign it to e.g. a property that is data bound to the UI. This allows for a bit more flexibility - but #Sheridan's solution is also fine.

Removing UserControl code behind for use with MVVM

I am trying to create a user control using MVVM.
Basically I am trying to wrap a combobox that will pull data from a respository. This will allow me to use the same combobox in many different views in my application. There will be many of the wrapped comboboxes throughout the application.
I was easily able to create this control using a DependencyProperty and code-behind. I am now trying to convert this to MVVM and am having trouble figuring out how to get the value back to /from the ViewModel that in bound to the View where my combobox is located.
Any ideas or suggestions would be greatly appreciated at this point.
Thanks,
Eric
It is perfectly acceptable to use a UserControl that has code behind in it when using MVVM. If you really want to move the functionality out of the UserControl, then move it to whichever parent view models will require it. If you don't want to have the same code repeated in several places, you could encapsulate it in a class and add an instance of that class as a property to each of the relevant view models.
if you have a viewmodel that will pull data from a respository - you can use the same viewmodel in many different viewmodels in your application :)
and if you use a datatemplate your views know how to render this viewmodel
<DataTemplate DataType="{x:Type local:MyPullDataViewmodel}">
<view:MyCoolPullDataShowComboboxUserControl />
</DataTemplate>
It's pretty easy.
Let's say you have:
MyUserControlView.xaml
MYUserControlViewModel.cs
MyMainWindowView.xaml - For your MainWindow view (the one containing the UserControl)
MyMainWindowViewModel.cs - Your MainWindow ViewModel.
And you want to bind List<string> MyListToBind
And leave the code-behind completely empty.
MyUserControlViewModel.cs
public class MyUserControlViewModel
{
private List<string> _MyListToBind;
public List<string> MyListToBind { get; set;}
}
MyMainWindowViewModel.cs
public class MyUserControlViewModel
{
private MyUserControlViewModel _MyControlViewModel;
public MyUserControlViewModel MyControlViewModel { get; set;}
}
MyMainWindowView.xaml
<Window ...
xmlns:my="clr-namespace:NamespaceContainingYourUserControlView>
<my:MyUserControlView DataContext = "{Binding Path= MyControlViewModel}"/>
</Window>
MyUserControlView.xaml
<UserControl ...>
<DataGrid ItemsSource = "{Binding Path= MyListToBind}" .../>
...
</DataGrid>
</UserControl>
This doesn't support ViewModel updating View. To do that You have to use either DependencyProperties as you did instead of normal variables (as i did) or use INotifyPropertyChanged(google it, you'll get tons of examples) and OnPropertyChanged event.
You might read up on DataTemplates they are really useful in data binding.
You can find this usefeul:
http://www.youtube.com/watch?v=BClf7GZR0DQ
I sure as hell did !
Good luck.

MVVM + UserControl + Dependency Property

Alright, this is somewhat related to this question: WPF Printing multiple pages from a single View Model
I tried to follow the advice given there but now I am stuck.
My application uses a MainView.xaml and the appropriate MainViewViewModel.cs, I am using MVVM Light in the background.
Now - according to the post - it seems I have to do the following:
Create a user control
Expose some properties from the user control
Make sure the view model shows these properties
The idea is clear but I am stuck when trying to notify each other.
My user control (UcTest.xaml) exposes a Dependency Property:
public string SpecialText
{
get { return (string)GetValue(SpecialTextProperty); }
set
{
SetValue(SpecialTextProperty, value);
}
}
// Using a DependencyProperty as the backing store for SpecialText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SpecialTextProperty =
DependencyProperty.Register("SpecialText", typeof(string), typeof(UcTest), new PropertyMetadata(new PropertyChangedCallback(SpecialTextChangedPropertyCallback)));
private static void SpecialTextChangedPropertyCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Do something
Debug.WriteLine("Ffgdgf");
}
Alright, so I do now have a user control which has some dependency properties. Yet, these properties are completely separated from my ViewModel properties (those are the ones which shall be displayed).
So basically I have two possibilities:
How can I now tell my ViewModel for the UserControl that some properties have changed?
Is there a possibility to forget about the dependency properties and access the view model directly?
Additional info #1:
I have uploaded a (simple) example of what I am trying to do here: Example Project. I would like to change the value of the label in UserControl1 (via the binding property in the ViewModel for UserControl1) from my MainViewViewModel.
You would usually bind the UserControl's property to the ViewModel property. A two-way binding would work in both directions, from ViewModel to View and vice versa.
<Window x:Class="TestApplication.MainWindow" ...>
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Grid>
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
</Grid>
</Window>
To directly access the ViewModel object in the above example, you could simply cast the UserControl's DataContext property to the ViewModel type. The DataContext is inherited from the MainWindow.
var viewModel = DataContext as MyViewModel;
var property = viewModel.MyViewModelProperty;
You could of course also directly assign a specialized ViewModel instance to the UserControl's DataContext:
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
<local:UcTest.DataContext>
<local:UserControlViewModel/>
</local:UcTest.DataContext>
</local:UcTest>
or you may create the ViewModel instance as a resource in a resource dictionary and assign the DataContext like this
<local:UcTest DataContext="{StaticResource MyUserControlViewModel}"
SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
Alright, after hours of googling it appears that the "correct" approach to this is not to do it at all. The general approach is to keep the data in your MainViewModel and not use an additional ViewModel for the UserControl (which I find a little ... well .. not so good). The main problem is that there is no easy mechanism to get the Data from the Dependency Property to the ViewModel.
For printing, I have now gone back to doing it purely in code.

How can I easily expose bindable properties of nested controls from a custom UserControl?

My first question here on the Stack. Forgive me for the bad explanation in advance.
I am working on my first MVVM application (Silverlight). I have a custom user control that contains a ListBox to show navigation items. This control is placed in my main xaml page. I don't know if I need to create a composite view model (my main page view model) with a view model especially for the custom control in it or if there is some way to elevate the ListBox properties that I need to bind to.
Through XAML I don't know how to bind, let's say, the ItemsSource property of the ListBox inside the custom control to my main page viewmodel. Basically, I'm at the point that I am questioning my design decision for trying to bind the custom control through my main page view model.
What I have done so far is create dependency properties for the custom control and try to tunnel those dependency properties down to the ListBox properties. I've achieved success with this method for ItemsSource but am having issues with SelectedItem.
Even if I do get SelectedItem to work, it still feels Wrong. Thanks for any advice in advance.
The UserControl should inherit the DataContext from its parent control, unless you are setting it directly. You can then bind to the properties on your view model from your UserControl.
If you would like to create a ViewModel specifically for the UserControl, you can also do that. You would then expose it as a property on your main ViewModel, and bind to it in the MainPage. Example:
public class MainViewModel
{
public ChildViewModel ChildInfo { get; private set; }
}
And then in the view:
<Grid>
...
<lcl:ChildView DataContext="{Binding ChildInfo}" />
...
</Grid>
Your ChildViewModel would then contain properties like SelectedItem to bind your ListBox to.

How is the DataContext typically set?

I've created a new WPF project, and threw in a DataGrid. Now I'm trying to figure out the easiest way to bind a collection of data to it.
The example I downloaded seems to do it in the window c'tor:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
But then the bindings don't seem to appear in the Visual Studio's Properties window. I'm pretty sure there's a way to set the data context in XAML too... it would make me even happier if I could do it directly through the properties window, but all the Binding options are empty. What's the typical approach?
Edit: At 14 minutes, he starts to talk about other methods of setting the data context, such as static resources, and some "injection" method. I want to learn more about those!
What I typically do is use MVVM. You can implement a simplified version by setting the data context in your code behind and having a model type class that holds your data.
Example: In your code behind
DataContext = Model; // where Model is an instance of your model
then in your view
<DataGrid .... ItemsSource="{Binding SomeProperty}">....
Where SomeProperty is an enumerable property on your view model
You can also set a data context in XAML by using the DataContext property
<uc:SomeUserControl DataContext="{Binding AnotherProperty}"....
This will run your user control within the DataContext of the AnotherProperty on your model.
Note that this is grosely simplified but it'll get you on your way.
Have a look at the MVVM design pattern. This pattern is very suitable for wpf applications.
There is described where to store your data and how to bind your ui to the data.

Resources