Я должен не согласиться с Шериданом - разработчику, конечно, разрешено устанавливать свойство SelectedIndex
. Однако невозможно достичь поставленной цели только в XAML, т. е. используя стили, шаблоны элементов управления и триггеры. Это связано с тем, что свойства зависимостей имеют определенный приоритет значения, например. локально установленные значения (с помощью привязки или самого элемента управления) имеют более высокий приоритет, чем значения, полученные из стиля WPF (поэтому ваш сеттер работает только один раз). Подробнее об этом можно узнать на странице http://msdn.microsoft.com/en-us/library/ms743230(v=vs.110).aspx
Однако, помимо возможности управлять выбранным индексом из модели представления (как предложил Шеридан), есть также возможность создать, например. прикрепленное свойство зависимости, которое установлено в поле со списком и которое ищет изменения в свойстве 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
.
Другой способ добиться этого — создать поведение WPF, которое является частью Blend SDK. Вы можете найти учебник здесь: http://wpftutorial.net/Behaviors.html. В основном это тот же шаблон, что и с прикрепленными свойствами.
Если вы используете MVVM, я определенно рекомендую вам использовать решение Шеридана, но если нет, мое решение может быть более подходящим.
Если у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь спрашивать.
person
feO2x
schedule
04.09.2014