我不得不不同意 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 的解决方案 - 但如果不是,我的解决方案可能更合适。
如果您有任何问题,请随时提出。