Search code examples
wpfmvvmcomboboxdatagriddatagridtemplatecolumn

WPF DataGrid Single Click to Create New Item


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.

enter image description here

I have to doubleclick on empty row space.

enter image description here

And only then I would get data in the ComboBox.

enter image description here

How can I get data in the Combobox with a single click on blank row??


Solution

  • 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.