简体   繁体   English

如何使用ComboBox的Text属性禁用ItemsSource同步

[英]How to disable ItemsSource synchronization with Text property of ComboBox

I use the ComboBox for binding to string property of view model. 我使用ComboBox绑定到视图模型的字符串属性。 I choose the ComboBox instead of TextBox, because i want to have an option to choose from the list (as a suggestion), but I don't want to change the selected text if the ItemsSource changes. 我选择ComboBox而不是TextBox,因为我希望有一个选项可以从列表中选择(作为建议),但如果ItemsSource更改,我不想更改所选文本。

I tried to set the IsSynchronizedWithCurrentItem property to false, but when the list of suggestions change (at the position of the selected text), the Text changes to empty. 我尝试将IsSynchronizedWithCurrentItem属性设置为false,但是当建议列表更改时(在所选文本的位置),文本将更改为空。 It seems that the ComboBox has remembered that the entered text was also in the list and when this item disappears, the Text property is also cleared. 似乎ComboBox记得输入的文本也在列表中,当此项消失时,Text属性也被清除。

So my question is: Is that a bug, or am I doing something wrong? 所以我的问题是:这是一个错误,还是我做错了什么? If it is a bug, could you suggest some work around? 如果它是一个bug,你能建议一些解决方法吗?

I created a sample project which preproduces this: 我创建了一个示例项目,它预先生成了这个:

in XAML: 在XAML中:

<Window x:Class="TestProject1.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">
    <Grid>
        <ComboBox IsSynchronizedWithCurrentItem="False" ItemsSource="{Binding Items}"
                  IsEditable="True" Text="{Binding SelectedText, UpdateSourceTrigger=PropertyChanged}"
                  HorizontalAlignment="Left" Margin="10,39,0,0" VerticalAlignment="Top" Width="120"/>
        <Button Click="Button_Click" Content="Update list" 
                HorizontalAlignment="Left" Margin="10,82,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>

In code behind: 代码背后:

public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow() {
            InitializeComponent();

            this.DataContext = this;
            Items = new List<string>() { "0", "1", "2" };
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyName) {
            if (PropertyChanged != null) {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private List<string> _items;
        public List<string> Items {// I use IEnumerable<string> with LINQ, but the effect is the same
            get { return _items; }
            set {
                if (_items != value) {
                    _items = value;
                    RaisePropertyChanged("Items");
                }
            }
        }

        private string _selectedText;
        public string SelectedText {
            get { return _selectedText; }
            set {
                if (_selectedText != value) {
                    _selectedText = value;
                    RaisePropertyChanged("SelectedText");
                }
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e) {
            var changed = Items.ToList();//clone
            int index = changed.IndexOf(SelectedText);
            if (index >= 0) {
                changed[index] += "a";//just change the currently selected value
            }
            Items = changed;//update with new list
        }

    }

This is my fix for that issue: 这是我对该问题的修复:

public class ComboBox : System.Windows.Controls.ComboBox
{
    private bool ignore = false;
    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        if (!ignore)
        {
            base.OnSelectionChanged(e);
        }
    }

    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        ignore = true;
        try
        {
            base.OnItemsChanged(e);
        }
        finally
        {
            ignore = false;
        }
    }
}

After your ItemsSource has changed, raise a property changed on your selected text to refresh the UI. ItemsSource更改后,在所选文本上引发更改的属性以刷新UI。

So in your Items collection setter, make the change: 因此,在Items集合setter中,进行更改:

RaisePropertyChanged("Items");
RaisePropertyChanged("SelectedText");

EDIT: in your example you aren't just changing the ItemSource, you are changing the text of the item that is the currently selected one but having a text binding on the old text. 编辑:在您的示例中,您不仅仅是更改ItemSource,而是更改当前所选项目的文本,但对旧文本具有文本绑定。 What are you expecting to see/happen? 你期待看到/发生什么? Are you wanting the selected item to stay the same, even if its text changes? 您是否希望所选项目保持不变,即使其文本发生变化?

Modify Button_Click like this (commented lines are new): 像这样修改Button_Click (注释行是新的):

private void Button_Click(object sender, RoutedEventArgs e)
{
    string tempCopy = SelectedText; // Create a copy of the current value

    var changed = Items.ToList();
    int index = changed.IndexOf(SelectedText);
    if (index >= 0)
    {
        changed[index] += "a";
    }
    Items = changed;

    SelectedText = tempCopy; // Replace the selected text with the copy we made
}

All this does is makes a copy of SelectedText before Items changes, and then replaces it once the change has been made. 所有这一切都是在Items更改之前复制SelectedText ,然后在更改完成后替换它。

  1. Copy SelectedText 复制SelectedText
  2. Modify items source 修改项目来源
  3. Replace SelectedText with the copy SelectedText替换为副本

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM