根据项目数量启用/禁用 WPF ComboBox 的行为

huangapple go评论80阅读模式
英文:

Behavior for enabling/disabling a WPF ComboBox depending on number of items

问题

每当 WPF ComboBox 的 ItemsSource 更改(例如添加或删除元素,或分配新的集合),我希望它执行以下操作(伪代码):

IsEnabled = Items.Count > 1;

if (Items.Count == 1)
    SelectedItem = Items.First();

我正在使用 MVVM,并且知道如何在 MVVM 中实现它。但我想避免模板代码,而且在我看来,这对于 ViewModel 来说太依赖于 UI。所以是否有办法在 Behavior<ComboBox> 中指定这个行为?

我找不到可以订阅的 ComboBox 事件。如果可能的话,它应该适用于 ObservableCollection 以及 ICollectionView 或 List 等。

英文:

Whenever the ItemsSource of a WPF ComboBox changes (i.e. adding or removing elements, or assigning a new Collection), I want it to do the following (pseudo code):

IsEnabled = Items.Count &gt; 1;

if (Items.Count == 1)
    SelectedItem = Items.First();

I'm using MVVM, and I know how to do it in MVVM. But I want to avoid the boilerplate code, and IMHO it is too UI-specific for the ViewModel to care about. So is there a way to specify this in a Behavior&lt;ComboBox&gt;?

I couldn't find a ComboBox event I could subscribe to. If possible, it should work on ObservableCollection as well as ICollectionView or List etc.

答案1

得分: 2

您可以使用触发器来定义一个名为customComboBox的样式:

<Style x:Key="customComboBox" TargetType="ComboBox">
    <Style.Triggers>
        <Trigger Property="HasItems" Value="False">
            <Setter Property="IsEnabled" Value="False" />
        </Trigger>
        <DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="1">
            <Setter Property="SelectedIndex" Value="0" />
        </DataTrigger>
    </Style.Triggers>
</Style>

例如,如果您将这个样式放在您的App.xaml文件中,您可以在整个应用程序中使用它,例如:

<ComboBox ItemsSource="{Binding SomeCollection}" Style="{StaticResource customComboBox}" />
英文:

You could use a Style with triggers:

&lt;Style x:Key=&quot;customComboBox&quot; TargetType=&quot;ComboBox&quot;&gt;
    &lt;Style.Triggers&gt;
        &lt;Trigger Property=&quot;HasItems&quot; Value=&quot;False&quot;&gt;
            &lt;Setter Property=&quot;IsEnabled&quot; Value=&quot;False&quot; /&gt;
        &lt;/Trigger&gt;
        &lt;DataTrigger Binding=&quot;{Binding Items.Count, RelativeSource={RelativeSource Self}}&quot;
                     Value=&quot;1&quot;&gt;
            &lt;Setter Property=&quot;SelectedIndex&quot; Value=&quot;0&quot; /&gt;
        &lt;/DataTrigger&gt;
    &lt;/Style.Triggers&gt;
&lt;/Style&gt;

If you for example put this in your App.xaml file, you can use it across the whole application, e.g.:

&lt;ComboBox ItemsSource=&quot;{Binding SomeCollection}&quot; Style=&quot;{StaticResource customComboBox}&quot; /&gt;

答案2

得分: 1

你可以通过监视 ItemsSource 依赖属性的变化以及监视源集合实现了 INotifyCollectionChanged 的情况下,通过 CollectionChanged 事件来捕获源集合的变化。

using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using Microsoft.Xaml.Behaviors;

public class ItemsControlBehavior : Behavior&lt;ItemsControl&gt;
{
    protected override void OnAttached()
    {
        base.OnAttached();

        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
        dpd?.AddValueChanged(AssociatedObject, OnItemsSourceChanged);

        AddSourceCollection();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
        dpd?.RemoveValueChanged(AssociatedObject, OnItemsSourceChanged);

        RemoveSourceCollection();
    }

    private void OnItemsSourceChanged(object? sender, EventArgs e)
    {
        RemoveSourceCollection();
        AddSourceCollection();
    }

    private INotifyCollectionChanged? _collection;

    private void AddSourceCollection()
    {
        _collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
        if (_collection is not null)
        {
            _collection.CollectionChanged += OnItemsSourceCollectionChanged;
        }
        Apply();
    }

    private void RemoveSourceCollection()
    {
        if (_collection is not null)
        {
            _collection.CollectionChanged -= OnItemsSourceCollectionChanged;
        }
        _collection = null;
    }

    private void OnItemsSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        Apply();
    }

    private void Apply()
    {
        AssociatedObject.IsEnabled = _collection is ICollection { Count: &gt; 1 };

        if (AssociatedObject is Selector selector)
        {
            selector.SelectedIndex = 0;
        }
    }
}
英文:

You can capture the change of the source collection from the change of ItemsSource DependencyProperty and the change of the source collection items from CollectionChanged events provided that the source collection implements INotifyCollectionChanged.

using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using Microsoft.Xaml.Behaviors;

public class ItemsControlBehavior : Behavior&lt;ItemsControl&gt;
{
    protected override void OnAttached()
    {
        base.OnAttached();

        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
        dpd?.AddValueChanged(AssociatedObject, OnItemsSourceChanged);

        AddSourceCollection();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
        dpd?.RemoveValueChanged(AssociatedObject, OnItemsSourceChanged);

        RemoveSourceCollection();
    }

    private void OnItemsSourceChanged(object? sender, EventArgs e)
    {
        RemoveSourceCollection();
        AddSourceCollection();
    }

    private INotifyCollectionChanged? _collection;

    private void AddSourceCollection()
    {
        _collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
        if (_collection is not null)
        {
            _collection.CollectionChanged += OnItemsSourceCollectionChanged;
        }
        Apply();
    }

    private void RemoveSourceCollection()
    {
        if (_collection is not null)
        {
            _collection.CollectionChanged -= OnItemsSourceCollectionChanged;
        }
        _collection = null;
    }

    private void OnItemsSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        Apply();
    }

    private void Apply()
    {
        AssociatedObject.IsEnabled = _collection is ICollection { Count: &gt; 1 };

        if (AssociatedObject is Selector selector)
        {
            selector.SelectedIndex = 0;
        }
    }
}

huangapple
  • 本文由 发表于 2023年7月7日 02:46:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76631729.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定