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.
Related
I'm totally lost with dependancy objects and binding. I often get things working without understanding why and how, this question is about knowing what should be happening.
I have a tiny user control with the following XAML
<Grid>
<Image Source="{Binding Icon}"></Image>
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
My code behind has the following
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(Image), typeof(MenuItem));
public Image Icon
{
get { return (Image)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(String), typeof(MenuItem));
public string Title
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
My MainWindow is empty, other than a reference to this control and to the ResourceDictionary. In the MainWindow code behind, I set the DataContext in the constructor.
<Window x:Class="AppUi.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:AppUi.Control"
Title="">
//set up to Resource Dictionary - all binding and styling works fine :)
<loc:MenuItem Icon="{Binding MailIcon}" Title="{Binding MailTitle}"></loc:MenuItem>
In the ModelView for the MainWindow, I have the following 2 properties
private Image_mailIcon;
public Image MailIcon{
//inotifyproperty implementation
}
private string _mailTitle;
public string MailTitle{
//inotifyproperty implementation
}
My question is, in the UserControl, how do I do the binding? Since it's a user control within a MainWindow, and the MainWindow already has a datacontext, I think the UserControl will inherit the DataContext from the parent (From what I have read).
So, in my UserControl XAML, should I be binding to the MainWindow's Code Behind properties OR to the ViewModel properties?
In other words, should my UserControl be
<Grid>
<Image Source="{Binding MailIcon}"></Image>
<TextBlock Text="{Binding MailTitle}"></TextBlock>
</Grid>
OR
<Grid>
<Image Source="{Binding Icon}"></Image>
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
Or, because I'm using a DataContext and the UserControl inherits, do I even need the Dependancy Properties at all?
You normally don't want to overwrite DataContext passed through visual tree so you can use either ElementName or RelativeSource binding inside UserControl to change binding context. The easiest way to achive this is give UserControl some name and use it ElementName binding
<UserControl ... x:Name="myUserControl">
<!-- ... -->
<Grid>
<Image Source="{Binding Icon, ElementName=myUserControl}"/>
<TextBlock Text="{Binding Title, ElementName=myUserControl}"/>
</Grid>
<!-- ... -->
</UserControl>
This way binding is DataContext independent. You can also create UserControl with assumption it will always work with only specific type of DataContext and then you just use Path from that view model type but then DataContext of that UserControl must always be of the view model it's designed for (mostly inherited through visual tree)
<UserControl ...>
<!-- ... -->
<Grid>
<Image Source="{Binding MailIcon}"/>
<TextBlock Text="{Binding MailTitle}"/>
</Grid>
<!-- ... -->
</UserControl>
I would also change type of Icon property from Image to ImageSource for example. You already have Image control inside your UserControl and you just want to bind its Source
in the UserControl, how do I do the binding? ... the UserControl will inherit the DataContext from the parent
That is correct, the UserControl will inherit the DataContext from the parent Window. Therefore you can data bind from the UserControl directly to the parent Window.DataContext. Please note that you would bind to whatever object has been set as the DataContext, regardless of whether that was the code behind or a separate view model class.
However, you don't have to data bind to the parent's DataContext object in this situation... you have other options. You could data bind to your own UserControl DependencyPropertys using a RelativeSource Binding like this:
<TextBlock Text="{Binding Title, RelativeSource={RelativeSource
AncestorType={x:Type YourPrefix:YourUserControl}}}" />
You could also name your UserControl and reference its properties like this:
<TextBlock Text="{Binding Title, ElementName=YourUserControlName}" />
While this example seems to be more concise, don't overlook the first example, as RelativeSource is a useful and powerful friend to have.
should I be binding to the MainWindow's Code Behind properties OR to the ViewModel properties?
That's your choice... what do you want or need to data bind to? you just need to know that a direct data binding will use the auto set DataContext value, so if you don't want to use that, then you can just specify a different data source for the Binding as shown above.
Finally, regarding the need to use DependencyPropertys... you only need to declare them if you are developing a UserControl that needs to provide data binding abilities.
I would like to bind a list box to an observable collection in code behind. This is what I am using for the binding:
Binding binding = new Binding();
binding.Source = symTable;
substanceList.SetBinding(ListBox.ItemsSourceProperty, binding);
symTable inherits from ObservableCollection, the Count property gets updated appropriatelly so I know I am adding elements correctly, but the list box isn't. I didn't know what to set the path to, since in XAML it is bound to the whole list.
Note: When adding individual items to the ListBox they get shown, so it is not a display issue. I also tried:
this.Resources.Add("symTable", symTable);
in the window constructor and then this:
but it says the resource cannot be resolved.
I also tried adding it as a resource in XAML but it didn't work again:
//in the window's resources.
<s:SymbolTable x:Key="symTable"/>
...
<ListBox x:Name="substanceList" ItemsSource="{Binding Source={StaticResource symTable}}"/>
and then in code behind:
symTable = (SymbolTable)this.FindResource("symTable");
Does anyone know any other way to do this in code behind or XAML, I think the ElementName refers to objects defined in code behind.
Here is part of the class definition for symTable:
public class SymbolTable : ObservableCollection<Substance>
{
Dictionary<string, Substance> symbolTable;
...
public Substance Insert(Substance s)
{
if (!symbolTable.ContainsKey(s.Name))
{
symbolTable.Add(s.Name, s);
Items.Add(s);
}
return symbolTable[s.Name];
}
Note alright so I just noticed the most weird thing, Items.Add wasn't raising the INotifyChanged event. I used Items.Add in my Insert method, I am guessing that Items.Add doesn't raise a INotifyChanged event so the ListBox wasn't getting updated, but when did Add instead of Items.Add then it worked. Do you know if this is indeed the case?
Ok, following the comments on your question, here is how to declare SymbolTable as resource in XAML and how to bind a ListBox to it. Note the XAML namespace declaration 'local', which refers to the local namespace/assembly, which is named ListBindingTest in my test project.
<Window x:Class="ListBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ListBindingTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:SymbolTable x:Key="symTable"/>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource symTable}}"/>
</Grid>
</Window>
You may access the SymbolTable resource in code behind in your window class like this:
SymbolTable st = (SymbolTable)Resources["symTable"];
st.Add(new Substance());
The DisplayMemberPath needs to be a public property of symTable.
You could always just set the ItemSource directly on the listbox.
substanceList.ItemsSource = symTable;
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>
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
I have one query related to designing WPF using MVVM
Here is the scenario :
1> I have one WPF screen which contains various user controls which are reusable in some other screens too.
2> Can i have separate ViewModel class for each of those user controls , what could be ideal design in this scenario
3> Should i separate my Viewmodel based on individual screen or on UserControls .
4> If i create separate viewmodels based on UserControls how i should integrate it .
Is there any design guidelines around this !!
Urgent Help appreciated ..
This post describes what I do in certain scenario, I don't know if it is a best practice or not but it works for me.
I create ViewModel for my Window that holds all the user controls, so this called ContainerViewModel and I create an instance of that Viewmodel and put it in the DataContext of the Window. From that moment all the UserControls can access that ViewModel with Binding.
The next thing to do is to create a property on my ContainerViewModel for everty UserControl that holds the ViewModel for each UserControl.
Then use binding to attach the usercontrols ViewModel to the DataContext property of the Usercontrol.
example of the viewmodels and a window with 2 listboxes instead of usercontrols:
Viewmodel classes without any implementation but just empty classes to show the concept:
public class ContainerViewModel
{
public ContainerViewModel()
{
ViewModelForControl1 = new Control1ViewModel();
ViewModelForControl2 = new Control2ViewModel();
}
public Control1ViewModel ViewModelForControl1 { get; set; }
public Control2ViewModel ViewModelForControl2 { get; set; }
}
public class Control1ViewModel { }
public class Control2ViewModel { }
Window xaml:
<Window x:Class="ConfigHellp.UI.Windows.ContainerWindow"
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:vm="clr-namespace:ConfigHellp.UI.ViewModel"
mc:Ignorable="d"
DataContext="{DynamicResource ContainerViewModel}" >
<Window.Resources>
<vm:ContainerViewModel x:Key="ContainerViewModel" d:IsDataSource="True" />
</Window.Resources>
<StackPanel>
<ListBox DataContext="{Binding ViewModelForControl1}" />
<ListBox DataContext="{Binding ViewModelForControl2}" />
</StackPanel>
</Window>
this depends on how complex the embedding of the UserControl into the environment is. If you think that its to much effort to build the view model logic for your user control again and again (which is also a very nice source for mistakes), you should infact encapsulate the logic in a single viewmodel for your control. If the user control will be an ListItem for example, i generally suggest you to build an own viewmodel for the control.
The infrastructure will be than:
A general viewmodel for your WPF screen, which holds instances of the viewmodels for your usercontrols. That DataContext of the screen will be the general viewmodel. The users controls's DataContext will be a Binding to the PropertyPath of the user control viewmodel in your general viewmodel. e.g:
In WPF Screen:
<ListBox DataContext="{Binding}" ItemsSource="{Binding Path=ItemList}">
<ListBox.ItemTemplate>
<yourControls:YourUserControl />
</ListBox.ItemTemplate>
</ListBox>
In the general viewmodel:
public class ScreenViewModel : INotifyPropertyChanged
{
private ObservableCollection<YourUserControlViewModel> _itemList =
new ObservableCollection<YourUserControlViewModel>();
public ObservableCollection<YourUserControlViewModel> ItemList
{
get { return _itemList; }
set { _itemList = value; }
}
}
This will automatically generate a your user control for each viewmodel in the ItemList of your general view model.