Using ViewModel as WPF DataContext Disposing problems - wpf

Just starting out using WPF / MVVM and have a question regarding using a viewmodel as the datacontext for a wpf window.
I'm using a view first approach and my view looks like this;
<Window x:Class="TestContext.TestForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestForm" Height="300" Width="300"
DataContext="{StaticResource testViewModel}">
<Grid>
<TextBox Text="{Binding Path=Address}" Height="23" HorizontalAlignment="Left" Margin="34,44,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>
and I create my viewmodel in code like this ;
public class ViewModelFactory
{
public TestViewModel CreateTestViewModel()
{
return new TestViewModel();
}
}
I'm using the following approach in the app.xaml to create an instance of the viewmodel whenever I instantiate the view ;
<ObjectDataProvider x:Key="testViewModel" ObjectInstance="{StaticResource viewModelFactory}" MethodName="CreateTestViewModel">
This all works fine.
The problem is that I only ever see the viewmodel being created once. So new instances of the view use the same instance of the viewmodel.
I'd like to start out with a new instance of the viewmodel.
If I create the view model manually in the forms constructer ;
public TestForm()
{
InitializeComponent();
this.DataContext = new TestViewModel();
}
then it works as expected.
I'm trying to get my head around what's happening here...
Thanks....

Honestly using ObjectDataProvider isnt a good idea to get view models as that instance is single per App.
But if you insist to recreate the viewmodels you will have to refresh the testViewModel.
The way you do that is testViewModel.Refresh();

In Code
Create your view
Create Single Property on View (called VM)
Create your viewmodel
Assign the new viewmodel to VM
Set DataContext to ViewModel
Setup your bindings in XAML to point to the correct properties on your view model
As a rule
I never use ObjectDataProviders or instantiate anything in XAML (too buggy)
Always use the MVVM pattern
Be careful of static resources - they are only set once per app instance where dynamic resources can be updated

Yes, Agree with #AngelWPF You should use set DataContext as
View.DataContext = ViewModel;

You could instantiate you view model in the resources section of your view. Once you create new view - you'll get new view model.
Moreover, you'll get static bindings which are a bit performant and convenient to use because you'll get intellsense.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<WpfApplication1:MyViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource ViewModel}">
<ItemsControl ItemsSource="{Binding Data}"/>
</Grid>
</Window>

Related

WPF Binding dynamic control in code behind to ViewModel

I'm building a custom UserControl in WPF, which has a ViewModel associated. I also want do dynamically make controls in the code behind. But now I'm having problems binding the generated controls with the ViewModel properties. My code is:
<UserControl x:Class="SVT.Teste.UserControl1"
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="UserControl1ViewModel">
<Grid Name="GridContainer">
</Grid>
</UserControl>
and code behind:
public UserControl1()
{
InitializeComponent();
System.Windows.Controls.Button newBtn = new Button();
newBtn.SetBinding(Button.ContentProperty, new Binding("Test"));
GridContainer.Children.Add(newBtn);
}
public class UserControl1ViewModel
{
private string test = "ola";
public string Test
{
get { return test; }
}
}
When I run this I get:
"System.Windows.Data Error: 40 : BindingExpression path error: 'Test'
property not found on 'object' ''String' (HashCode=-946585093)'.
BindingExpression:Path=Test; DataItem='String' (HashCode=-946585093);
target element is 'Button' (Name=''); target property is 'Content'
(type 'Object')"
Can you help me?
You are setting DataContext property of UserControl1 to a string instead of your view model instance.
You need to do something like this:
<UserControl xmlns:local="clr-namespace:NAMESPACE_WHERE_VIEWMODEL_IS_DEFINED">
<UserControl.DataContext>
<local:UserControl1ViewModel />
</UserControl.DataContext>
<!-- unrelated code omitted -->
</UserControl>
You are setting you DataContext to the type, not an instance that has the properties.
In your method that creates the user control do :
public UserControl1()
{
InitializeComponent();
System.Windows.Controls.Button newBtn = new Button();
newBtn.SetBinding(Button.ContentProperty, new Binding("Test"));
GridContainer.Children.Add(newBtn);
**DataContext = new UserControl1ViewModel();**
}
You still have more work to do. The way you have it no notifications or update will happen. Implement the INotifyPropertyChanged interface (on UserControlViewModel). And remove setting DataContext in the XAML to the type.
try with this binding
Binding MyBinding = new Binding();
MyBinding.Path = new PropertyPath("Test");
newBtn.DataContext = new UserControl1ViewModel(); //or MyBinding.Source = new //UserControl1ViewModel();
newBtn.SetBinding(Button.ContentProperty, MyBinding);
max is right, but i have another question. why do you want create your usercontrol dynamic when you have a viemwodel you wanna bind to? makes no sense to me. let me explain:
if you have a viewmodel - you know in mind how this viewmodel should be rendered and what the bindings are. so you could create a usercontrol/view for this viewmodel
MyUserControl1View.xaml
<UserControl>
<Grid>
<Button Content="{Binding Test}"/>
<!-- more controls and binding if the viewmodel expose more-->
</Grid>
</UserControl>
so what you have now is a representation of your viewmodel. they are not connnected but your viewmodel should look like this and the binding are set. till now no datacontext is set at all.
all you have to do now is to go the viewmodel first approach and the use of datatemplates.
let us assume the following in your mainwindow
<Window>
<Window.Resources>
<DataTemplate Datatype="{x:Type local:Usercontrol1viewmodel}">
<view:MyUserControl1View/>
</DataTemplate>
</Window.Resources>
now wpf knows how to render Usercontrol1viewmodel.
one step more in your mainwindow viewmodel you handle your Usercontrol1viewmodel.
public Usercontrol1viewmodel MyWhatEver {get;set;}
if you bind this property to a contentpresenter, you will see the wpf magic;)
<ContentPresenter Content="{Binding MyWhatEver}"/>
now you see the MyUserControl1View in the contentpresenter, no dynamic view code needed. just go with your viewmodels.
ps: feel free to edit my bad english.

"The attachable property not found in type " error while using a dependency property in Silverlight

I am trying to do some sample applications to use Dependency Property in a DataGrid,but when i tried to run application I am getting an run time exception
The attachable property 'SelectedColumnIndex' was not found in type
'CustomDependencyProperty'. [Line: 17 Position: 74]
This is the code i used to declare my dependency property
public class CustomDependencyProperty : DataGrid
{
public static DependencyProperty SelectedColumnIndexProperty = DependencyProperty.Register("SelectedColumnIndex",
typeof(object),
typeof(DataGrid),
new PropertyMetadata(0));
public int SelectedColumnIndex
{
get
{
return (int)GetValue(SelectedColumnIndexProperty);
}
set
{
SetValue(SelectedColumnIndexProperty, value);
}
}
}
And this is my XAML code
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="BindingDictionary.MainPage"
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:BindingDictionary"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:SimpleConverter x:Key="myConverter"></local:SimpleConverter>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
ItemsSource="{Binding Responses}"
local:CustomDependencyProperty.SelectedColumnIndex="{Binding Index,Mode=TwoWay}">
</sdk:DataGrid>
<TextBlock x:Name="DisplayIndex" Text="{Binding Index}" />
</Grid>
</UserControl>
I am unable to figure out what excatly is the problem.Is there anything wrong in the way I declare a dependency property?
Please help.
Thanks,
Alex
I think you need an attached property here. Try changing
DependencyProperty.Register
to
DependencyProperty.RegisterAttached.
Also, typeof(object) should be typeof(int).
UPDATE
Yes, the above will fix your problem, but I think you don't really need an attached property here as your class is extending the DataGrid class. A normal dependency property is all you need. So keep your existing code and change
typeof(object),typeof(DataGrid),
to
typeof(int),typeof(CustomDependencyProperty),
and in your xaml, you can just use this extended class directly, something like this,
<local:CustomDependencyProperty SelectedColumnIndex="{Binding Index,Mode=TwoWay}">
You might want to change the name 'CustomDependencyProperty' to be something more meanful like ExtendedDataGrid.
So I think the conclusion is you normally have two ways of creating a bindable property, either by extending the control and creating a normal dependency property, or by creating a static class with an attached property.
Hope this helps. :)
I think I can answer this question now.This exception just explains what exactly is the diffrence between an AttachedProperty and a DependencyProperty.
To use a dependency property SelectedColumnIndex I should redefine my DataGrid xaml like this
<local:CustomDependencyProperty x:Name="customGrid"
AutoGenerateColumns="True"
ItemsSource="{Binding Responses}"
SelectedColumnIndex="{Binding Index, Mode=TwoWay}">
</local:CustomDependencyProperty>

MVVM way to use different controls for editing different objects

I need to design a form with a treeview in the left and a container for some other control in the remaining area. Whenever user selects an item in the treeview some custom control appears on the right. For example, let's say that the treeview contains values "Audio settings" and "Video settings", and I have two controls that can be bound to these settings and I want to display them on the form when needed.
Now, from what I've read about MVVM, I shouldn't have properties that will return UserControls or DataTemplates, am I right? It will be messing with "VM shouldn't know implementation details of the view" as I see it. So, how do I handle this situation properly in terms of MVVM? Should I maybe use converters for this and if so, how would it look?
I can't provide any code at the moment (mostly because there isn't any), but I will try to clarify the problem if needed.
Thanks in advance.
This is where the WPF templating system helps out.
The main idea is to have a ContentControl display the appropriate view depending on the selected value in the TreeView.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<ListBox DockPanel.Dock="Left" ItemsSource="{Binding Editors}" SelectedItem="{Binding SelectedEditor}" />
<ContentControl Content="{Binding SelectedEditor}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type l:VideoViewModel}">
<l:VideoView />
</DataTemplate>
<DataTemplate DataType="{x:Type l:AudioViewModel}">
<l:AudioView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</Window>
AudioView and VideoView are UserControls.
As you can see, the ContentControl's content is bound to the SelectedEditor property in the ViewModel, which is also bound to the SelectedItem of the Listbox.
So the ViewModel for the main view looks like this.
public class MainWindowViewModel : INotifyPropertyChanged
{
public IEnumerable<object> Editors
{
get
{
yield return new VideoViewModel();
yield return new AudioViewModel();
}
}
private object selectedEditor;
public object SelectedEditor
{
get { return selectedEditor; }
set
{
if (selectedEditor == value)
return;
selectedEditor = value;
OnPropertyChanged("SelectedEditor");
}
}
}
So you can see that the main ViewModel has no GUI data in it.
To handle hooking up a TreeView to a SelectedItem property in a ViewModel see Data binding to SelectedItem in a WPF Treeview
You can implement it throw two properties: ShowAudioSettings and ShowVideoSettings in ViewModel and bind it on Visibility of your controls.
Also, you can make it with VisualStateManager.

WPF How to expose fields on a UserControl

I have a simple application with just a window and a user control. The user control has a list box. The user control is positioned on the Window and I want to bind the user control's listbox to an element on the window's data context.
The examples I've been able to find have CLR properties on the user control which are accessed in code not via XAML.
<Window x:Class="WpfApplication2b.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication2b="clr-namespace:WpfApplication2b" Title="MainWindow" Height="410" Width="520">
<Grid>
<WpfApplication2b:MyUserControl></WpfApplication2b:MyUserControl>
</Grid>
And here is the user control itself.
<UserControl x:Class="WpfApplication2b.MyUserControl"
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">
<Grid Background="#FFD8AA13">
<ListBox Height="276" HorizontalAlignment="Left" Margin="12,12,0,0" Name="listBox1" VerticalAlignment="Top" Width="276" />
</Grid>
As you can see it's just a listbox on a different coloured background. I have no idea where to go next :)
I'm guessing that I need to add a code behind property for the list box as a dependency property?
Edit: I've added a dependencyProperty, but I don't think I've quite got the point.
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty ListBoxProperty;
static MyUserControl()
{
FrameworkPropertyMetadata md = new FrameworkPropertyMetadata();
MyUserControl.ListBoxProperty = DependencyProperty.Register("MyListBox", typeof (ListBox),
typeof (MyUserControl), md);
}
public ListBox MyListBox
{
get
{
return (ListBox) GetValue(ListBoxProperty);
}
set
{
SetValue(ListBoxProperty, value);
}
}
public MyUserControl()
{
InitializeComponent();
}
}
Your UserControl will inherit the DataContext from the Window so you can bind properties on the ListBox as though it were declared in the Window. To make the control more flexible you can declare Dependency Properties for the data items from the DataContext that you want to use (i.e. an ItemsSource collection) and pass them into the control, rather than passing the ListBox out.
I think this question/answer is almost what you're looking for. Essentially you're going to need to make a dependency property (using the AddOwner registration method) and set up the DataBinding on the ListBox's ItemsSource to hook to the Dependency Property. The example in the answer does the same thing for a ComboBox, and should be almost the same for a ListBox.
Exposing inner Control properties for binding in WPF

how to load wpf usercontrol in MVVM pattern

I'm creating a wpf user control which is in mvvm pattern.
So we have : view(with no code in codebehind file), viewmodel,model,dataaccess files.
I have MainWindow.xaml as a view file, which I need to bind with MainWindowModel.cs.
Usually, in a a wpf application we can do this with onStartUp event in App.xaml file. But in user control, as we do not have App.xaml...How do I achieve it ?
Please help :(...Thanks in Advance !!!
You can use a ContentControl, with a DataTemplate to bind the UserControl (View) to the ViewModel :
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<v:MyUserControl />
</DataTemplate>
...
<ContentControl Content="{Binding Current}" />
WPF will pick the DataTemplate automatically based on the type of the Content
I know this is an old, answered question, but I have a different approach. I like to make implicit relationships in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
With this, there is no need to set a DataContext anywhere.
UPDATE >>>
In response to #Vignesh Natraj's request, here is a fuller explanation:
Once you have set up the DataTemplate in a Resources element, you can display the KioskView in this example by adding an instance of the KioskViewModel anywhere in your XAML. This could be filling the MainWindow, or just inside a particular section of the screen. You could also host multiple instances of the KioskViewModel in a ListBox and it will generate multiple KioskView instances.
You can add an instance of the KioskViewModel to your XAML in a couple of ways, depending on your requirements. One way is to declare the XML namespace for the project that contains the KioskViewModel.cs file and simply add an instance of it in a ContentControl to the page where you want your view to appear. For example, if you had a UserControl called MainView and the KioskViewModel.cs file was in a Kiosk.ViewModels namespace, you could use basic XAML like this:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<UserControl.Resources>
<ViewModels:KioskViewModel x:Key="KioskViewModel" />
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</UserControl.Resources>
<ContentControl Content="{StaticResource KioskViewModel}" />
</UserControl>
I prefer to use the MVVM design pattern with WPF, so I would have a base view model class providing useful functionality such as implementing the essential INotifyPropertyChanged interface. I then have a property called ViewModel in the main (top level) view model of type BaseViewModel. This provides me with a nice way to change the ViewModel property to any view model that has derived from BaseViewModel and therefore to be able to change the associated view from the view model.
For example, in the MainViewModel.cs class that is bound to MainView there is a field and relating property:
private BaseViewModel viewModel = new KioskViewModel();
public BaseViewModel ViewModel
{
get { return viewModel; }
set { viewModel = value; NotifyPropertyChanged("ViewModel"); }
}
As you can see, it starts off as a KioskViewModel instance, but can be changed to any other view at any time in response to user interaction. For this setup, the XAML is very similar, but instead of declaring an instance of the view model in the Resources element, we bind to the property in the MainViewModel:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<ContentControl Content="{Binding ViewModel}" />
</UserControl>
Note that for this example, we would need to declare two (or more to make this approach useful) DataTemplates in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
I've been using MVVM Light Toolkit which has a ViewModelLocator class that you can put properties to the viewmodels in. You then create a reference to the ViewModelLocator in your Mainwindow.xaml like so:
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True"/>
In the grid panel, or whatever you're using, you can then set the datacontext like this:
<Grid DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}">
...
</Grid>
You could also go with MEFedMVVM which potentially adds a bit more flexibility in terms of being able to swap different viewModel implementations into the view.
The flexibility in both of these libraries is that you don't have to use their ViewModel base classes if you don't want to - the ViewModelLocator and the MEFedMVVM can work with any class.
There are endless ways to do it, wich all fall in one of the two categories:"view first" or "model first".
In a "view first" mode the view (e.g. your mainwindow) is created first and then (e.g. in the codebehind) the View instantiates the ViewModel and sets it as its datacontext):
private void WindowLoaded(object sender, EventArgs args)
{
this.DataContext = ViewModelService.GetViewModelX();
}
In a "model first" mode the ViewModel is there first and then instanciated the View.
// method of the viewmodel
public void LoadView()
{
// in this example the view abstracted using an interface
this.View = ViewService.GetViewX();
this.View.SetDataContext(this);
this.View.Show();
}
The examples given here are just one way of many. You could look at the various MVVM frameworks and see how they do it.
We can use ObjectDataProvider to call a method inside an object ..as follows :
<ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
MethodName="ConvertTemp"
x:Key="convertTemp">
Is there anyway to do the same using DataTemplate
You can probably look at MSDN. I find it as a good resource, though it doesn't explain how to use usercontrols,you will find your way out.

Resources