比这里的一些答案更详细一点,但不想在我的后面有任何代码或 ViewModel 更改。我将其写为 WPF 行为。当附加到 XAML 时,它将在视觉对象中注入一个按钮。它将默认值设置为 -1(或者您可以调整为其他默认值)。这是一个可重用的控件,很容易在整个项目中添加到 XAML 中。希望这可以帮助。如果您发现错误,请接受反馈。
- 没有外部引用,您可以将其与您的代码一起使用,而无需其他 DLL。(嗯,它确实使用 System.Windows.Interactivity 但大多数都会在 WPF 应用程序中使用它)
- 它可在您的整个应用程序中重复使用
- 风格将符合您的主题。
- 你可以随心所欲
- 我知道这是一个有将近 6 年历史的线程(截至我在 2019 年写作时),但如果你喜欢它 - 让它成为答案,因为没有一个!
结果视觉:
选择的项目:
行为代码:
public class ComboBoxClearBehavior : Behavior<ComboBox>
{
private Button _addedButton;
private ContentPresenter _presenter;
private Thickness _originalPresenterMargins;
protected override void OnAttached()
{
// Attach to the Loaded event. The visual tree at this point is not available until its loaded.
AssociatedObject.Loaded += AssociatedObject_Loaded;
// If the user or code changes the selection, re-evaluate if we should show the clear button
AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
base.OnAttached();
}
protected override void OnDetaching()
{
// Its likely that this is already de-referenced, but just in case the visual was never loaded, we will remove the handler anyways.
AssociatedObject.Loaded -= AssociatedObject_Loaded;
base.OnDetaching();
}
private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
EvaluateDisplay();
}
/// <summary>
/// Checks to see if the UI should show a Clear button or not based on what is or isn't selected.
/// </summary>
private void EvaluateDisplay()
{
if (_addedButton == null) return;
_addedButton.Visibility = AssociatedObject.SelectedIndex == -1 ? Visibility.Collapsed : Visibility.Visible;
// To prevent the text or content from being overlapped by the button, adjust the margins if we have reference to the presenter.
if (_presenter != null)
{
_presenter.Margin = new Thickness(
_originalPresenterMargins.Left,
_originalPresenterMargins.Top,
_addedButton.Visibility == Visibility.Visible ? ClearButtonSize + 6 : _originalPresenterMargins.Right,
_originalPresenterMargins.Bottom);
}
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
// After we have loaded, we will have access to the Children objects. We don't want this running again.
AssociatedObject.Loaded -= AssociatedObject_Loaded;
// The ComboBox primary Grid is named MainGrid. We need this to inject the button control. If missing, you may be using a custom control.
if (!(AssociatedObject.FindChild("MainGrid") is Grid grid)) return;
// Find the content presenter. We need this to adjust the margins if the Clear icon is present.
_presenter = grid.FindChildren<ContentPresenter>().FirstOrDefault();
if (_presenter != null) _originalPresenterMargins = _presenter.Margin;
// Create the new button to put in the view
_addedButton = new Button
{
Height = ClearButtonSize,
Width = ClearButtonSize,
HorizontalAlignment = HorizontalAlignment.Right
};
// Find the resource for the button - In this case, our NoChromeButton Style has no button edges or chrome
if (Application.Current.TryFindResource("NoChromeButton") is Style style)
{
_addedButton.Style = style;
}
// Find the resource you want to put in the button content
if (Application.Current.TryFindResource("RemoveIcon") is FrameworkElement content)
{
_addedButton.Content = content;
}
// Hook into the Click Event to handle clearing
_addedButton.Click += ClearSelectionButtonClick;
// Evaluate if we should display. If there is nothing selected, don't show.
EvaluateDisplay();
// Add the button to the grid - First Column as it will be right justified.
grid.Children.Add(_addedButton);
}
private void ClearSelectionButtonClick(object sender, RoutedEventArgs e)
{
// Sets the selected index to -1 which will set the selected item to null.
AssociatedObject.SelectedIndex = -1;
}
/// <summary>
/// The Button Width and Height. This can be changed in the Xaml if a different size visual is desired.
/// </summary>
public int ClearButtonSize { get; set; } = 15;
}
用法:
<ComboBox
ItemsSource="{Binding SomeItemsSource, Mode=OneWay}"
SelectedValue="{Binding SomeId, Mode=TwoWay}"
SelectedValuePath="SomeId">
<i:Interaction.Behaviors>
<behaviors:ComboBoxClearBehavior />
</i:Interaction.Behaviors>
</ComboBox>
对于此行为,您将需要两件事 - 您可能已经拥有它们,但它们在这里:
1.) 按钮模板 - 代码正在寻找一种样式。就我而言,它被称为 NoChromeButton- 如果您正在寻找一个交钥匙解决方案,您可以将我的添加到您的资源文件中:
<Style x:Key="NoChromeButton"
TargetType="{x:Type Button}">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Foreground"
Value="{DynamicResource WindowText}" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="Cursor"
Value="Hand"/>
<Setter Property="Padding"
Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="Chrome"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="#ADADAD" />
<Setter Property="Opacity"
TargetName="Chrome"
Value="0.5" />
</Trigger>
<Trigger
Property="IsMouseOver"
Value="True">
<Setter
TargetName="Chrome"
Property="Background"
Value="{DynamicResource ButtonBackgroundHover}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
此外,您将需要您的图标来清除。如果你有,只需更新代码以使用该资源(名为“RemoveIcon”)。否则..这是我的:
<Viewbox x:Key="RemoveIcon"
x:Shared="False"
Stretch="Uniform">
<Canvas Width="58"
Height="58">
<Path Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Control, Mode=FindAncestor}}">
<Path.Data>
<PathGeometry Figures="M 29 0 C 13 0 0 13 0 29 0 45 13 58 29 58 45 58 58 45 58 29 58 13 45 0 29 0 Z M 43.4 40.6 40.6 43.4 29 31.8 17.4 43.4 14.6 40.6 26.2 29 14.6 17.4 17.4 14.6 29 26.2 40.6 14.6 43.4 17.4 31.8 29 Z"
FillRule="NonZero" />
</Path.Data>
</Path>
</Canvas>
</Viewbox>