WPF DataGrid 单击以创建新项

WPF DataGrid Single Click to Create New Item

我有一个 DataGrid,里面有 DataGridTemplateColumnComboBox

<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" 
        };
    }

当我单击空白行中的 ComboBox 时,它显然不会创建我的集合的新实例,只会提供一个空白列表。

我必须双击空行 space。

只有这样我才能在 ComboBox.

中获取数据

如何通过单击空白行获取 Combobox 中的数据?

我找到了一个解决方案,您可以通过以下两个步骤实现所需的功能。

第一步: 您应该为 DataGridTemplateColumn 指定一个 CellEditingTemplate 并为 DataGridTemplateColumn.CellTemplate。这将强制 UI 在单击 DataGridCell(例如您的 ComboBox)时输入 edit-mode,因此您的 collection 的新实例将被创建。为此,您应该首先在 TestClass 中定义一个 属性 以保留每个 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" };
    }

}

那么 DataGridxaml 应该像这样改变:

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

第二步: 正如你在上面看到的 xamlSelectionUnit 设置为 Cell 并且也是一个事件处理程序DataGridCell.Selected 已定义。事件处理代码如下:

    private void DataGridCell_GotFocus(object sender, RoutedEventArgs e)
    {
        if (e.OriginalSource.GetType() == typeof(DataGridCell))
        {
            DataGrid dataGrid = (DataGrid)sender;
            dataGrid.BeginEdit(e);
        }
    }

这样可以确保每次单击 DataGridCell 时都会输入 editing-mode。因此,每次尝试打开新创建的 DataGridRow.

中的 ComboBox 时,您都需要少点击一次

如果你需要在ComboBox中获取数据点击空白行,我建议你使用Caliburn.Micro到"attach" 对 ComboBox.

DropDownOpened 事件的命令

这里有一个例子:首先 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>

然后是 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; }
}

请注意,在我的示例中,TestClass 代码与您编写的代码相同。当然,您必须配置您的应用程序才能使用 Caliburn.Micro(如果您不知道如何操作,可以阅读 documentation)。

如果您不想(或者可能不能)使用 Caliburn.Micro,您可以使用 System.Windows.Interactivity 库获得相同的结果(请参阅下面我的编辑)。

试试代码:只需单击一下,就会自动创建一个新行。 希望对您有所帮助。

编辑: System.Windows.Interactivity

的替代解决方案

如果不能使用Caliburn.Micro,只需修改MainViewXAML即可:

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

如您所见,我刚刚添加了对 Microsoft.Expression.InteractionsSystem.Windows.Interactivity 库的引用。然后我在 ComboBox.

中添加了一个 EventTrigger and a CallMethodAction

现在在 MainViewModel 中你可以用 ChooseWithInteraction 方法替换 Choose 方法(当然你也可以简单地将它添加到代码中):

public void ChooseWithInteraction(object sender, EventArgs args)
{
    object data = ((ComboBox)sender).DataContext;
    if (!(data is TestClass))
    {
        TestItemCollection.Add(new TestClass());
    }
}

通过这种方式,您可以获得与我的第一个解决方案相同的行为,但无需使用 Caliburn。