Stu*_*gan 3 wpf combobox textbox itemspaneltemplate
我想进行搜索ComboBox,搜索框TextBox显示ItemsPanel在ComboBox下拉列表展开的上方.我想我需要制作一个自定义控件才能实现搜索功能,但首先我只想尝试TextBox使用普通模式进行显示ComboBox.这是我当前的尝试,当我尝试扩展下拉列表时产生异常:
<Style x:Key="FilteredComboBox" TargetType="ComboBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel>
<TextBox/>
<StackPanel IsItemsHost="True"
Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</StackPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)
这产生的例外是:
无法显式修改用作ItemsControl的ItemsPanel的Panel的Children集合.ItemsControl为Panel生成子元素.
我很确定有办法让这样做我想要的,但经过几个小时的谷歌搜索和反复试验,我的脑袋现在正在旋转.任何帮助将不胜感激!
虽然我得到的答案都在正确的轨道上,但我认为他们中的任何一个都没有真正回答我的问题,不足以被标记为解决方案.我最终做的是从ComboBox派生自定义控件(右键单击设计器中的ComboBox并选择编辑模板 - >编辑副本,这会为ComboBox模板生成一大堆XAML代码,我将其复制到我制作的自定义控件项目的Generic.xaml文件.然后我编辑了生成的XAML代码的Popup部分,在ItemsPresenter上面添加了WatermarkTextBox(来自Extended WPF Toolkit),如下所示:
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}">
<Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
</Canvas>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<xtk:WatermarkTextBox Grid.Row="0"
Visibility="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource booleanToVisibilityConverter}}"
Watermark="Type here to filter..."
Text="{Binding SearchFilter, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}">
</xtk:WatermarkTextBox>
<ItemsPresenter x:Name="ItemsPresenter" Grid.Row="1" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
Run Code Online (Sandbox Code Playgroud)
我正在绑定Text的SearchFilter属性是我的自定义控件的代码隐藏中的属性,然后执行ComboBox中值的实际过滤.这有点超出了原始问题的范围,但是如果有人好奇的话,这就是我在做过滤的方式:
public class SearchableComboBox : ComboBox
{
public const string SearchFilterPropertyName = "SearchFilter";
public readonly static DependencyProperty SearchFilterProperty;
public string SearchFilter
{
get { return (string)GetValue(SearchFilterProperty); }
set { SetValue(SearchFilterProperty, value); }
}
static SearchableComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchableComboBox), new FrameworkPropertyMetadata(typeof(SearchableComboBox)));
SearchFilterProperty = DependencyProperty.Register(SearchFilterPropertyName, typeof(string), typeof(SearchableComboBox),
new PropertyMetadata(string.Empty, new PropertyChangedCallback(SearchFilter_PropertyChanged)));
}
private static void SearchFilter_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((SearchableComboBox)d).RefreshFilter();
}
private void RefreshFilter()
{
if (this.ItemsSource != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.Refresh();
}
}
private bool FilterPredicate(object value)
{
if (value == null)
return false;
if (string.IsNullOrEmpty(SearchFilter))
return true;
return value.ToString().Contains(SearchFilter, StringComparison.CurrentCultureIgnoreCase);
}
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (newValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
view.Filter += this.FilterPredicate;
}
if (oldValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
view.Filter -= this.FilterPredicate;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
Run Code Online (Sandbox Code Playgroud)