【问题标题】:WPF DataGrid Single Click to Create New ItemWPF DataGrid 单击以创建新项目
【发布时间】:2016-01-13 17:52:39
【问题描述】:

我有一个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 时,它显然不会创建我的集合的新实例,而只会给出一个空白列表。

我必须双击空白行空间。

只有这样我才能在ComboBox 中获取数据。

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

【问题讨论】:

    标签: wpf mvvm combobox datagrid datagridtemplatecolumn


    【解决方案1】:

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

    第一步:您应该为DataGridTemplateColumn 指定CellEditingTemplate,并为DataGridTemplateColumn.CellTemplate 设置IsHitTestVisiblefalse。这将强制 UI 在单击 DataGridCell(例如您的 ComboBox)时进入编辑模式,结果将创建您的集合的新实例。为此,您应该首先在 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>
    

    第二步:正如您在上面的xaml 中看到的,SelectionUnit 设置为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 时,它都会进入编辑模式。因此,每次尝试在新创建的DataGridRow 中打开ComboBox 时,您需要少点击一次。

    【讨论】:

    • 少一个,但仍然不是一个 :) 需要两次点击才能打开下拉菜单。但无论如何,谢谢,你的解决方案适合我。
    • 你是对的,第一次点击是选择一个新的单元格,第二次点击会打开下拉菜单。
    【解决方案2】:

    如果您需要在ComboBox 中通过 点击空白行来获取数据,我建议您使用Caliburn.Micro 将命令“附加”到DropDownOpened 事件你的ComboBox

    这里是一个示例:首先是 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,你只需要这样修改MainView 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: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 中添加了EventTriggerCallMethodAction

    现在在MainViewModel 中,您可以将Choose 方法替换为ChooseWithInteraction 之一(当然您也可以简单地将其添加到代码中):

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

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

    【讨论】:

    • 我知道你的想法。这将在 DataContext 中创建 TestClass 的新实例,但不会打开带有数据的 Combobox。和之前一样,您首先需要选择行,然后 Combobox 才会获取数据。
    • 这不对@Spirosi。我用 .NET 4 和 Caliburn.Micro 2.0.2 测试了我的代码;如果我点击最后一个组合框 - 放在“插入行”内的那个 - 它会打开 with 数据(即“第一个测试项目”,“第二个测试项目” ,“第三个测试项目”和“第四个测试项目”)。你有不同的行为吗?
    • 我只用交互性测试过它。将与 Cliburn 一起尝试。
    • 是的,你是对的,它适用于 Caliburn.Micro。但不幸的是我不能在我的项目中使用它。您能否尝试使用 Windows.Interactivity 实现相同的功能。我的尝试没有成功。将不胜感激。
    • 工作正常。谢谢。
    猜你喜欢
    • 2018-03-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-21
    • 2015-05-05
    • 2014-10-09
    相关资源
    最近更新 更多