【问题标题】:How to disable ItemsSource synchronization with Text property of ComboBox如何使用 ComboBox 的 Text 属性禁用 ItemsSource 同步
【发布时间】:2014-03-06 10:09:46
【问题描述】:

我使用 ComboBox 绑定到视图模型的字符串属性。我选择 ComboBox 而不是 TextBox,因为我希望有一个从列表中选择的选项(作为建议),但如果 ItemsSource 发生更改,我不想更改所选文本。

我尝试将 IsSynchronizedWithCurrentItem 属性设置为 false,但是当建议列表更改时(在所选文本的位置),文本更改为空。 似乎 ComboBox 已经记住了输入的文本也在列表中,当该项消失时,Text 属性也被清除。

所以我的问题是:这是一个错误,还是我做错了什么? 如果这是一个错误,您能建议一些解决方法吗?

我创建了一个示例项目,它预先生成了这个:

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

在后面的代码中:

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
        }

    }

【问题讨论】:

    标签: c# wpf combobox


    【解决方案1】:

    这是我解决这个问题的方法:

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

    【讨论】:

      【解决方案2】:

      在您的 ItemsSource 发生更改后,在您选择的文本上引发一个属性已更改以刷新 UI。

      因此,在您的 Items 集合设置器中进行更改:

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

      编辑:在您的示例中,您不仅更改了 ItemSource,还更改了当前所选项目的文本,但在旧文本上具有文本绑定。你期待看到/发生什么?您是否希望选定的 item 保持不变,即使其文本发生更改?

      【讨论】:

      • 我试过了,没用。我不知道它为什么会起作用?
      • 它应该可以工作,因为当 ItemsSource 更改将清除 SelectedItem 的概念时 - 通过 RaisingPropertyChanged 您可以重新说明所选项目。但是我刚刚注意到您正在尝试更改所选项目的 text ,并以某种方式保留依赖于文本的绑定......这永远不会起作用。我将编辑我的答案。
      • 首先我想从列表中选择文本。然后我不希望在更改项目源后更改所选文本。如果集合(字符串)的类型是引用类型,那么这是预期的行为。但是字符串是值类型,因此选择后应该保持选择时的状态(新副本)。
      • 在 C# 中,字符串是引用类型,而不是值类型。
      • 我认为您使用了错误的控件。 ComboBox 的文本部分是 SelectedItem 的文本表示形式。如果您打算从 ComboBox 中选择某些内容,但随后将其与状态和更改隔离开来,这可能发生在 ComboBox 中,那么您需要检测 SelectedItem 更改并将该文本存储在不同的变量/属性中。
      【解决方案3】:

      像这样修改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
      }
      

      这只是在Items 更改之前复制SelectedText,然后在更改完成后替换它。

      1. 复制SelectedText
      2. 修改项目来源
      3. 用副本替换SelectedText

      【讨论】:

        猜你喜欢
        • 2011-12-06
        • 2012-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多