I have created a user control in wpf which consists of a textbox and a listview. Now, I want to bind a class object to the list view from the form where I will use this control. So, basically I want to dynamically set the binding for the listview.
This might be simple but I am new to wpf which is making this a giant task for me. Please suggest a solution for this.
You need to bind your ListView's ItemsSource property to a collection of your class instances (I recommend using ObservableCollection for this). You can place the collection into ListView's DataContext, for example. Then the binding will be just
<ListView ItemsSource={Binding} .../>
It is more useful to create a class to contain this collection along with any other objects that your UserControl may require and pass the instance of this class to UserControl's DataContext. This refers to MVVM pattern.
EDIT
You can set DataContext either in code-behind or in xaml. Let's say we have a class with the collection:
public class ViewModel
{
public ViewModel()
{
Products = new ObservableCollection<Product>()
{
new Product("book"),
new Product("chair"),
new Product("table"),
new Product("bookshelf"),
}
}
ObservableCollection<Product> Products { get; set; }
}
Here's how to set UserControl's DataContext in code:
userControl.DataContext = new ViewModel();
where userControl is an instance of your UserControl.
Here's how to do in in xaml:
<UserControl ...
xmlns:vm="namespace_containing_class_ViewModel">
<UserControl.DataContext>
<!-- Following line instantiates the ViewModel class and calls its parameterless constructor -->
<vm:ViewModel />
</UserControl.DataContext>
...
<ListView ItemsSource="{Binding Products}"/>
...
</UserControl>
Both variants do exactly the same thing. Note that ItemsSource="{Binding Products}" means that the Binding.Path property is set to "Products" and this path will be searched for in ListView's DataContext. If ListView's DataContext is not explicitly set (as in the mentioned above case) it's DataContext is the same as its Parent's DataContext. If the parent's DataContext is not set - then it's the same as parent's parent's DataContext and so on. So in this case ListView's DataContext is the same as UserControl's DataContext and is a ViewModel class instance, that containes collection of Products. Ta-da! :)
Related
For context, I am building a universal Windows Store app.
I'm just starting to learn C# and MVVM patterns and I need help correctly implementing binding.
I have followed this tutorial (Binding) and understand how it works, however in this example the code which does the binding is stored within the View Class.
public partial class MainPage : Page
{
public ObservableCollection<TermTest> MyTerms = new ObservableCollection<TermTest>();
public MainPage()
{
this.InitializeComponent();
MyTerms.Add(new TermTest("BNC", "Wire"));
MyTerms.Add(new TermTest("Lens", "Collects light"));
this.DataContext = new CollectionViewSource { Source = MyTerms };
}
As I understand it however this is poor design. In my implementation I will be using my Model to retrieve data which will get put into an Observable Collection. Then in my ViewModel I will want to bind the ObservableCollection to the XAML controls in which it is being used, not send the Collection to the View and then call a method in the View to populate the XAML controls.
Is that the correct way of doing this and, if so, how should it be done because I do not know how to expose the XAML controls to my ViewModel (and don't think I should be, right?).
I know I can expose the control creating a new instance of Mainpage but that is useless as I would need to bind to the current instance.
Mainpage Test = new MainPage();
Can someone please help me explain this - I have been through a lot reading and either not found the answer or not understood it!
Thanks, James
To begin, you definitely have the right idea.
What you want to do is create a ViewModel object (have it implement INotifyPropertyChanged) something like:
public class MainViewModel : INotifyPropertyChanged
{
//INPC implementation
public ObservableCollection<TermTest> MyTerms
{
//Standard INPC property stuff
}
}
Note that I used a property. You can only bind to properties, and you'll need the set method to raise the PropertyChanged event.
Then, you set the data context for the view. You can do this a number of ways, but the simplest is to do this:
public MainView() //Default Constructor
{
InitializeComponent();
DataContext = new MainViewModel();
}
Finally, bind!
<ListView ItemsSource="{Binding MyTerms}"/>
Also, if you don't want to touch the code behind of your window, you can do something like this:
<Window.Resources>
<YourNamespace:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainViewModel}">
<ListView x:Name="TermsListView" ItemsSource="{Binding MyTerms}">
</ListView>
</Grid>
If you want understand in details this pattern I recommend you read this article:WPF MVVM step by step (Basics to Advance Level)
I'm using the Bing map SDK in my WPF application and the XAML looks like:
<m:Map
x:Name="MyMap"
Grid.Row="1"
CredentialsProvider="KEY"
ZoomLevel="{BINDING MapZoomLevel}"
Mode="Road">
The code behind:
private int mapZoomLevel;
public int MapZoomLevel { get { return mapZoomLevel; } set { mapZoomLevel = value; NotifyOfPropertyChange(() => MapZoomLevel); } }
But this aint working. I guessing it is because I've already bound the Map by setting x:Name. The problem is that I can't remove the x:Name since I'm doing some setup in the view but is there a workaround? I would like to be able to bind the ZoomLevel of the map somehow
In order to data bind, you need to do a few things:
1) You must set the DataContext of the UserControl or Window to the object that contains the property that you want to bind to. That could be like this (in the UserControl or Window code behind) if that object is a separate view model class:
DataContext = new SomeTypeOfViewModel();
Or like this if the property is declared in the code behind:
DataContext = this;
2) You must implement the INotifyPropertyChanged interface or implement DependencyPropertys - you seem to have implemented the INotifyPropertyChanged interface, but you must ensure that you have done it correctly.
3) You must provide a valid Binding Path... BINDING is not valid, so an appropriate Binding Path for you might be this (depending on where you have declared your property):
<m:Map x:Name="MyMap" Grid.Row="1" CredentialsProvider="KEY"
ZoomLevel="{Binding MapZoomLevel}" Mode="Road">
Please read the Data Binding Overview page on MSDN for the full story.
based on your tags you are using Caliburn Micro with this? Datacontext is already set with viewmodel/view from the framework. ZoomLevel="{Binding MapZoomLevel, Mode=TwoWay}" is required.
I have a UserControl who's DataContext is being set to an instance of a ViewModel (using MVVM). But, I have controls within the UserControl which need to be bound to properties that only pertain to the view (which is why I placed them in code behind). I'm not sure how to bind this in xaml appropriately:
Note: SelectedOrderType is a property on the View-Model, and OrderTypes is a property on the UserControl itself.
<UserControl x:Class="MyNamespace.OrderControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="OrderUserControl">
<Grid>
...
<ComboBox ItemsSource="{Binding Path=OrderTypes, ElementName=OrderUserControl}"
SelectedValue="{Binding Path=SelectedOrderType}"
SelectedValuePath="OrderTypeCode"
DisplayMemberPath="OrderTypeName" />
</Grid>
</UserControl>
public partial class OrderControl : UserControl
{
public OrderControl()
{
InitializeComponent();
OrderTypes = ...;
}
public IReadOnlyCollection<OrderTypeInfo> OrderTypes { get; private set; }
}
Also, I know I can simply create a property on the View-Model, and I get that some people would suggest that that would be the correct place to put it... but I really would like to know how I could do what I'm attempting to do if not for this scenario, maybe for other scenarios in the future?
I may be wrong but would you not need to make a dependency property on your user control for "SelectedOrderType" and bind the the View Model to that property not bind directly to the view model from the user control.
That way your UserControl is not dependent on the view model?
Edit:
I think you could set it up the way you have it, but the binding for SelectedOrderType would need to be something like {Binding Path=DataContext.SelectedOrderType, ElementName=OrderUserControl}
I have a WPF Window which contains few UserControls, those controls contain another. And now, what is the most principal way how to create ViewModel for this Window and where to bind it.
I do expect that one firstly needs to create ViewModel for each of sub-controls.
There are a few ways to do this.
Inject the VM
I would recommend this method.
If your window is created in the App class like
var window = new MyWindow();
window.Show();
I would assign the VM before showing the window:
var window = new MyWindow();
window.DataContext = GetDataContextForWindow();
window.Show();
If one of your controls needs an own view model assign the VM wile creating the control instance.
DataBind
If you want to set the VM of a control you can bind the DataContext property to an VM instance provided by the surrounding VM.
<Controls:MyControl DataContext={Binding MyControlsVm} />
Code Behind
You may set the VM using the init method in code behind like
public MyWindow()
{
InitializeComponent();
DataContext = CreateViewModel;
}
You may use a trick if you don't want to create a VM for your main page:
public MyWindow()
{
InitializeComponent();
DataContext = this;
}
and just use the code behind class as VM.
I see the view as a visual representation of the ViewModel so I like WPF picking the view based on the instance of the ViewModel it wants to render.
I call this the View Locator pattern, I use this method to instantiate my view because I have found it to be very simple to implement.
It basically puts an entry in the ResourceDictionary of your app that tells WPF to use an IValueConverter to look up and instantiate the View when it comes across a ViewModel.
So a working example would be:
In your app.xaml:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" >
<Application.Resources>
<ResourceDictionary Source="Resources.xaml"/>
</Application.Resources>
</Application>
In resources.xaml:
<DataTemplate DataType="{x:Type vm:ViewModelBase}">
<ContentControl Content="{Binding Converter={StaticResource ViewModelConverter}}"/>
</DataTemplate>
Set the DataContext of your startup Window Control e.g.
public MainWindow : Window
{
InitializeComponent();
DataContext = new MainViewModel();
}
And you're pretty much done. So if you have a MainViewModel like so:
public class MainViewModel : ViewModelBase
{
public ChildViewModel1 Child1 {get;set;}
public ChildViewModel2 Child2 {get;set;}
}
and you have a UserControl that resolves to your MainViewModel like so:
<UserControl x:Class="MainView">
<StackPanel>
<ContentPresenter Content="{Binding Child1}"/>
<ContentPresenter Content="{Binding Child2}"/>
</StackPanel>
</UserControl>
So your ViewModelConverter will return an instance of the appropriate View without any extra effort on your part.
On the child controls issue, why wouldn't one of the properties of the root view model be an instance of the child view model that you would pass onto the child control? The other option would be a converter that converts the non-view model based property into an instance of the child view model (like an adapter pattern).
You might be interested in the sample applications of the WPF Application Framework (WAF). They show how composite Views and ViewModels can be instantiated and how they interact which each other.
I've built a WPF UserControl View/ViewModel pair: the view XAML handles the layout and bindings, and the ViewModel handles the logic, in-line with the recommended MVVM pattern.
I would like to be able to re-use this as a control.
How do I hide/encapsulate the ViewModel associated with the view, so that I can use the control as I would a standard control [such as a button] ?
i.e. How do I hide the control's viewmodel ?
depends on how you bind ViewModel class to the control.
if you do like this:
YourControl()
{
DataContex = new ViewModel();
}
then I don't see any problems. add reference to your control and use it.
You can create your ViewModel as a StaticResource within your XAML. The problem with setting the DataContext to your ViewModel is that you can't use that you can no longer use your DataContext from the window or page you in which you use the control.
In your XAML declare your ViewModel:
<myNS:MyViewModel x:Key="ViewModel />
Reference your view model within your XAML:
<TextBlock Text="{Binding Source={StaticResource ViewModel}, Path=TextToBind}" />
In your Code Behind you can access and initialize quickly, I usually make a property for easy reference to my view model.
private MyViewModel viewModel
{
get { return this.Resources["ViewModel"] as MyViewModel; }
}