WPF Datagrid ComboBox DataBinding - wpf

Can anyone tell me why this works;
<DataGridTemplateColumn Header="Supplier">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox DisplayMemberPath="SupplierName" SelectedValuePath="SupplierID"
SelectedValue="{Binding SupplierID}"
ItemsSource="{Binding Path=DataContext.Suppliers, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
but this doesn't;
<DataGridComboBoxColumn Header="Combo" DisplayMemberPath="SupplierName" SelectedValuePath="SupplierID"
SelectedValueBinding="{Binding SupplierID}"
ItemsSource="{Binding Path=DataContext.Suppliers, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
Second snippet does not show the list of SupplierName on edit...

It's because a DataGridComboBoxColumn isn't a user interface element, but ComboBox is.
In the first example, because your ComboBox is part of the visual tree, RelativeSource can do what it's supposed to do: walk up the UI tree looking for the item you've asked for. But in the second example, the DataGridComboBoxColumn is a DependencyObject but it's not an actual UI element - it's an object that describes something about the UI element.
You could try using ElementName instead, and give a name to your root window. Or, you might be able to get away with just:
<DataGridComboBoxColumn ...
ItemsSource="{Binding Path=Suppliers}" />
The DataContext will flow down from the window to the grid, so unless you've overidden it with something else at this point in the UI, it'll still be available.
Or if that doesn't work, you might want to add the relevant collection to a resource dictionary so you can get it with a Source={StaticResource suppliers} in the binding.

The reason is that the ItemsSource for the DataGridComboBoxColumn can not be found.
You will need to use the RelativeSource Binding and point it to the correct DataContext AncestorType. This will take some trial and error to find the DataContext that contains your list to satisfy your ItemsSource.

Related

Binding a list of String to a DataGridTemplateColumn's ComboBox

The question might seem quite easy but I can't get it and I tried everything I've found. It works for a list of class, since I can bind on one of my class' properties, but not for a simple ObservableCollection of String.
Here's my code in Xaml :
<DataGridTemplateColumn x:Name="VT" Header="VT" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding ListOfString}"
SelectedValuePath="ListOfString"
DisplayMemberPath="ListOfString"
ItemsSource="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type UserControl}},
Path=DataContext.ListOfString}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
My code behind works fine since when I open the dropdown it displays as many rows as there are elements inside, but rows are empty. I also tried to work with the CellEditingTemplate thing like here but it has the same result in the best case. I think I'm getting wrong on those DisplayMemberPath properties and others, but I don't see what I should get inside.
So how can I correctly bind my ObservableCollection to my ComboBox ?
Thanks !
if your ListOfString looks like this:
public ObservableCollection<string> ListOfString {get;set;}
then you should simply remove the SelectedValuePath and DisplayMemberPath. because otherwise wpf looks for the Property ListOfString which does not exists on strings :)
<ComboBox SelectedItem="{Binding ListOfString}"
ItemsSource="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type UserControl}},
Path=DataContext.ListOfString}"/>
and if this is not the case you should post the code for your ListOfString property.
btw you should rename the SelectedItem property or the collection property, because atm both named ListOfString.

How do I bind the visibility of a CheckBox in a DataGrid to the DataContext of that row?

I have a JibGrid DataGrid like so (JibGrid is an open-source relatively simple subclass of a standard DataGrid to implement stuff like filtering etc):
<dataGrid:JibGrid ItemsSource="{Binding AvailableRDs}"
FilteredItemsSource="{Binding AvailableRDs}"
SelectedItem="{Binding SelectedAvailRD}"
AutoGenerateColumns="False" >
<dataGrid:JibGrid.Columns>
<DataGridCheckBoxColumn Header="Add?" Binding="{Binding Add}" Visibility="{Binding GetAddVisibility}"/>
<DataGridTextColumn Header="Tag" Binding="{Binding Tag}" />
<DataGridTextColumn Header="Revision Tag" Binding="{Binding RevisionTag}" />
<DataGridTextColumn Header="Current System" Binding="{Binding SystemStr}" />
</dataGrid:JibGrid.Columns>
</dataGrid:JibGrid>
The intent is that there is a custom class for each row that provides properties for the content of each cell in that row - Tag, RevisionTag, etc. All of that works fine. What I can't get to work is that Visibility binding. I would like for the checkbox in each row to be invisible if the CanAdd property on the class representing that Row in the DataContext returns False. When I add the Visibility binding in XAML as above, it seems that what happens is that WPF attempts to apply this binding to the actual column instead of for each row, so the binding fails to connect. I can go in using WPF explorer and manually bind a row's checkbox visibility DependencyProperty to the CanAdd property of the Row's datacontext, and that works fine, but I can't figure out how to, in XAML or code, cause it to generate that binding for the checkbox in every row automatically. Anybody have any ideas on that?
I have searched around for questions like this, and it seems that, for some reason, everybody wants to change the visibility of the column itself based on something in the datacontext of the whole grid, and nobody else wants to change the visibility of things in individual rows based on that row's datacontext. I tried the solution here while I was trying to figure this out, and that's what that answer is trying to do.
You can use DataGridTemplateColumn and BooleanToVisibilityConverter to achieve the desired result
Add BooleanToVisibilityConverter to your resources
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
Set Converter for the DataGridTemplateColumn binding
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Add}"
Visibility="{Binding CanAdd, Converter={StaticResource BoolToVis}}" ></CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Bind to a Property in the Data Context from a ListviewItem

I have a data context in the form of MyViewModel.
MyViewModel has a property: public int MyWidth.
For each item in my listview, that is ListViewItem, I need to display a canvas with a width equal to MyWidth.
My listView has it's ItemSource bound to a property called MyCollectionOfInts.
Which, as you may have guessed, is of the following definition: ObservableCollection<int>.
The astute reader has likely realized, the data context of myListView is int and thus fails when trying to bind the non-existant MyWidth property from the int type data-context.
What kind of theoretical crazy binding is necessary to get this kind of nutty thing to work?
My most recent attempt was to bind using RelativeSource but couldn't exactly figure it out...
My list View:
<ListView Name="MyListView" ItemsSource="{Binding MyCollectionOfInts}"
My Items within the List view.
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumns Header=MyInts">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Name="m_TestLabel" Content="ASDF" />
<TextBox Text="{Binding Path=MyWidth, RelativeSource=????{RelativeSource AncestorType={x:Type MyViewModel}}}"/>
</DataTemplate>
</...a bunch of close brackets>
My list view Item is an int, but I want to get the Control's original data-context, ie. myViewModel, and bind the canvas width of my ListViewItem to the MyWidth property of myViewModel. How can I get the ListViewItem to recognize the control's data-context?
Note: I don't really want to make a container for the ListView and store a static MyWidth variable in it, but if that is the only way, then let me know. I'm hoping it's not.
Try this:
<TextBox Text="{Binding Path=DataContext.MyWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
If your View is usercontrol else use Window in type
<TextBox Text="{Binding Path=DataContext.MyWidth,
RelativeSource={RelativeSource AncestorType={x:Type ListView }}}"/>
Or
<TextBox Text="{Binding Path=DataContext.MyWidth,
Source={x:Reference MyListView}"/>

DataGrid binding in DataTemplate

I'm currently trying to do some binding inside of a datagrid but I'm having problems getting up to the level of DataContext of the view.
Here is the code:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Operators}"
ItemsSource="{Binding DataContext.OperatorList,ElementName=FilterGrid}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
Any ideas on whats wrong? The View's Viewmodel is connected in the code behind.
EDIT: The Binding that is not working is the ItemsSource binding shown above
When you use the DataTemplate of the DataGrid, you cannot use ElementName bindings as it won't resolve properly due to limitations in the resolution capabilities of FindControl within the DataGrid control hierarchy. You need to use a RelativeSource binding that travels up the control tree looking for a specific control type (which you need to determine - from your element name I assumed it was a DataGrid ancestor type).
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
SelectedItem="{Binding Operators}"
ItemsSource="{Binding DataContext.OperatorList,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
See this SO post that shares some potentially related sample code using MVVM to access the DataContext of the UserControl host to populate a ComboBox ItemsSource.

WPF Binding to ElementName inside ItemsControl

I have a checkbox, and an ItemsControl populating several DataGrids the following way:
<Checkbox Content="Birthday Column Visible" x:Name="UI_BirthdayVisibleCB" />
<ItemsControl ItemsSource="{Binding Path=ParentsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Children}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Birthday" Width="120" Visibility="{Binding IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" >
...
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Rest of closing tags>
This creates binding output errors as it tries to find IsChecked on the DataGridTemplateColumn. If I try to search for a Relative Ancestor I receive the exception:
Binding.RelativeSource cannot be set while using Binding.ElementName.
I have a ViewModel, and stick to MVVM mostly, but in this case I'd really like to keep the column visibilities on the View layer. Note that BoolToVis just converts Boolean to Visibility.
Edit
Here is an example of what I'm trying to do:
<DataGridTemplateColumn Header="Birthday" Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" />
It compiles but doesn't run however, it throws the exception above.
You are using RelativeSource, which can't be mixed with ElementName, but you once you have the correct RelativeSource, you can drill down deeper using path.
e.g.
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=UI_BirthdayVisibleCB.IsChecked, Converter={StaticResource BoolToVis}}"
presumably you have some xaml like this:
<UserControl class="MyView" ... >...<CheckBox Name="UI_BirthdayVisibileCB"/> ...
The above binding should find this UserControl by type based on RelativeSource, then it will try to find a property named UI_BirthdayVisibleCB, which it won't find because WPF XAML implements this named element as a field.
The easy work around is to go into your codebehind and expose a property for it.
public object BirthdayVisibileCB_4_binding {
get { return UI_BirthdayVisibileDB; }
}
and bind to it instead:
Visibility="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MyView} },
Path=BirthdayVisibileCB_4_binding.IsChecked, Converter={StaticResource BoolToVis}}"
Yes, it kind of a pain to do this, but MVVM only matches WPF so far... its not a great fit, its only the best fit we have around.
If you want to try RelativeSource, you have to remove ElementName from the declaration:
However, only one of the three
properties, ElementName, Source, and
RelativeSource, should be set for each
binding, or a conflict might occur.
This property throws an exception if
there is a binding source conflict.
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.elementname.aspx
Your usage of ElementName seems correct, so I'll continue to look at the problem if you prefer that over RelativeSource.

Resources