I have a DataGrid
with DataGridTemplateColumn
and ComboBox
in it.
<DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
public ObservableCollection<TestClass> TestItemCollection { get; set; } = new ObservableCollection<TestClass>
{
new TestClass(),
new TestClass(),
new TestClass(),
};
public class TestClass
{
public ObservableCollection<string> TestChildCollection { get; set; } = new ObservableCollection<string>
{
"First test item", "Second test item" , "Third test item" , "Fourth test item"
};
}
When I click on the ComboBox
in the blank row it apparently doesn't create a new instance of my collection and only gives a blank list.
I have to doubleclick on empty row space.
And only then I would get data in the ComboBox
.
How can I get data in the Combobox
with a single click on blank row??
If you need to get data in the ComboBox
with a single click on blank row, I suggest you to use Caliburn.Micro to "attach" a command to the DropDownOpened
event of your ComboBox
.
Here a sample: first of all the XAML
<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
Title="MainView" Height="600" Width="600"
Name="_window">
<DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}"
cal:Message.Attach="[Event DropDownOpened] = [Action Choose($dataContext)]"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Then the ViewModel:
public class MainViewModel : Caliburn.Micro.PropertyChangedBase
{
public MainViewModel()
{
TestItemCollection = new ObservableCollection<TestClass>
{
new TestClass(),
new TestClass(),
new TestClass(),
};
}
public void Choose(object data)
{
if (!(data is TestClass))
{
TestItemCollection.Add(new TestClass());
}
}
public ObservableCollection<TestClass> TestItemCollection { get; set; }
}
Please consider that in my sample the TestClass
code is the same that you wrote. Of course you must configure you application in order to work with Caliburn.Micro (if you do not know how to do it, you can read the documentation).
If you do not want (or maybe you cannot) use Caliburn.Micro, you can obtain the same result by using the System.Windows.Interactivity library (see my edit below).
Try the code: just a click and a new row is automatically created. I hope it can help you.
EDIT: alternative solution with System.Windows.Interactivity
If you cannot use Caliburn.Micro, you just need to modify the MainView XAML in this way:
<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="MainView" Height="600" Width="600"
Name="_window">
<DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DropDownOpened">
<ei:CallMethodAction MethodName="ChooseWithInteraction"
TargetObject="{Binding ElementName=_window, Path=DataContext}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
As you can see, I just added the references to Microsoft.Expression.Interactions and System.Windows.Interactivity libraries. Then I added an EventTrigger and a CallMethodAction to the ComboBox
.
Now in the MainViewModel
you can replace the Choose
method with the ChooseWithInteraction
one (of course you can also simply add it to the code):
public void ChooseWithInteraction(object sender, EventArgs args)
{
object data = ((ComboBox)sender).DataContext;
if (!(data is TestClass))
{
TestItemCollection.Add(new TestClass());
}
}
In this way you can obtain the same behaviour of my first solution, but without using Caliburn.
I've come to a solution which you can achieve your desired functionality by following two steps.
Step One: You should specify a CellEditingTemplate
for the DataGridTemplateColumn
and set IsHitTestVisible
to false
for the DataGridTemplateColumn.CellTemplate
. This will force UI to enter the edit-mode when a DataGridCell
(e.g. your ComboBox
) is being clicked and as a result a new instance of your collection will be created. To do so, you should first define a property in your TestClass
to keep the selected value of each ComboBox
:
public class TestClass
{
public ObservableCollection<string> TestChildCollection { get; set; }
// keeps the selected value of each ComboBox
public string SelectedTestItem { get; set; }
public TestClass()
{
TestChildCollection = new ObservableCollection<string> {"First test item", "Second test item" , "Third test item" , "Fourth test item" };
}
}
Then the xaml
for your DataGrid
should change like this:
<DataGrid Grid.Row="0"
SelectionUnit="Cell"
DataGridCell.Selected="DataGridCell_GotFocus"
GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150" IsHitTestVisible="False"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}"
SelectedItem="{Binding SelectedTestItem}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}"
SelectedItem="{Binding SelectedTestItem}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Step Two: As you can see in the above xaml
, SelectionUnit
is set to Cell
and also an event handler for DataGridCell.Selected
has been defined. The event handler code is as below:
private void DataGridCell_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
DataGrid dataGrid = (DataGrid)sender;
dataGrid.BeginEdit(e);
}
}
This makes sure that every time you click on a DataGridCell
it will enter editing-mode. therefore you need one less click each time you try to open the ComboBox
inside the newly created DataGridRow
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With