Setting RelativeSource correctly - wpf

I have a combo box on a xaml form (MainWindow).
I set the Items source to an ObservableCollection in the code behind. To populate the Combo box I used Relative Source (it sits inside an ItemsControl), which worked great (without it, if did not populate):
ItemsSource="{Binding SelectableItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
I have now factored out the ObservableCollection into a seperate View Model Class, named 'MainWindowViewModel', the combo box does not populate.
I have set the DataContext of the MainWindow to my ViewModel and have checked that it populates other controls as expected, which it does.
How should I construct the RelativeSource so the combo box populates?
Thanks
Joe

I needed to add the Path at the end, thus:
ItemsSource="{Binding SelectableItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.SelectableItems}"

You do not want to use a RelativeSource any longer. If you don't specify a RelativeSource (or Source, or ElementName), then the binding will resolve against the current DataContext. Since the DataContext is inherited, your ItemsControl obtains its DataContext from the parent Window. Thus, this binding will resolve against your view model.
ItemsSource="{Binding SelectableItems}"

Related

What's a better way to bind header checkbox to user control's datacontext?

I have a workable solution but I'm pretty convinced there's a better way of writing this.
I have a User Control with a Data Grid inside. The Data Grid's ItemsSource is set to {Binding Path=MyView} where MyView is an ICollectionView property of the View Model. The User Control's data context is set to the View Model.
In the data grid, I have a check box header. I want to bind the IsChecked state of the checkbox to a property in the View Model.
This is what I have so far and it seems to work, but I'm concerned this binding is unnecessarily complex. The UI is pretty basic so I would expect the binding to be more straightforward to write than it was.
Is there a better way to express such a binding?
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Views:MyUserControlClass}}, Path=DataContext.AllRowsSelected}" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
In such situations I use
ElementName=userControl
instead of
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Views:MyUserControlClass}}
Also you can use
{Binding Parent.DataContext.AllRowsSelected, ElementName=LayoutRoot}
In this case I assume that LayoutRoot is the name of the element who's parent is the user control. Parent is its property. So binding is set to parent's DataContext property.
I prefer the last variant, because providing name for user control limits its usage.
EDIT
About LayoutRoot. This name is often provided for the top element in a Window or a UserControl, or just some layout:
<Window ...>
<Grid Name="LayoutRoot">
...
</Grid>
</Window>
There's nothing special about this name. Just often used. Same situation as with namespace aliases in xaml: sys (points to mscorlib), local (points to your application namespace), etc.

Binding list of objects to WPF ListView

I have a list of objects which i want to bind to a ListView control in my WPF application.
The Objects have a DataTemplate already, so no need to define that.
The list of objects is a property in the codebehind file in the format list<object>
When i add one object programatically, it appears fine. But when i try to bind the ItemSource of the ListBox to the list of objects, nothing shows up.
I am using the following binding:
<ListBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Portfolios}"/>
where the name of the property i am trying to bind to is Portfolios and exists on the parent window
List<> objects don't automatically report when a new item is added. Try using an ObservableCollection<> instead, and see if that helps.

How to bind to a property of the ViewModel from within a GridView

I'm using the MVVM design pattern, and the DataContext of my View is set to a ViewModel.
In my View, I have a ListView/GridView with ItemsSource bound to a DataTable. One of the GridViewColumns has a CellTemplate that presents a Button. I want the IsEnabled property of the button to be bound to the SelectButtonsEnabled property of my ViewModel.
Using IsEnabled="{Binding Path=SelectButtonsEnabled}" doesn't work because my DataTable doesn't have a column named "SelectButtonsEnabled". The property I'm trying to bind to is global for the whole ViewModel, not specific to a row of the DataTable.
I think I need some kind of RelativeSource tag, but all my attempts thus far have failed.
Thanks.
Well, I don't know if this is the most succinct way to do it, but this works:
IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.SelectButtonsEnabled}"

How do I databind to the control's property rather than the datacontext?

I have a sub control embedded inside my main control, it allows the user to edit an address. Because this is reused all over the place (sometimes in multiple places on one control) I bind it like so
<Controls:EditAddressUserControl DataContext="{Binding Path=HomeAddress}"/>
<Controls:EditAddressUserControl DataContext="{Binding Path=WorkAddress}"/>
But the EditAddressUserControl needs access to the main control's list of CountrySummary objects so it can choose which country the address belongs to.
I have added a Countries DependencyProperty to EditAddressUserControl and added
Countries="{Binding Countries}"
So far all is going well, the EditAddressUserControl.Countries property has the correct countries in it. However, how do I databind my Combobox.ItemsSource to that in XAML?
I still want everything on my EditAddressUserControl to bind to its DataContext, but the ComboBoxCountries.ItemsSource needs to bind to "this.Countries".
How do I do that?
I've tried this
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:EditAddressUserControl}}, Path=Countries}" />
I saw no binding errors in the output box, but I also saw no items in the combobox.
You can accomplish this by using a RelativeSource for the binding source, instead of the DataContext.
This would most likely look something like:
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:EditAddressUserControl}}, Path=Countries}" />
The way to do it was to stop using DataContext completely. Instead I added a DependencyProperty to my control
public static DependencyProperty AddressProperty = DependencyProperty.Register("Address", typeof(EditPostalAddressDto), typeof(EditPostalAddressControl));
Then in the parent control instead of setting DataContext="..." I set Address="..." - The XAML for the control is then changed to include an ElementName on the binding
<UserControl ..... x:Name="MainControl">
<TextBox Text="{Binding ElementName=MainControl,Path=Address.Region}"/>
Now I can specifically bind to the Address property, but also bind to properties on the main data context.

WPF : Binding Order

I have a usercontrol (ItemsView) that I use in one of my other view. Since I need to access its ViewModel, the ItemsViewViewModel is contained by the ViewModel of the view that contained the control. I use this control at many times and I find it useful to bind a collection on the ItemsSource of my ItemsView control (differently, depending on the view that used it). It works, but not all the time.
Here some code :
<local:ItemsView DataContext="{Binding Path=ItemsViewModel}" ItemsSource="{Binding Path=DataContext.CurrentItem.Children, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
The problem is that the DataContext of the ItemsView is sometimes set before the ItemsSource, and sometimes after. This is problematic because the ItemsSource is a dependency property linked to the ItemsViewViewModel.
Is there a way to have the DataContext set before the ItemsSource everytime?
After InitializeComponent, set SelectedIndex to -1 - this worked for me.

Resources