【问题标题】:Xaml Set SelecteIndex on itemSource updated on comboboxXaml 设置 itemSsource 上的选定索引在组合框上更新
【发布时间】:2014-09-04 07:14:30
【问题描述】:

我希望每次更改组合框的 itemssource 时都设置 SelectedIndex。

到目前为止我有这个:

    <Style TargetType="ComboBox">
        <Setter Property="SelectedIndex"
                Value="0"></Setter>
    </Style>

它设置 selectedIndex,但仅在第一次设置 itemSource 时。当整个 itemSource 改变时,它被加载到组合框中,但不再被选中。此时它应该选择第一项。 每个 ItemSource 都有一个绑定,我不想通过后面的代码设置 SelectedIndex。 我想要一个带有触发器的 xaml 解决方案。

【问题讨论】:

    标签: wpf xaml combobox itemssource selectedindex


    【解决方案1】:

    我不得不不同意 Sheridan - 开发人员当然可以设置 SelectedIndex 属性。但是,仅在 XAML 中不可能实现您的目标,即通过使用样式、控件模板和触发器。这是因为依赖属性具有一定的值优先级,例如本地设置的值(通过绑定或控件本身)比来自 WPF 样式的值具有更高的优先级(这就是为什么您的 Setter 只工作一次)。你可以通过http://msdn.microsoft.com/en-us/library/ms743230(v=vs.110).aspx了解更多信息

    但是,除了可以从视图模型控制所选索引(如 Sheridan 建议的那样)之外,还可以创建例如在组合框上设置的附加依赖属性,用于查找组合框的 ItemsSource 属性的更改。然后,您的 XAML 将看起来像这样:

    <ComboBox x:Name="ComboBox" l:ComboBoxExtensions.InitialIndexOnItemsSourceChanged="0" />
    

    请注意,我将附加的依赖属性ComboBoxExtensions.InitialIndexOnItemsSourceChanged 设置为0,这意味着每次ItemsSource 更改时,SelectedIndex 将设置为0。l: 指的是本地 XML 命名空间(xmlns) 我需要引用以使用我的自定义附加属性。

    我以这种方式实现了依赖属性:

    public class ComboBoxExtensions
    {
        public static readonly DependencyProperty InitialIndexOnItemsSourceChangedProperty;
        private static readonly IDictionary<ComboBox, BindingSpy<ComboBox, IEnumerable>> ComboBoxToBindingSpiesMapping = new Dictionary<ComboBox, BindingSpy<ComboBox, IEnumerable>>();
    
    
        static ComboBoxExtensions()
        {
            InitialIndexOnItemsSourceChangedProperty = DependencyProperty.RegisterAttached("InitialIndexOnItemsSourceChanged",
                                                                                           typeof (int?),
                                                                                           typeof (ComboBoxExtensions),
                                                                                           new FrameworkPropertyMetadata(null, OnInitialIndexOnItemsSourceChanged));
        }
    
        public static void SetInitialIndexOnItemsSourceChanged(ComboBox targetComboBox, int? value)
        {
            if (targetComboBox == null) throw new ArgumentNullException("targetComboBox");
    
            targetComboBox.SetValue(InitialIndexOnItemsSourceChangedProperty, value);
        }
    
        public static int? GetInitialIndexOnItemsSourceChanged(ComboBox targetComboBox)
        {
            if (targetComboBox == null) throw new ArgumentNullException("targetComboBox");
    
            return (int?) targetComboBox.GetValue(InitialIndexOnItemsSourceChangedProperty);
        }
    
        private static void OnInitialIndexOnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var targetComboBox = d as ComboBox;
            if (targetComboBox == null)
                return;
    
            if ((int?) e.NewValue != null)
            {
                SetInitialIndexIfPossible(targetComboBox);
                EstablishBindingSpy(targetComboBox);
                return;
            }
    
            ReleaseBindingSpy(targetComboBox);
        }
    
        private static void EstablishBindingSpy(ComboBox targetComboBox)
        {
            if (ComboBoxToBindingSpiesMapping.ContainsKey(targetComboBox))
                return;
    
            var bindingSpy = new BindingSpy<ComboBox, IEnumerable>(targetComboBox, ItemsControl.ItemsSourceProperty);
            bindingSpy.TargetValueChanged += OnItemsSourceChanged;
            ComboBoxToBindingSpiesMapping.Add(targetComboBox, bindingSpy);
        }
    
        private static void ReleaseBindingSpy(ComboBox targetComboBox)
        {
            if (ComboBoxToBindingSpiesMapping.ContainsKey(targetComboBox) == false)
                return;
    
            var bindingSpy = ComboBoxToBindingSpiesMapping[targetComboBox];
            bindingSpy.ReleaseBinding();
            ComboBoxToBindingSpiesMapping.Remove(targetComboBox);
        }
    
        private static void OnItemsSourceChanged(BindingSpy<ComboBox, IEnumerable> bindingSpy)
        {
            SetInitialIndexIfPossible(bindingSpy.TargetObject);
        }
    
        private static void SetInitialIndexIfPossible(ComboBox targetComboBox)
        {
            var initialIndexOnItemsSourceChanged = GetInitialIndexOnItemsSourceChanged(targetComboBox);
            if (targetComboBox.ItemsSource != null && initialIndexOnItemsSourceChanged.HasValue)
            {
                targetComboBox.SelectedIndex = initialIndexOnItemsSourceChanged.Value;
            }
        }
    }
    

    在这个类中,我定义了前面提到的附加属性。另一个重要部分是OnInitialIndexOnItemsSourceChanged 方法,当在组合框上设置附加属性时将调用该方法。在那里我只是确定我是否必须观察组合框的ItemsSource 属性。

    为了实现对 ItemsSource 的观察,我使用了另一个名为 BindingSpy 的自定义类,它在 ItemsSource 和绑定间谍的自定义依赖属性之间建立了数据绑定,因为这是我可以识别的唯一方法ItemsSource 属性已更改。不幸的是,ComboBox(分别为ItemsControl)没有提供表明源集合已更改的事件。 BindingSpy 实现如下:

    public class BindingSpy<TSource, TValue> : DependencyObject where TSource : DependencyObject
    {
        private readonly TSource _targetObject;
        private readonly DependencyProperty _targetProperty;
    
        public static readonly DependencyProperty TargetValueProperty = DependencyProperty.Register("TargetValue",
                                                                                                    typeof (TValue),
                                                                                                    typeof (BindingSpy<TSource, TValue>),
                                                                                                    new FrameworkPropertyMetadata(null, OnTargetValueChanged));
    
        public BindingSpy(TSource targetObject, DependencyProperty targetProperty)
        {
            if (targetObject == null) throw new ArgumentNullException("targetObject");
            if (targetProperty == null) throw new ArgumentNullException("targetProperty");
            _targetObject = targetObject;
            _targetProperty = targetProperty;
    
            var binding = new Binding
                          {
                              Source = targetObject,
                              Path = new PropertyPath(targetProperty),
                              Mode = BindingMode.OneWay
                          };
            BindingOperations.SetBinding(this, TargetValueProperty, binding);
        }
    
        public TValue TargetValue
        {
            get { return (TValue) GetValue(TargetValueProperty); }
            set { SetValue(TargetValueProperty, value); }
        }
    
        public TSource TargetObject
        {
            get { return _targetObject; }
        }
    
        public DependencyProperty TargetProperty
        {
            get { return _targetProperty; }
        }
    
        public void ReleaseBinding()
        {
            BindingOperations.ClearBinding(this, TargetValueProperty);
        }
    
        private static void OnTargetValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var bindingSpy = d as BindingSpy<TSource, TValue>;
            if (bindingSpy == null)
                return;
    
            if (bindingSpy.TargetValueChanged != null)
                bindingSpy.TargetValueChanged(bindingSpy);
        }
    
        public event Action<BindingSpy<TSource, TValue>> TargetValueChanged;
    }
    

    正如我之前所说,这个类建立一个绑定并通过TargetValueChanged 事件通知客户端。

    实现此目的的另一种方法是创建作为 Blend SDK 一部分的 WPF 行为。你可以在这里找到一个教程:http://wpftutorial.net/Behaviors.html。基本上它与附加属性的模式相同。

    如果您使用 MVVM,我肯定会建议您使用 Sheridan 的解决方案 - 但如果不是,我的解决方案可能更合适。

    如果您有任何问题,请随时提出。

    【讨论】:

      【解决方案2】:

      ComboBox.SelectedIndexComboBox 本身设置,不应由开发人员设置。您应该将 int 属性数据绑定到它,以便您可以判断选择了哪个项目和/或从代码中选择一个项目:

      <ComboBox ItemsSource="{Binding YourItems}" SelectedIndex="{Binding YourIndex}" />
      

      然后在代码中:

      int selectedIndex = YourIndex;
      

      或者选择第一项例如:

      YourIndex = 0;
      

      要了解有关数据绑定的更多信息,请参阅 MSDN 上的Data Binding Overview‎ 页面。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多