我遇到了一个特殊的问题,我在修理时遇到了麻烦。下面是显示问题的简化代码。
我有一个WPF DropDownButton,其中包含下拉列表中的字符串列表。我也有一个搜索框。当您在搜索框中键入内容时,下拉菜单会自动展开并仅显示匹配的项目(从而使您更容易找到您感兴趣的项目)。如果没有匹配的项目或搜索字段为空,则关闭下拉列表。如果存在搜索词但没有匹配项,则搜索字段会更改颜色。
这一切都很好。在此之前,您要么使用它的按钮清除搜索字段(当有清除的东西时),要么使用它的下拉箭头按钮下拉列表。完成其中任何一项操作后,当您更改搜索字词时,下拉列表将不再自动打开和关闭。
一旦“破坏”,搜索仍然有效,您可以搜索某些内容,例如'文本1',如果您打开下拉列表,它只包含匹配的项目。只是自动打开和关闭不再有效。
我已经检查过,ViewModel正在引发正确的事件。我已经查看了DropDownButton代码,并且在它到达OnIsOpenChanged
代码时可以看到它,但是一旦被破坏它就不会。
可能是通过'手动'打开下拉列表我打破/覆盖IsOpen
绑定吗?如果是这样,我该如何解决这个问题。为什么清除搜索字段也会破坏绑定?删除手动下拉菜单不是一种选择。
修改
在设置我添加的SeachExpression
之后,在后面的按钮单击代码(我已经尝试用命令替换,但它对行为没有影响):
BindingExpression be = BindingOperations.GetBindingExpression(SelectButton, Xceed.Wpf.Toolkit.DropDownButton.IsOpenProperty);
我认为是检查是否存在绑定的正确方法,它返回null。如果我在设置SeachExpression
之前执行此操作,则它为非空。
视图模型
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private IList<string> originalText;
public string SearchExpression
{
get
{
return _searchExpression;
}
set
{
if (value == _searchExpression)
return;
_searchExpression = value;
UpdateDescriptions(_searchExpression);
OnPropertyChanged("SearchExpression");
}
}
string _searchExpression = string.Empty;
public ReadOnlyObservableCollection<string> Descriptions
{
get { return _descriptions; }
private set
{
if (value == _descriptions)
return;
_descriptions = value;
OnPropertyChanged("Descriptions");
}
}
ReadOnlyObservableCollection<string> _descriptions;
public bool NoMatches
{
get { return _noMatches; }
private set
{
if (value == _noMatches)
return;
_noMatches = value;
OnPropertyChanged("NoMatches");
}
}
bool _noMatches;
public bool ShowSearchResults
{
get { return _showSearchResults; }
private set
{
if (value == _showSearchResults)
return;
_showSearchResults = value;
OnPropertyChanged("ShowSearchResults");
}
}
bool _showSearchResults;
public ViewModel()
{
originalText = new List<string>() {"Text 1", "Text 2", "Text 3"};
UpdateDescriptions();
}
private void UpdateDescriptions(string searchExpression = null)
{
ObservableCollection<string> descriptions = new ObservableCollection<string>();
IEnumerable<string> records;
if (string.IsNullOrWhiteSpace(searchExpression))
{
records = originalText;
NoMatches = false;
ShowSearchResults = false;
}
else
{
records = originalText.Where(x => x.Contains(searchExpression));
NoMatches = records.Count() < 1;
ShowSearchResults = records.Count() > 0;
}
descriptions = new ObservableCollection<string>(records);
this.Descriptions = new ReadOnlyObservableCollection<string>(descriptions);
}
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
视图的
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpf="clr-namespace:Xceed.Wpf.Toolkit;assembly=WPFToolkit.Extended"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="65" Width="225">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<wpf:DropDownButton Grid.Column="0" x:Name="SelectButton" Content="Select" Margin="3 0" IsOpen="{Binding ShowSearchResults, UpdateSourceTrigger=PropertyChanged}">
<wpf:DropDownButton.DropDownContent>
<ListBox x:Name="descriptionsList" MaxHeight="250" ItemsSource="{Binding Path=Descriptions, Mode=OneWay}" HorizontalContentAlignment="Stretch">
<!-- this code makes sure the ListBox displays at the correct with for the outset, and does not resize as you scroll-->
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="True">
<Setter Property="Control.Background" Value="{x:Static SystemColors.HighlightBrush}" />
<Setter Property="Control.Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</wpf:DropDownButton.DropDownContent>
</wpf:DropDownButton>
<Border Grid.Column="2" Background="White" BorderBrush="Gray" BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<xctk:WatermarkTextBox x:Name="searchTextBox" Text="{Binding SearchExpression, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Watermark="Search" ToolTip="Search for an item in the list." >
<xctk:WatermarkTextBox.Style>
<Style>
<Setter Property="Control.BorderThickness" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding NoMatches}" Value="True" >
<Setter Property="TextBox.Background" Value="Tomato"/>
<Setter Property="TextBox.Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</xctk:WatermarkTextBox.Style>
</xctk:WatermarkTextBox>
<Button Grid.Column="1"
x:Name="ClearButton"
ToolTip="Clear search text"
Click="ClearButton_Click"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image Source="/WPFToolkit.Extended;component/PropertyGrid/Images/ClearFilter16.png" Width="16" Height="16" />
</Button>
</Grid>
</Border>
</Grid>
查看代码背后
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
private void ClearButton_Click(object sender, RoutedEventArgs e)
{
ViewModel vm = this.DataContext as ViewModel;
if (vm != null)
vm.SearchExpression = "";
}
}