WPF 组合框 - 尝试输入时显示错误
WPF combo box - error showing when trying to type in it
在我的 WPF window 应用程序中,我有一个组合框,它在应用程序启动时被填充,我还想为组合框 (using this) 启用 suggestappend 类型功能。但是,当我尝试在组合框中键入内容时,出现错误:System.InvalidOperationException: Items collection must be empty before using ItemsSource.
我该如何摆脱它?我想从下拉列表中 select 或开始在组合框中输入 select 项目,然后 window 加载 select 项目。
public partial class Window1 : Window
{
List<string> Names= new List<string>();
int lastRow = 0;
string file_Bills=@"BILLS.xlsx";
string file_CNDN=@"CN_DN.xlsx";
string file_supp=@"suppliers.xlsx";
public Window1()
{
InitializeComponent();
ExcelPackage.LicenseContext =LicenseContext.NonCommercial;
FillCombo();
}
public List<string> FillCombo()
{
using (ExcelPackage package = new ExcelPackage(new System.IO.FileInfo(file_Bills), false))
{
ExcelWorksheet mainSheet = package.Workbook.Worksheets.First();
for (int i = 2; i <= mainSheet.Dimension.End.Row; i++)
{
if (!string.IsNullOrEmpty(mainSheet.Cells["A"+i].Text))
{
lastRow =i;
}
}
List<string> party = new List<string>();
for (int row = 2; row <= lastRow; row++)
{
if (!string.IsNullOrEmpty(mainSheet.Cells[row, 1].Text))
{
party.Add(mainSheet.Cells[row, 1].Text);
}
}
foreach (var element in party.OrderBy(a=>a.ToLowerInvariant()).Distinct())
{
cmb.Items.Add(element);
Names.Add(element);
}
}
return Names;
}
//portion implementing the suggestappend like feature
public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
private void PreviewTextInput_EnhanceComboSearch(object sender, TextCompositionEventArgs e)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
if (!string.IsNullOrEmpty(cmb.Text))
{
string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, e.Text);
cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else if (!string.IsNullOrEmpty(e.Text))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
private void Pasting_EnhanceComboSearch(object sender, DataObjectPastingEventArgs e)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
string pastedText = (string)e.DataObject.GetData(typeof(string));
string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, pastedText);
if (!string.IsNullOrEmpty(fullText))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
private void PreviewKeyUp_EnhanceComboSearch(object sender, KeyEventArgs e)
{
if (e.Key == Key.Back || e.Key == Key.Delete)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
if (!string.IsNullOrEmpty(cmb.Text))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(cmb.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
}
}
XAML:
<ComboBox
IsTextSearchEnabled="False"
PreviewTextInput="PreviewTextInput_EnhanceComboSearch"
PreviewKeyUp="PreviewKeyUp_EnhanceComboSearch"
DataObject.Pasting="Pasting_EnhanceComboSearch"
IsEditable="True"
Name="cmb"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontFamily="Segoe UI"
FontStyle="Normal"
FontSize="18"
Margin="10 10 10 0"
Height="35"
MaxHeight="40"
Width="300"
MaxWidth="450" />
添加本地集合,比方说cBItems
private List<string> cBItems;
在你的c'中填写列表,而不是添加到cmb.Items
foreach (var element in party.OrderBy(a=>a.ToLowerInvariant()).Distinct())
{
cBItems.Add(element);
Names.Add(element);
}
然后设置ItemsSource
cmb.ItemsSource = cBItems;
所以你只操纵 ItemsSource
,而让 Items
为空
无论如何,还有 CollectionView
等其他选项...但那是针对 MVVM...;-)
使用 ICollectionView.Filter 会像这样工作:
XAML:
<Window x:Class="WPFSandbox.Window1"
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"
xmlns:local="clr-namespace:WPFSandbox"
mc:Ignorable="d"
Title="Window1"
Height="450"
Width="800">
<Grid>
<ComboBox x:Name="cmb"
IsEditable="True"
KeyUp="cmb_KeyUp"
Height="25"
VerticalAlignment="Top"
IsTextSearchEnabled="False"
IsReadOnly="false"
/>
</Grid>
</Window>
xaml.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace WPFSandbox
{
public partial class Window1 : Window
{
private List<string> Items { get; }
private ICollectionView view;
public Window1()
{
InitializeComponent();
Items = new List<string>()
{
"john",
"john doe",
"jane",
"jane doe",
"steve miller",
"jane miller"
};
cmb.ItemsSource = Items;
KeyboardNavigation.SetDirectionalNavigation(cmb, KeyboardNavigationMode.Cycle);
view = CollectionViewSource.GetDefaultView(Items);
}
private void cmb_KeyUp(object sender, KeyEventArgs e)
{
var tb = cmb.Template.FindName("PART_EditableTextBox", cmb) as TextBox;
var val = tb.Text;
var empty = string.IsNullOrEmpty(tb.Text);
var keysToIgnore = new Key[] { Key.Down, Key.Up, Key.Enter, Key.Left, Key.Right };
if (keysToIgnore.Contains(e.Key))
{
return;
}
if (empty)
{
view.Filter = null;
}
else
{
view.Filter = (i) =>
{
var str = i.ToString();
return str.ToLowerInvariant().Contains(tb.Text.ToLowerInvariant());
};
}
cmb.IsDropDownOpen = true;
tb.Text = val;
tb.CaretIndex = tb.Text.Length;
}
}
}
由于 EditableTextBox
有自己的逻辑,w/o 对其进行样式设置,因此 code-behind 有一些奇怪的元素,例如移动插入符等...
在我的 WPF window 应用程序中,我有一个组合框,它在应用程序启动时被填充,我还想为组合框 (using this) 启用 suggestappend 类型功能。但是,当我尝试在组合框中键入内容时,出现错误:System.InvalidOperationException: Items collection must be empty before using ItemsSource.
我该如何摆脱它?我想从下拉列表中 select 或开始在组合框中输入 select 项目,然后 window 加载 select 项目。
public partial class Window1 : Window
{
List<string> Names= new List<string>();
int lastRow = 0;
string file_Bills=@"BILLS.xlsx";
string file_CNDN=@"CN_DN.xlsx";
string file_supp=@"suppliers.xlsx";
public Window1()
{
InitializeComponent();
ExcelPackage.LicenseContext =LicenseContext.NonCommercial;
FillCombo();
}
public List<string> FillCombo()
{
using (ExcelPackage package = new ExcelPackage(new System.IO.FileInfo(file_Bills), false))
{
ExcelWorksheet mainSheet = package.Workbook.Worksheets.First();
for (int i = 2; i <= mainSheet.Dimension.End.Row; i++)
{
if (!string.IsNullOrEmpty(mainSheet.Cells["A"+i].Text))
{
lastRow =i;
}
}
List<string> party = new List<string>();
for (int row = 2; row <= lastRow; row++)
{
if (!string.IsNullOrEmpty(mainSheet.Cells[row, 1].Text))
{
party.Add(mainSheet.Cells[row, 1].Text);
}
}
foreach (var element in party.OrderBy(a=>a.ToLowerInvariant()).Distinct())
{
cmb.Items.Add(element);
Names.Add(element);
}
}
return Names;
}
//portion implementing the suggestappend like feature
public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
private void PreviewTextInput_EnhanceComboSearch(object sender, TextCompositionEventArgs e)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
if (!string.IsNullOrEmpty(cmb.Text))
{
string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, e.Text);
cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else if (!string.IsNullOrEmpty(e.Text))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
private void Pasting_EnhanceComboSearch(object sender, DataObjectPastingEventArgs e)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
string pastedText = (string)e.DataObject.GetData(typeof(string));
string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, pastedText);
if (!string.IsNullOrEmpty(fullText))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
private void PreviewKeyUp_EnhanceComboSearch(object sender, KeyEventArgs e)
{
if (e.Key == Key.Back || e.Key == Key.Delete)
{
ComboBox cmb = (ComboBox)sender;
cmb.IsDropDownOpen = true;
if (!string.IsNullOrEmpty(cmb.Text))
{
cmb.ItemsSource = Names.Where(s => s.IndexOf(cmb.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
}
else
{
cmb.ItemsSource = Names;
}
}
}
}
XAML:
<ComboBox
IsTextSearchEnabled="False"
PreviewTextInput="PreviewTextInput_EnhanceComboSearch"
PreviewKeyUp="PreviewKeyUp_EnhanceComboSearch"
DataObject.Pasting="Pasting_EnhanceComboSearch"
IsEditable="True"
Name="cmb"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontFamily="Segoe UI"
FontStyle="Normal"
FontSize="18"
Margin="10 10 10 0"
Height="35"
MaxHeight="40"
Width="300"
MaxWidth="450" />
添加本地集合,比方说cBItems
private List<string> cBItems;
在你的c'中填写列表,而不是添加到cmb.Items
foreach (var element in party.OrderBy(a=>a.ToLowerInvariant()).Distinct())
{
cBItems.Add(element);
Names.Add(element);
}
然后设置ItemsSource
cmb.ItemsSource = cBItems;
所以你只操纵 ItemsSource
,而让 Items
为空
无论如何,还有 CollectionView
等其他选项...但那是针对 MVVM...;-)
使用 ICollectionView.Filter 会像这样工作:
XAML:
<Window x:Class="WPFSandbox.Window1"
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"
xmlns:local="clr-namespace:WPFSandbox"
mc:Ignorable="d"
Title="Window1"
Height="450"
Width="800">
<Grid>
<ComboBox x:Name="cmb"
IsEditable="True"
KeyUp="cmb_KeyUp"
Height="25"
VerticalAlignment="Top"
IsTextSearchEnabled="False"
IsReadOnly="false"
/>
</Grid>
</Window>
xaml.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace WPFSandbox
{
public partial class Window1 : Window
{
private List<string> Items { get; }
private ICollectionView view;
public Window1()
{
InitializeComponent();
Items = new List<string>()
{
"john",
"john doe",
"jane",
"jane doe",
"steve miller",
"jane miller"
};
cmb.ItemsSource = Items;
KeyboardNavigation.SetDirectionalNavigation(cmb, KeyboardNavigationMode.Cycle);
view = CollectionViewSource.GetDefaultView(Items);
}
private void cmb_KeyUp(object sender, KeyEventArgs e)
{
var tb = cmb.Template.FindName("PART_EditableTextBox", cmb) as TextBox;
var val = tb.Text;
var empty = string.IsNullOrEmpty(tb.Text);
var keysToIgnore = new Key[] { Key.Down, Key.Up, Key.Enter, Key.Left, Key.Right };
if (keysToIgnore.Contains(e.Key))
{
return;
}
if (empty)
{
view.Filter = null;
}
else
{
view.Filter = (i) =>
{
var str = i.ToString();
return str.ToLowerInvariant().Contains(tb.Text.ToLowerInvariant());
};
}
cmb.IsDropDownOpen = true;
tb.Text = val;
tb.CaretIndex = tb.Text.Length;
}
}
}
由于 EditableTextBox
有自己的逻辑,w/o 对其进行样式设置,因此 code-behind 有一些奇怪的元素,例如移动插入符等...