WPF: ComboBox with reset item

后端 未结 9 1917
长发绾君心
长发绾君心 2020-12-15 23:33

I want to make a ComboBox in WPF that has one null item on the top, when this gets selected, the SelectedItem should be set to null (reset to default state). I\

相关标签:
9条回答
  • 2020-12-16 00:01

    Remove the following line and add a CheckBox, then you can perform your custom operation.

        <ComboBoxItem>(None)</ComboBoxItem>
    
    0 讨论(0)
  • 2020-12-16 00:01

    Still not 100% happy with this solution, but the best thing I found so far, you only need to override the ComboBox Style and apply an AttachedBehaviour.

    <ComboBox ItemsSource="{Binding Names}"
              ext:ComboBoxHelper.IsNullable="True" />
    

    Source: http://xamlblog.com/PostPage.aspx?postId=16#/Posts/16

    Edit: Link to the Internet Archive since the link is broken: https://web.archive.org/web/20160420174905/http://xamlblog.com/PostPage.aspx?postId=16

    0 讨论(0)
  • 2020-12-16 00:07

    A little more elaborate than some answers here, but didn't want to have any code behind or ViewModel changes in mine. I wrote this as a WPF behavior. When attached to the XAML, it will inject a button in the visual. It will set the Default value of -1 (or you can adjust to be something else default). This is a re-usable control that is easy to add on your XAML throughout your project. Hope this helps. Open to feedback if you spot an error.

    1. There are no external references, you can use this with your code and no other DLLs. (well, it does use System.Windows.Interactivity but most will have this in WPF apps)
    2. Its re-usable throughout your application
    3. Style will conform to your themes.
    4. You can jack this up all you want
    5. I know this is an almost 6 year old thread (as of my writing in 2019), but if you like it -- make it the answer since there isn't one!

    Resulting Visual:

    Item Selected:

    Behavior Code:

    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;
    }
    

    Usage:

    <ComboBox 
     ItemsSource="{Binding SomeItemsSource, Mode=OneWay}"
     SelectedValue="{Binding SomeId, Mode=TwoWay}"
     SelectedValuePath="SomeId">
      <i:Interaction.Behaviors>
        <behaviors:ComboBoxClearBehavior />
      </i:Interaction.Behaviors>
    </ComboBox>
    

    You will need two things for this Behavior -- you may already have them, but here they are:

    1.) The Button Template - The code is looking for a style. In my case, it's called NoChromeButton- If you are looking for a turnkey solution, you can add mine to your resources file:

    <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>
    

    Also you will need your icon for the clear. If you have one, just update the code to use that resource (named "RemoveIcon"). Otherwize.. here is mine:

    <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>
    
    0 讨论(0)
提交回复
热议问题