【问题标题】:Editable ComboBox with selectedvalue(path) from itemsource OR user input具有来自 itemsource 或用户输入的选定值(路径)的可编辑组合框
【发布时间】:2021-02-05 12:41:00
【问题描述】:

嘿,我正在尝试创建一个组合框,允许用户从列表中选择一个项目(在列表中显示为 fx。“2324 - James - 21”),将该项目的属性放入文本框中(fx ."James"),还允许用户输入该属性。 (fx。“杰克”)

What I've got so far is a combobox that allows for user input, but when an item is selected, instead of the property (As specified in SelectedValuePath), the items ToString is put in组合框文本字段,而不仅仅是名称。 任何想法如何解决它?这是我现在的设置:

人物类:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return $"{Id} - {Name} - {Age}";
    }
}

XAML:

<ComboBox Text="{Binding PersonName, UpdateSourceTrigger=PropertyChanged}" 
ItemsSource="{Binding People, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
SelectedValue="{Binding PersonName, UpdateSourceTrigger=PropertyChanged}" 
IsEditable="True" SelectedValuePath="Name" /> 

编辑:

People 是 ObservableCollection,PersonName 是绑定到字段 personName 和 OnPropertyChanged() 的字符串;在其 set 方法中调用。 ViewModel 从实现 INotifyPropertychanged 的​​ ObservableObject 类继承。

编辑 2:

好的,所以我一直在尝试这个:

<ComboBox Text="{Binding PersonName, UpdateSourceTrigger=PropertyChanged}" 
ItemsSource="{Binding People, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
SelectedItem="{Binding Person, UpdateSourceTrigger=PropertyChanged}" 
IsEditable="True" /> 

视图模型中的 Aaand:

public Person SelectedPerson
{
    get => selectedPerson;
    set
    {
        selectedPerson = value;
        OnPropertyChanged();
        if (SelectedPerson != null) PersonName = SelectedPerson.Name;
    }
}

但是,当我从列表中选择一个人时,组合框文本字段设置为 Persons ToString 值,而不是 SelectedPerson.Name... 或者更确切地说;它可能首先设置为 SelectedPerson.Name,然后由 ComboBox 自动设置为 SelectedPerson.ToString() :/

编辑 3:

好吧,我一直试图中断文本更改,当它以一种非常糟糕的方式来自 SelectionChanged 时,但我不知道如何“取消”文本更改。到目前为止,这是我的代码,但据我所知,TextChanged 似乎不允许恢复文本,并且 PreviewTextInput 仅在表单用户输入更改时触发:/。

Useless code removed. Almost identical to edit 4, but without the "oldtext" field.

编辑 4:

我以一种可怕的方式解决了它。它有效,但我真的希望有更好的方法。也许你们中的一个人有更好的解决方案? :) 无论如何,这是我的解决方案:

在组合框强制更改文本后恢复文本的依赖属性:

public class ComboBoxBehaviour
{
    private static bool overrideTextChange;
    private static string oldText;

    public static bool GetDisconnectTextFromSelectedItem(ComboBox comboBox)
    {
        return (bool)comboBox.GetValue(DisconnectTextFromSelectedItemProperty);
    }
    public static void SetDisconnectTextFromSelectedItem(ComboBox comboBox, bool value)
    {
        comboBox.SetValue(DisconnectTextFromSelectedItemProperty, value);
    }

    public static readonly DependencyProperty DisconnectTextFromSelectedItemProperty =
        DependencyProperty.RegisterAttached(
            "DisconnectTextFromSelectedItem",
            typeof(bool),
            typeof(ComboBoxBehaviour),
            new UIPropertyMetadata(false, OnDisconnectTextFromSelectedItemChanged));

    private static void OnDisconnectTextFromSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var comboBox = d as ComboBox;
        if (comboBox == null) return;

        if (e.NewValue is bool == false) return;

        if ((bool)e.NewValue)
        {
            comboBox.SelectionChanged += HandleSelection;
            comboBox.Loaded += ComboBoxOnLoaded;
        }
        else
        {
            comboBox.SelectionChanged -= HandleSelection;
            comboBox.Loaded -= ComboBoxOnLoaded;
        }
    }
    private static void ComboBoxOnLoaded(object sender, RoutedEventArgs e)
    {
        ComboBox comboBox = (ComboBox)e.Source;
        TextBox txtBox = (TextBox)comboBox.Template.FindName("PART_EditableTextBox", comboBox);
        txtBox.TextChanged += TxtBoxOnTextChanged;
    }
    private static void TxtBoxOnTextChanged(object sender, TextChangedEventArgs e)
    {
        if (overrideTextChange)
        {
            ((TextBox) e.Source).Text = oldText;
            overrideTextChange = false;
            e.Handled = true;
        }
    }
    private static void HandleSelection(object sender, RoutedEventArgs e)
    {
        ComboBox comboBox = (ComboBox)e.Source;
        TextBox txtBox = (TextBox)comboBox.Template.FindName("PART_EditableTextBox", comboBox);
        oldText = txtBox.Text;
        overrideTextChange = true;
        e.Handled = true;
    }
}

Xaml 组合框:

<ComboBox Text="{Binding PersonName, UpdateSourceTrigger=PropertyChanged}" 
ItemsSource="{Binding People, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
SelectedItem="{Binding Person, UpdateSourceTrigger=PropertyChanged}" 
IsEditable="True" helpers:ComboBoxBehaviour.DisconnectTextFromSelectedItem="True" /> 

编辑 5: 关闭,但没有雪茄。原来是打电话

((TextBox) e.Source).Text = oldText;

不会更新我的组合框文本绑定,因此它显示了正确的文本,但视图模型中的字符串是错误的 - 它是 ToString 文本,而不是短文本。那么,当我无权访问文本更改上下文中的组合框时,如何强制刷新组合框绑定?更多“黑客”?嗯……

编辑 6: 好的,我通过将 TxtBoxOnTextChanged 更改为此来工作;

private static void TxtBoxOnTextChanged(object sender, TextChangedEventArgs e)
{
    if (overrideTextChange) //The combo box changed the text of the textbox.
    {
        TextBox textBox = (TextBox) e.Source; //The textbox
        ComboBox comboBox = (ComboBox)textBox.TemplatedParent; //The combobox.
        comboBox.Text = oldText; //Set the text of the combobox to update the property on the viewmodel.
        textBox.Text = oldText; //Set the text of the textbox manually, because setting the combobox text doesn't change it for some reason.
        overrideTextChange = false;
        e.Handled = true; //Done!
    }
}

仍然希望有人有更好的解决方案,但它确实有效!

【问题讨论】:

  • 如果你删除Text="{Binding PersonName, UpdateSourceTrigger=PropertyChanged}" 会发生什么?
  • @mm8 如果我删除文本绑定,它仍会在组合框中显示 ToString 值,但 PersonName 已正确设置为 Name 属性,但如果用户手动输入名称,则不会设置 PersonName。
  • 将 DisplayMemberPath 设置为 Name?
  • @mm8 不能这样做,那么列表中的项目只显示名称 - 我需要列表具有完整的 ToString 值。也不影响用户输入
  • 在弹出的实际列表中我需要显示 ToString,但在手动输入字段中我只需要显示 Name 属性。

标签: c# wpf xaml mvvm combobox


【解决方案1】:

如果要显示Name 属性的值而不是ToString(),则应将DisplayMemberPath 属性设置为“名称”。

我需要在列表中显示 ToString,但在输入字段中显示名称。

那么你应该将PersonName设置为你想在输入字段中显示的string,并且不要将SelectedValue绑定到PersonName

您实际上只能选择源集合中的值。

【讨论】:

  • 我需要在列表中显示 ToString,但在输入字段中显示名称。
  • 输入字段显示当前选择的值,您只能选择ItemsSource集合中的值。
  • 好吧,这很有意义。我首先尝试了类似的东西但没有成功。再次尝试以防万一并更新了我的问题。还是不太对:/
猜你喜欢
  • 1970-01-01
  • 2015-12-13
  • 2017-09-28
  • 2018-11-10
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多