我正在使用ComboBox
元素,这些元素通常包含大量数据。约250000个数据条目。
这样设置ComboBox
时,效果很好。
<ComboBox ItemsSource="{Binding Items}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
但是,我正在使用的ComboBox
的一些自定义修改要求ComboBoxItem
元素不能集中。我通过在ComboBox.ItemContainerStyle
中使用二传手实现了这一点。
<ComboBox ItemsSource="{Binding Items}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter
Property="Focusable"
Value="False" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
但是这有一个问题。在选择对象之前,它可以正常工作。然后,当用户尝试再次打开ComboBox
时,它将使程序崩溃。
我的问题是,如何设置ComboBox
,使其所有ComboBoxItem
元素都无法聚焦,但不会使程序崩溃。
示例代码
XAML
<Window x:Class="FocusableTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Viewbox Stretch="Uniform"
Grid.ColumnSpan="3">
<Label Content="Welcome"
FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Viewbox>
<StackPanel Grid.Row="1"
Grid.Column="1">
<ComboBox ItemsSource="{Binding Items}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter
Property="Focusable"
Value="False" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</StackPanel>
</Grid>
</Window>
C#
using System.Collections.ObjectModel;
using System.Security.Cryptography;
using System.Text;
namespace FocusableTest
{
public partial class MainWindow
{
public MainWindow()
{
for (int i = 0; i < 250000; i++)
{
Items.Add(GetUniqueKey());
}
InitializeComponent();
DataContext = this;
}
public ObservableCollection<string> Items { get; } = new ObservableCollection<string>();
private static string GetUniqueKey(int maxSize = 20)
{
char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[1];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetNonZeroBytes(data);
data = new byte[maxSize];
crypto.GetNonZeroBytes(data);
}
StringBuilder result = new StringBuilder(maxSize);
foreach (byte b in data)
{
result.Append(chars[b % (chars.Length)]);
}
return result.ToString();
}
}
}
答案 0 :(得分:5)
我不确定您的设置看起来如何,但是我设法拥有一个TextBox
和一个ComboBox
,前者在从后者中选择项目时保持了重点。诀窍是双重的-使ComboBox
不能聚焦(因此在单击以打开下拉菜单时它不会失去焦点),并处理PreviewGotKeyboardFocus
上的ComboBoxItem
以防止其将注意力集中在悬停上作为将Focusable
设置为false
的替代方法,这显然是造成问题的原因。这是代码摘录:
<StackPanel>
<TextBox></TextBox>
<ComboBox ItemsSource="{Binding Items}" Focusable="False">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<EventSetter Event="PreviewGotKeyboardFocus"
Handler="ComboBoxItem_PreviewGotKeyboardFocus" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</StackPanel>
和代码隐藏:
private void ComboBoxItem_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
e.Handled = true;
}
答案 1 :(得分:4)
据我所研究的源代码所言,您尝试实现的目标是不可能的。
打开DropDown
时,ComboBox
尝试navigate到SelectedItem
内部调用MakeVisible
滚动到SelectedItem
有趣的部分在这里
GetFirstItemOnCurrentPage(container, FocusNavigationDirection.Up, out firstElement);
while (firstElement != container)
请参见https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/ItemsControl.cs第2735,2736行
在将while-loop
设置为true
时,此Focusable
永远不会评估为false
,因为GetFirstItemOnCurrentPage
总是将firstElement
设置为{{1 }}。这将导致滚动到最后,因此将处理一整堆项目,在您的情况下,这可能会足够快地进行处理,这取决于您当前的null
还剩下多少项目滚动。
SelectedItem
始终将GetFirstItemOnCurrentPage
设置为firstElement
的原因是因为内部调用了FindFocusable
。
null
请参见https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/ItemsControl.cs第2592-2618行
如您所见,private object FindFocusable(int startIndex, int direction, out int foundIndex, out FrameworkElement foundContainer)
{
// HasItems may be wrong when underlying collection does not notify, but this function
// only cares about what's been generated and is consistent with ItemsControl state.
if (HasItems)
{
int count = Items.Count;
for (; startIndex >= 0 && startIndex < count; startIndex += direction)
{
FrameworkElement container = ItemContainerGenerator.ContainerFromIndex(startIndex) as FrameworkElement;
// If the UI is non-null it must meet some minimum requirements to consider it for
// navigation (focusable, enabled). If it has no UI we can make no judgements about it
// at this time, so it is navigable.
if (container == null || Keyboard.IsFocusable(container))
{
foundIndex = startIndex;
foundContainer = container;
return Items[startIndex];
}
}
}
foundIndex = -1;
foundContainer = null;
return null;
}
被调用,在您的情况下始终为Keyboard.IsFocusable
答案 2 :(得分:4)
首先,请澄清一下...您的程序不是执行崩溃(实际运行时错误),而是在执行了给定的操作后挂起(或明显挂起)。
实际上,组合框不是问题,而是其中的项目数。是的,您可能正在使用某些过滤或可搜索的上下文,但是总的挂起是元素的数量。
除了更改随机字符串循环外,无需更改代码中的任何内容,我将列表降低到50,并不断增加double + plus,直到我可以进行更多的测量。.50、100、250、500、1000、2000, 5000等。
当我将计数减少到一个合理的数字时,它可以正常工作……只要将其增加到大约1000个条目,延迟就会变慢,但不会挂起。在5000个条目时,速度仍然较慢,但是打开下拉列表大约用了32秒...这一次是在Dell Alienware计算机i7-2.8Ghz,16gig上。在第二次打开下拉列表后,随后的尝试很快。
现在,有了您的250,000条以上的记录列表,它可能只是为了使系统负荷而使系统窒息,以供以后显示。
所有这些都说明了,这并不是组合框的问题,它绝对是您需要的重新设计的实现。如果您可以编辑原始帖子(仅添加评论),则可以阐明为什么希望该列表包含所有250k +记录而不是子过滤列表。如果进行了子过滤,则最好是根据基础过滤器重新填充列表。
另一个警告...如果我首先选择列表顶部附近的一个项目,请关闭组合框,然后单击以重新打开它,大约需要一分钟以上的时间(10k条记录)才能重新打开。然后,如果我在列表底部附近选择一个条目,将其关闭然后重新打开,它会很快显示。在顶部附近选择一个条目,将其关闭并重新打开,这又需要很长时间。
显然,过分填充列表是行不通的,您需要在用户界面中考虑其他选择。
问题是出在随后的显示尝试中,重新加载了组合框。无论出于何种内部原因,列表中的记录越多,重建显示的时间就越长。
答案 3 :(得分:4)
也许这是一种破解,但是对我有用:(带有Framework 4.7.1的XAML测试项目)
将您的XAML更改为此:
<ComboBox ItemsSource="{Binding Items}" x:Name="MyComboBox" DropDownOpened="DropDownWasOpened">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Focusable" Value="False" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
并在后面的代码中添加处理程序:
private void DropDownWasOpened(object sender, EventArgs e) {
var selectedItem = MyComboBox.SelectedItem;
MyComboBox.SelectedItem = null;
Dispatcher.BeginInvoke(new Action(() => MyComboBox.SelectedItem = selectedItem));
}
它甚至具有组合框打开时具有与关闭时相同的滚动位置的优点。
答案 4 :(得分:-1)
不确定这是否是您要寻找的东西
cv::Vec3