可编辑的WPF组合框

57

我有一个ComboBox,ComboBox.IsEditable属性被设置为True,以使ComboBox同时充当TextBox和下拉列表。但是,当ComboBox进行数据绑定时,输入自定义文本将不会导致将新项添加到数据绑定集合中。

例如,如果我在绑定到人员列表的ComboBox中输入“Joe”,而该列表不包含值“Joe”,则该值不会自动添加到下拉列表中。

如何最好地处理这个问题?

4个回答

94

这是一种基本的符合MVVM标准并实现所需行为的方法:

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <ComboBox Margin="30,5,30,5"
                  IsEditable="True"
                  ItemsSource="{Binding Items}"
                  SelectedItem="{Binding SelectedItem}"
                  Text="{Binding NewItem, UpdateSourceTrigger=LostFocus}"/>
        <TextBox Margin="30,5,30,5" />
    </StackPanel>
</Window>

MainWindow.cs

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private string _selectedItem;

    private ObservableCollection<string> _items = new ObservableCollection<string>()
    {
        "One",
        "Two",
        "Three",
        "Four",
        "Five",
    };

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public IEnumerable Items
    {
        get { return _items; }
    }

    public string SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            OnPropertyChanged("SelectedItem");
        }
    }

    public string NewItem
    {
        set
        {
            if (SelectedItem != null)
            {
                return;
            }
            if (!string.IsNullOrEmpty(value))
            {
                _items.Add(value);
                SelectedItem = value;
            }
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

由于我将

我改变了更新模式,因为默认的更新模式是Propertychanged,这将为每个按键添加一个新项。

例如,如果您输入文本“Window”,则会将以下内容添加到您的集合中:

W
Wi
Win
Wind
Windo
Window

12
我需要在NewItem属性中添加get { return SelectedItem; } 才能使它正常工作。 - Papa Mufflon
我尝试了这种方法,它有时能正常工作但并非总是如此。在SelectedItem和Text属性上都有绑定,不确定哪个先触发。似乎如果Text绑定先触发,则一切都好,但如果是Selected Item则会将文本更改回来。 - Istvan Heckl

10

我会在 LostFocus 事件中处理它。

在这里,您可以检查 SelectedItem 是否为 null。如果是,则将 Text 的值添加到绑定列表中,并将 SelectedItem 设置为新项目。

在 XAML 中:

  <ComboBox Name="_list" LostFocus="LostFocus" IsEditable="True"/>

在代码后台:

    private ObservableCollection<string> _names;
    public MainWindow()
    {
        InitializeComponent();
        _names = new ObservableCollection<string> {"Eric", "Phillip"};
        _list.SetBinding(ItemsControl.ItemsSourceProperty, new Binding {Source = _names});
    }

    private void LostFocus(object sender, RoutedEventArgs e)
    {
        var comboBox = (ComboBox) sender;
        if(comboBox.SelectedItem != null)
            return;
        var newItem = comboBox.Text;
        _names.Add(newItem);
        comboBox.SelectedItem = newItem;
    }
希望这可以帮到你 :)

您可能还希望在DropDownOpened事件中执行此操作。 - Taudris
你还应该处理PreviewKeyDown事件并处理Key.Enter。 - Paul Baxter
我尝试了这个解决方案,但我也不得不使用SelectedItem绑定。 这引起了一个问题,无法确定SelectedItem绑定是首先运行还是LostFocus。 如果是LostFocus,那么就没问题,如果是SelectedItem,则会重置我刚刚写入ComboBox的文本。 - Istvan Heckl

3
我的建议是使用MVVM方法,并将ComboBox.Text绑定到ViewModel的某个TextProperty上。(也可以通过在视图中添加字符串属性来实现相同的效果) 然后,您可以在此属性的setter中处理输入并将新项添加到列表中,无论以哪种方式在视图中“提交”它。 据我所知,没有开箱即用的机制可将新项添加到数据源中,您仍需自行进行项生成。 另外,您还可以将ComboBox的SelectedItem和Text都绑定起来,以避免查找用户已输入的已知项。但这部分内容可能不太重要,与回答问题无关。

0
我基于Gale先生的回答进行了编程。它使用了一个浮点数列表而不是字符串,并检查输入的值是否已经添加到列表中。
private float zoomFactor = 2;
public float ZoomFactor
{
     get { return zoomFactor; }
     set
     {
         SetProperty(ref zoomFactor, value);
     }
 public float NewZoom
    {
        get { return zoomFactor; }
        set
        {
            if (!zoomValues.Contains(value))
                zoomValues.Add(value);
            ZoomFactor = value;
        }
    }
    
    private ObservableCollection<float> zoomValues = new ObservableCollection<float>()
    {
        1, 2, 3, 4
    };
    public ObservableCollection<float> ZoomValues { get => zoomValues; }

以及XAML代码:

                <ComboBox
                  IsEditable="True"
                  ItemsSource="{Binding ZoomValues}"
                  SelectedItem="{Binding ZoomFactor}"
                  Text="{Binding NewZoom, UpdateSourceTrigger=LostFocus}"/>

我尝试了这种方法,它有时有效,有时无效。在SelectedItem和Text属性上都有绑定,不确定哪个先触发。似乎如果Text绑定先触发,那么一切都没问题,但如果是SelectedItem,则会将文本更改回来。 - Istvan Heckl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接