WPF DataGrid ComboBox 模板列在滚动时丢失了选择
WPF DataGrid ComboBox Template Column Loses the Selection While Scrolling
我有一个使用虚拟化的 DataGrid,其中一列使用内部带有 ComboBox 的 DataGridTemplateColumn。但是,当我在 DataGrid 中滚动 left/right(将 ComboBox 列移出然后移入视图)时,ComboBox 失去了选择。
设置 EnableColumnVirtualization="False" 确实解决了这个问题,但是我显然失去了列的虚拟化。
是否有不涉及禁用虚拟化的解决方案?
我整理了一个简单的解决方案来演示该问题。如果您 运行 它并从左向右快速滚动,您会看到 ComboBox 最终变为空,并在其周围显示红色边框。
XAML:
<Window x:Class="SimpleReproTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<DataGrid x:Name="MyGrid" AutoGenerateColumns="False" EnableColumnVirtualization="True" EnableRowVirtualization="True"
VirtualizingStackPanel.VirtualizationMode="Standard" VirtualizingStackPanel.IsVirtualizing="True">
<DataGrid.Columns>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="ComboBox Column"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DummyItems}"
SelectedValue="{Binding DummySelection}"
SelectedValuePath="Key"
DisplayMemberPath="Value"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
隐藏代码:
using System.Collections.Generic;
using System.Windows;
using System.Collections.ObjectModel;
namespace SimpleReproTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<DummyColumn> MyData;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyData = new ObservableCollection<DummyColumn>();
for (int i = 0; i < 50; i++)
MyData.Add(new DummyColumn());
MyGrid.ItemsSource = MyData;
}
}
public class DummyColumn
{
public ObservableCollection<KeyValuePair<int, string>> DummyItems {get; set;}
public string Dummy { get; set; }
public int DummySelection { get; set; }
public DummyColumn()
{
Dummy = "...";
DummySelection = 0;
DummyItems = new ObservableCollection<KeyValuePair<int,string>>();
DummyItems.Add(new KeyValuePair<int,string>(0, "Item 0"));
DummyItems.Add(new KeyValuePair<int,string>(1, "Item 1"));
}
}
}
我的回答的关键点是您只需要将组合的文本 属性 与 UpdateSourceTrigger 绑定等于 属性 已更改。
这是唯一正确的解决方案。
不要被其他不相关的细节所迷惑。如果您非常了解 WPF(不幸的是这里很少有人这样做),您必须知道 On属性Changed(或等效的通知程序)在 UI 属性 的更改时绝对无用源于 UI 对象本身。
试试这个 xaml
<DataTemplate>
<ComboBox ItemsSource="{Binding DummyItems}"
SelectedValue="{Binding DummySelection}"
SelectedValuePath="Key"
Text="{Binding DummySel, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Value"/>
</DataTemplate>
和 ViewModel
private string dummySel;
public string DummySel
{
get { return dummySel; }
set { dummySel = value;
//OnPropertyChanged(() => DummySel);
}
}
private int dummySelection;
public int DummySelection {
get { return dummySelection; }
set {
dummySelection = value;
//OnPropertyChanged(()=>DummySelection);
}
}
我有一个使用虚拟化的 DataGrid,其中一列使用内部带有 ComboBox 的 DataGridTemplateColumn。但是,当我在 DataGrid 中滚动 left/right(将 ComboBox 列移出然后移入视图)时,ComboBox 失去了选择。
设置 EnableColumnVirtualization="False" 确实解决了这个问题,但是我显然失去了列的虚拟化。
是否有不涉及禁用虚拟化的解决方案?
我整理了一个简单的解决方案来演示该问题。如果您 运行 它并从左向右快速滚动,您会看到 ComboBox 最终变为空,并在其周围显示红色边框。
XAML:
<Window x:Class="SimpleReproTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<DataGrid x:Name="MyGrid" AutoGenerateColumns="False" EnableColumnVirtualization="True" EnableRowVirtualization="True"
VirtualizingStackPanel.VirtualizationMode="Standard" VirtualizingStackPanel.IsVirtualizing="True">
<DataGrid.Columns>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="ComboBox Column"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DummyItems}"
SelectedValue="{Binding DummySelection}"
SelectedValuePath="Key"
DisplayMemberPath="Value"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
<DataGridTextColumn Header="DummyColumn" Binding="{Binding Dummy}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
隐藏代码:
using System.Collections.Generic;
using System.Windows;
using System.Collections.ObjectModel;
namespace SimpleReproTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<DummyColumn> MyData;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyData = new ObservableCollection<DummyColumn>();
for (int i = 0; i < 50; i++)
MyData.Add(new DummyColumn());
MyGrid.ItemsSource = MyData;
}
}
public class DummyColumn
{
public ObservableCollection<KeyValuePair<int, string>> DummyItems {get; set;}
public string Dummy { get; set; }
public int DummySelection { get; set; }
public DummyColumn()
{
Dummy = "...";
DummySelection = 0;
DummyItems = new ObservableCollection<KeyValuePair<int,string>>();
DummyItems.Add(new KeyValuePair<int,string>(0, "Item 0"));
DummyItems.Add(new KeyValuePair<int,string>(1, "Item 1"));
}
}
}
我的回答的关键点是您只需要将组合的文本 属性 与 UpdateSourceTrigger 绑定等于 属性 已更改。
这是唯一正确的解决方案。 不要被其他不相关的细节所迷惑。如果您非常了解 WPF(不幸的是这里很少有人这样做),您必须知道 On属性Changed(或等效的通知程序)在 UI 属性 的更改时绝对无用源于 UI 对象本身。
试试这个 xaml
<DataTemplate>
<ComboBox ItemsSource="{Binding DummyItems}"
SelectedValue="{Binding DummySelection}"
SelectedValuePath="Key"
Text="{Binding DummySel, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Value"/>
</DataTemplate>
和 ViewModel
private string dummySel;
public string DummySel
{
get { return dummySel; }
set { dummySel = value;
//OnPropertyChanged(() => DummySel);
}
}
private int dummySelection;
public int DummySelection {
get { return dummySelection; }
set {
dummySelection = value;
//OnPropertyChanged(()=>DummySelection);
}
}