在我的WPF窗口应用程序中,我有一个组合框,它会在应用程序启动时填充;我还想为组合框启用suggestappend类型的功能(使用此功能(。但当我试图在组合框上键入内容时,我会收到错误:System.InvalidOperationException: Items collection must be empty before using ItemsSource.
我该如何摆脱它?我想从下拉列表中选择,或者开始在组合框中键入,然后在窗口加载时从中选择项目。
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" />
使用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
有自己的逻辑,没有样式,后面的代码有一些奇怪的元素,比如移动Caret等等…
添加本地集合,比如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的…;-(