DataGridComboBoxColumn binding - c#

I have seen a few posts on here with people getting confused on how to bind to a DataGridComboBoxColumn,
I have
<DataGridComboBoxColumn SelectedItemBinding="{Binding Collection}" DisplayMemberPath="Name" Header="Name" Width="70">
which didnt work..
So I used
<DataGridComboBoxColumn ItemBinding="{Binding Collection}" DisplayMemberPath="Name"> Header="Name" Width="70">
Which again didn't work, why is binding to a datagridcombo different to a original combo box.
<ComboBox ItemsSource="{Binding Collection}" DisplayMemberPath="Name" HorizontalAlignment="Left">
which does work
What is the correct method of binding to a combo box inside a DataGrid?
---Edit---
I might have found the problem, I have a DataGrid binding to a ItemSource, however, I want the ComboBoxColumn to be bounded to a different Itemsource, is this possible?
Cheers

You need to bind to ItemsSource property. Set it in EditingElementStyle.
<DataGridComboBoxColumn>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Collection}"/>
<Setter Property="DisplayMemberPath" Value="Name"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
In case you want ItemsSource to bind to collection which is outside of DataGrid underlying source object, you can do that as well.
Say you have collection AnotherCollection residing in ViewModel of Window/UserControl, you can bind with it using RelativeSource markup extension.
Also, you have to set SelectedItemBinding to the property where you want to set the value selected from ComboBox and declare same style under ElementStyle of DataGridComboBoxColumn.
Suppose property name is Name to which you want to bind.
<DataGridComboBoxColumn SelectedItemBinding="{Binding Name}">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource"
Value="{Binding DataContext.AnotherCollection,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}}"/>
<Setter Property="DisplayMemberPath" Value="Name"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource"
Value="{Binding DataContext.AnotherCollection,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}}"/>
<Setter Property="DisplayMemberPath" Value="Name"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>

Related

Why is the ComboBoxColumn very slow on drop-down open in DataGrid WPF?

I am using C# WPF and currently, I am loading some data from the database in Datagrid.
I loading more than 24,000 rows from a table in the database into DataGridComboBoxColumn, the problem is that when I open the Combobox it is very slow so it takes about 30 seconds to display the records
I solved that problem in DataGridTemplateColumn here is the XAML :
<DataGridTemplateColumn Header="ComboBox Element" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="ComboBox_Commodity"
ItemsSource="{Binding Path=TheCommodityCombo_DATA, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedValue="{Binding CommodityID}"
DisplayMemberPath="CommodityName"
SelectedValuePath="CommodityCode"
IsTextSearchEnabled="True"
IsEditable="True"
SelectedIndex="0" BorderBrush="#FFADEEB4" Background="{x:Null}" BorderThickness="1" PreviewLostKeyboardFocus="ComboBox_Commodity_PreviewLostKeyboardFocus">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling"/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
but I don't want use DataGridTemplateColumn because in the combobox element doen't firing the CellEndEdit event
so I'm using DataGridComboBoxColumn
XAML:
<DataGridComboBoxColumn Width="160" Header="DataGridComboBoxColumn"
SelectedValueBinding="{Binding CommodityID}"
DisplayMemberPath="CommodityName"
SelectedValuePath="CommodityCode">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=TheCommodityCombo_DATA, RelativeSource={RelativeSource AncestorType=Window}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=TheCommodityCombo_DATA, RelativeSource={RelativeSource AncestorType=Window}}" />
<Setter Property="SelectedIndex" Value="0"/>
<Setter Property="IsEditable" Value="True"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
My issue is : the settings that I made exactly like the ComboBox in DataGridTemplateColumn do not work for the DataGridComboBoxColumn! and DataGridComboBoxColumn on opening combobox is so much slow
How do I fix this ↑ ?
similar problem but in Templatecolumn : Combox column in wpf datagrid performance issue
I am unable to test the solution below and it does not fit in a comment but it seems like you are trying to style VirtualizingStackPanel without first creating one for a ComboBox.
First define your ItemsPanelTemplate:
<Window.Resources>
<ItemsPanelTemplate x:Key="VSP">
<VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling"/>
</ItemsPanelTemplate>
</Window.Resources>
Then style your DataGridComboBoxColumn's ComboBox to use this ItemsPanelTemplate.
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsPanel" Value="{StaticResource VSP}" />
This should replace ItemsPanel with your configured VirtualizingItemsPanel.
Source: http://vbcity.com/blogs/xtab/archive/2009/12/15/wpf-using-a-virtualizingstackpanel-to-improve-combobox-performance.aspx
ComboBox element can fire the CellEndEdit event
ComboBox should be contained in CellEditingTemplate instead of CellTemplate
<DataGrid ItemsSource="{Binding ItemList}" x:Name="dataGrid" CellEditEnding="dataGrid_CellEditEnding">
<DataGrid.Columns>
<DataGridTemplateColumn Header="ComboBox Element" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ItemList}"
IsEditable="True">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ComboBox is created(Initialized) when entering edit mode..
It will help improve performance

Binding to DataGridComboBox

I know this has been asked a few times, but I cannot get the binding to work on my DataGridComboBox, it never displays at all. Can someone show me the error of my ways?
c#
IList<ServiceCodes> servicecodes = App.GetInfo.GetServiceCodes();
newinvoice.INVItemsDataGrid.DataContext = servicecodes;
newinvoice.ShowDialog();
XAML
<DataGrid x:Name="INVItemsDataGrid" DataContext="{Binding}">
<DataGrid.Columns>
<DataGridComboBoxColumn x:Name="INVSCDropDown" DisplayMemberPath="CodeName" SelectedValuePath="CodeName" SelectedValueBinding="{Binding CodeName}" />
</DataGrid.Columns>
</DataGrid>
Thanks for your help as always.
The first thing you need to do is to set the ItemsSource property of the DataGrid to an IEnumerable.
Once you have done this, you could bind the ComboBox to another or the same IEnumerable like this:
<DataGrid x:Name="INVItemsDataGrid" ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridComboBoxColumn x:Name="INVSCDropDown" DisplayMemberPath="CodeName" SelectedValuePath="CodeName" SelectedValueBinding="{Binding CodeName}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=., RelativeSource={RelativeSource AncestorType=DataGrid}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=., RelativeSource={RelativeSource AncestorType=DataGrid}}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
...although it doesn't make much sense to bind the ComboBox and the DataGrid to the same source collection. But you should at least get the idea.

DataGridComboBoxColumn not updating model WPF

I'm using Datagrid in WPF and DataGridComboBoxColumn. Please find the code below:
<DataGrid>...
<DataGridComboBoxColumn Header="Category" Width="200"
SelectedValueBinding="{Binding SelectedCategory, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="CategoryName"
SelectedValuePath="CategoryID">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding CategoriesList}"></Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding CategoriesList}"></Setter>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
The model is as follows:
public CategoryModel SelectedCategory { get; set; }
public ObservableCollection<CategoryModel> CategoriesList
{
get;
set;
}
Now when ever I change the selection in combobox it shows a red border, unable to commit the changes to the source.
This is wrong:
<DataGridComboBoxColumn Header="Category" Width="200"
SelectedValueBinding="{Binding SelectedCategory, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="CategoryName">
remove the SelectedValuePath or you get a type missmatch. I doubt you need the UpdateSourceTrigger either... Try to ommit.

How can I bind to the count of the items source in XAML using a style and a DataTrigger?

I can bind to the Item source if I call the list by name but, I have not been able to get the binding to work generically by getting the Count of the Items on each individual combobox.
Here is what I would like to do in XAML. What do I need to change for this binding to work?
<Grid.Resources>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Items.Count}" Value="0">
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ComboBox
ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedElement}"
ItemTemplate="{StaticResource MyTemplate}">
</ComboBox>
Include the RelativeSource component in your binding:
<DataTrigger Binding="{Binding Path=Items.Count, RelativeSource={RelativeSource Self}}"
Value="0"
>
The way you currently have it the binding subsystem will look for the Items.Count property on whatever you have set as the DataContext of the ComboBox.

WPF + MVVM: Override autocomplete in DataGridComboBoxColumn

I've been searching for quite a while, but cannot figure out how to override the autocomplete functionality in a DataGridComboBoxColumn.
What I want to do is what is explained here, except for a combobox: Searching for items in a list box
That is: I want to be able to enter any string and then apply a filter to the ComboBox items in the DataGridComboBoxColumn to show only those items that match this as a substring.
I'm new to WPF and have been searching online for a while. I've found things like EventSetters and CommandBehaviorCollection.Behaviors, but I can't get a clear picture of the possibilities (and impossibilities).
I've got:
<DataGrid ... >
...
<DataGrid.Columns>
...
<MyCustomDataGridComboBoxColumn Header="My Header" MinWidth="200" >
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding DataContext.MyData, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<Setter Property="SelectedItem" Value="{Binding DataItem, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True}" />
<Setter Property="DisplayMemberPath" Value="HardwareId" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding DataContext.MyFilteredData, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<Setter Property="SelectedItem" Value="{Binding DataItem, UpdateSourceTrigger=LostFocus}" />
<Setter Property="DisplayMemberPath" Value="HardwareId" />
<Setter Property="IsEditable" Value="True"/>
<Setter Property="Text" Value="{Binding DataContext.MyNewDataItem, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</MyCustomDataGridComboBoxColumn>
...
</DataGrid.Columns>
</DataGrid>
Ideally, I'd like to create a new class that inherits from DataGridComboBoxColumn and supply it with some custom logic, such as supplying an anonymous function in its constructor so that the autocomplete behavior can be overridden in different ways in the future.
Is this even possible, or am I going about this entirely the wrong way?
I'm not saying your approach is wrong, however, I would approach it differently. For me it seems easier to use a DataGridTemplateColumn and supply a ComboBox that has the functionality you speak of.
<DataGridTemplateColumn Header="ColumnName" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<YourCustomComboBox/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Edit:
A while ago I needed a ComboBox with the same functionality. I ended up combining a TextBox with a Popup control because it gave me much more control over it.
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" x:Name="editBox"/>
<Popup x:Name="textboxPopup" Width="{Binding ElementName=editBox, Path=ActualWidth, Mode=OneWay}"
PlacementTarget="{Binding ElementName=editBox}"
StaysOpen="False"
IsOpen="{Binding Path=IsOpen, Mode=OneWay}">
<Grid>
<DockPanel MaxHeight="500">
<ListView SelectionMode="Single"
ItemsSource="{Binding Path=Suggestions}"
Name="popupList">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"Color="LightBlue"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="LightBlue"/>
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</DockPanel>
</Grid>
In the codebehind I subscribed to the TextChanged event and a few other events that came in handy. I can't share all my code because it is production code. However there are a few other people on the internet with similar implementations: using a ComboBox, using a textbox and of course the link you posted in your question. There is more than enough out there.
And about using your custom control as a TargetType... I don't see anything wrong with that, I do it all the time.
The error with the CellTemplate shouldn't occur. Are you using it correctly? See this link for an example.

Categories