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.
Related
I have a UserControl positioned inside of the MainWindow. The UserControl runs a query and populates certain TextBlocks within it. I also want to populate TextBlock in the MainWindow from the same returned data.
How do I bind the MainWindow data to the UserControl? I have tried this:
<MainWindow DataContext="{Binding Path=DataContext, ElementName=UserControlName}">
Any help would be appreciated. Thanks!
Here is a simple working example.
The UserControl XAML contains just a two-way bound text box. The relative source stuff is more verbose than you need, you could have a data context set above that, but it's just to make it clear where the property is coming from:
<TextBox x:Name="ucTextBox"
Text="{Binding Path=UcText,
RelativeSource={RelativeSource AncestorType={x:Type local:UserControl1}},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
User Control code behind declares either a dependency property or, as shown here, a regular property implementing INotifyPropertyChanged:
private string _ucText;
public string UcText
{
get { return _ucText; }
set
{
_ucText = value;
OnPropertyChanged("UcText");
}
}
The MainWindow XAML then sets it's own text block to the text property from the textbox in the user control, like so:
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=UcText, ElementName=uc1}"/>
<local:UserControl1 x:Name="uc1" />
</StackPanel>
Nothing extra is required in the MainWindow codebehind.
What this results in is a text box (in the user control) which - as you type inside it - updates the text block on the main window.
So i have a UserControl for one of my Views and have another 'child' UserControl inside that.
The outer 'parent' UserControl has a Collection on its View-Model and a Grid control on it to display a list of Items.
I want to place another UserControl inside this UserControl to display a form representing the details of one Item.
The parent UserControl's View-Model already has a property on it to hold the currently selected Item and i would like to bind this to a DependancyProperty on the child UserControl. I would then like to bind that DependancyProperty to a property on the child UserControl's View-Model.
I can then set the DependancyProperty once in XAML with a binding expression and have the child UserControl do all its work in its View-Model like it should.
The code i have looks like this..
Parent UserControl:
<UserControl x:Class="ItemsListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Source={StaticResource ServiceLocator}, Path=ItemsListViewModel}">
<!-- Grid Control here... -->
<ItemDetailsView Item="{Binding Source={StaticResource ServiceLocator}, Path=ItemsListViewModel.SelectedItem}" />
</UserControl>
Child UserControl:
<UserControl x:Class="ItemDetailsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Source={StaticResource ServiceLocator}, Path=ItemDetailsViewModel}"
ItemDetailsView.Item="{Binding Source={StaticResource ServiceLocator}, Path=ItemDetailsViewModel.Item, Mode=TwoWay}">
<!-- Form controls here... -->
</UserControl>
EDIT: Heres how i created the Dependancy Proeprty on the child UC:
public partial class ItemDetailsView : UserControl
{
private static readonly DependencyProperty itemProperty;
static ItemDetailsView()
{
ItemDetailsView.itemProperty = DependencyProperty
.Register("Item", typeof(Item), typeof(ItemDetailsView), null);
}
public Item Item
{
get { return (Item)GetValue(ItemDetailsView.itemProperty); }
set { SetValue(ItemDetailsView.itemProperty, value); }
}
public static Item GetItem(DependencyObject target)
{
return (Item)target.GetValue(itemProperty);
}
public static void SetItem(DependencyObject target, Item value)
{
target.SetValue(itemProperty, value);
}
}
The selected Item is bound to the DependancyProperty fine. However from the DependancyProperty to the child View-Model does not.
It appears to be a situation where there are two concurrent bindings which need to work but with the same target for two sources.
Why won't the second (in the child UserControl) binding work?? Is there a way to acheive the behaviour I'm after??
Cheers.
Well, it looks like you are trying to use a "normal" DependencyProperty on the parent UserControl, and an "attached" DependencyProperty on the child UserControl. You need to pick one way. :)
EDIT for clarification:
There are two ways of registering a dependency property, "Normal", like so:
public static readonly DependencyProperty BobProperty =
DependencyProperty.Register("Bob",....)
and Attached:
public static readonly DependencyProperty BobAttachedProperty =
DependencyProperty.RegisterAttached("BobAttached",...)
Let's say the control you are registering these properties on is called "MyPanel". To use each property:
<MyPanel Bob="somevalue" MyPanel.BobAttached="somevalue"/>
Note the need to specify "where the attached property is defined". Attached properties are great when you have some bit of behavior or functionality that applies to multiple types of controls.
That said, perhaps there is a better way to do this - If the parent UserControl contained an ItemsControl, the ItemTemplate for that control could be a DataTemplate that contained the ItemDetailsView, in which case you could use standard data binding to do what you needed to:
<UserControl blahblahblah>
<ItemsControl ItemsSource="{Binding WhereYourItemsAre}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ns:WhatYourChildViewIsCalled DataContext="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
I've got a custom WPF control with a DependencyProperty MyString
I'm using the control within an ItemsControl on my View and want to fish a value out from the ViewModel.
As the DataContext of the control becomes each Item in the ItemsSource of the ItemsControl I thought I'd just be able to use FindAncestor but it dosnt seem to work ... can anyone see where I'm going wrong please?
Heres the XAML on the View ...
<Grid>
<ItemsControl ItemsSource="{Binding MyItems}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Name="myStack">
<ImportExceptions:ControlStrip MyString="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.MyStringOnViewModel}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
and heres the code behind my custom control where I've set up my dependency property ...
public partial class ControlStrip
{
public ControlStrip()
{
InitializeComponent();
}
public string MyString
{
get
{
return GetValue(MyStringProperty).ToString();
}
set
{
SetValue(MyStringProperty, value);
}
}
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.RegisterAttached("MyString", typeof (string), typeof (ControlStrip));
}
The DataContext of the control doesn't change - the DataContext for the ImportExceptions:ControlStrip will be (unless explicitly specified) the next DataContext available as its goes 'up' the visual tree...
I infer from your code that you have set the DataContext of the View to a ViewModel with properties 'MyItems' and 'MyStringOnViewModel' - you should be able to simply bind the MyString property directly to the ViewModel, like
<ImportExceptions:ControlStrip MyString="{Binding Path=MyStringOnViewModel}" />
Your code looks fine. Probably you have made an error in the DataContext reference. In all likeliness the DataContext of the the ItemsControl already is MyStringOnViewModel. So, omit the .MystringOnViewModel after the DataContext in the Path attribute. If not can you give some more code, ore post a simplification of it that mimicks how the DataCon,text(s) is/are set?
I have a XAML code that should load my UserControl inside the TabControl.
If I put this XAML code:
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Gui}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
</DataTemplate>
I have absolutly nothing appear in the windows (Gui property is inside the ViewModel class and return a UserControl).
But if I put his XAML code instead of the previous one:
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
</DataTemplate>
I have the ViewModel Object loading:
(source: clip2net.com)
Here is a piece of code of the TextBoxInputViewModel that has the property Gui that should be binded to be able to get the Visual (usercontrol):
private UserControl gui;
public UserControl Gui
{
get
{
if (this.gui == null)
{
this.gui = new SimpleTextBoxInputControl();//Xaml User Control
this.gui.DataContext = this;//Bind the Visual and ViewModel
}
return this.gui;
}
}
Any idea how that I can get the UserControl instead of this object reference text?
The problem is that ItemSource is a collection, where as you're binding it to a property that is just one value. The error in the Output window that you're seeing is likely related to this.
Instead of returning a UserControl directly from your View Model, it would be better to return another View Model that represents the contents of the tab, and use templates to display that content. If you need it to be more dynamic than choosing the template based on the Type of the View Model, look into setting TabControl.ContentTemplateSelector. This needs to be set to a class that derives from DataTemplateSelector. You can use this class to decide which template to load based on the object bound to that tab.
you should create a template for your viewmodel in your app.xaml file like this
<DataTemplate DataType="{x:Type simpleModel:TextBoxInputViewModel}">
<myView:TextBoxInputControl />
</DataTemplate>
where simpleModel is the namespace of TextBoxInputViewModel, and TextBoxInputControl is the user control you want to show and myView is the namespace of that user control.
How would I bind the IsChecked member of a CheckBox to a member variable in my form?
(I realize I can access it directly, but I am trying to learn about databinding and WPF)
Below is my failed attempt to get this working.
XAML:
<Window x:Class="MyProject.Form1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Title" Height="386" Width="563" WindowStyle="SingleBorderWindow">
<Grid>
<CheckBox Name="checkBoxShowPending"
TabIndex="2" Margin="0,12,30,0"
Checked="checkBoxShowPending_CheckedChanged"
Height="17" Width="92"
VerticalAlignment="Top" HorizontalAlignment="Right"
Content="Show Pending" IsChecked="{Binding ShowPending}">
</CheckBox>
</Grid>
</Window>
Code:
namespace MyProject
{
public partial class Form1 : Window
{
private ListViewColumnSorter lvwColumnSorter;
public bool? ShowPending
{
get { return this.showPending; }
set { this.showPending = value; }
}
private bool showPending = false;
private void checkBoxShowPending_CheckedChanged(object sender, EventArgs e)
{
//checking showPending.Value here. It's always false
}
}
}
<Window ... Name="MyWindow">
<Grid>
<CheckBox ... IsChecked="{Binding ElementName=MyWindow, Path=ShowPending}"/>
</Grid>
</Window>
Note i added a name to <Window>, and changed the binding in your CheckBox. You will need to implement ShowPending as a DependencyProperty as well if you want it to be able to update when changed.
Addendum to #Will's answer: this is what your DependencyProperty might look like (created using Dr. WPF's snippets):
#region ShowPending
/// <summary>
/// ShowPending Dependency Property
/// </summary>
public static readonly DependencyProperty ShowPendingProperty =
DependencyProperty.Register("ShowPending", typeof(bool), typeof(MainViewModel),
new FrameworkPropertyMetadata((bool)false));
/// <summary>
/// Gets or sets the ShowPending property. This dependency property
/// indicates ....
/// </summary>
public bool ShowPending
{
get { return (bool)GetValue(ShowPendingProperty); }
set { SetValue(ShowPendingProperty, value); }
}
#endregion
You must make your binding mode as TwoWay :
<Checkbox IsChecked="{Binding Path=ShowPending, Mode=TwoWay}"/>
If you have only one control that you want to bind to a property of your code-behind, then you can specify this as the source in your binding via a RelativeSource like this:
<CheckBox ...
IsChecked="{Binding ShowPending, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
That could be the end of the answer. But more generally you will have multiple controls and wish to bind them to various properties on your class. In this case it is neater and more convenient to make use of the fact that the DataContext property (which is the default source object for data binding) is inherited down through the control hierarchy, so setting it at the top level will make it available to all the child controls.
There is no default value for DataContext, but there are at least two ways you can set the DataContext property of your Window element to point at itself:
By setting DataContext = this in the code-behind constructor. This is very simple, but some might argue that it's not clear in the XAML where the DataContext is pointing.
By setting the DataContext in XAML using DataBinding
The simplest and, I think, most elegant way to set the DataContext at the Window/UserControl level in XAML is very straight forward; just add DataContext="{Binding RelativeSource={RelativeSource Self}}" to your Window element. RelativeSource Self just means "bind directly to the object", which in this case is the Window object. The lack of a Path property results in the default Path, which is the source object itself (i.e. the Window).
<Window x:Class="MyProject.Form1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<CheckBox ...
IsChecked="{Binding ShowPending}">
</CheckBox>
</Grid>
</Window>
Once you have done this, the DataContext property for all child controls will be the Window class, so data binding to properties in your code-behind will be natural.
If for some reason you don't want to set the DataContext on the Window, but wish to set it lower down the control hierarchy, then you can do so by using the FindAncestor mechanism. E.g. if you want to set it on the Grid element and all children of the Grid:
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
<CheckBox ...
IsChecked="{Binding ShowPending}">
</CheckBox>
</Grid>
It's probably worth noting at this point that what we have achieved so far is the ability to bind a UI Control to a property of your code-behind class, and for that code-behind property to be kept up-to-date with changes to the UI element. So if the user checks the CheckBox, the ShowPending property will be updated.
But quite often you also want the reverse to be true; a change to the source property should be reflected in a corresponding change to the UI Control. You can see this by adding another CheckBox control to your window, bound to the same ShowPending property. When you click one checkbox, you would probably hope or expect the other Checkbox to be synchronized, but it won't happen. To achieve this your code-behind class should either (a) implement INotifyPropertyChanged, (b) add a ShowPendingChanged event or (c) make ShowPending a Dependency Property. Of the 3, I suggest implementing INotifyPropertryChanged on your code-behind is the most common mechanism.