WPF DataGrid 单击以创建新项
WPF DataGrid Single Click to Create New Item
我有一个 DataGrid
,里面有 DataGridTemplateColumn
和 ComboBox
。
<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" };
}
}
那么 DataGrid
的 xaml
应该像这样改变:
<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
时都会输入 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.Interactions 和 System.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。
我有一个 DataGrid
,里面有 DataGridTemplateColumn
和 ComboBox
。
<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" };
}
}
那么 DataGrid
的 xaml
应该像这样改变:
<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
时都会输入 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.Interactions 和 System.Windows.Interactivity 库的引用。然后我在 ComboBox
.
现在在 MainViewModel
中你可以用 ChooseWithInteraction
方法替换 Choose
方法(当然你也可以简单地将它添加到代码中):
public void ChooseWithInteraction(object sender, EventArgs args)
{
object data = ((ComboBox)sender).DataContext;
if (!(data is TestClass))
{
TestItemCollection.Add(new TestClass());
}
}
通过这种方式,您可以获得与我的第一个解决方案相同的行为,但无需使用 Caliburn。