2009-11-06 1 views
3

Есть ли способ рассказать, как событие ComboBox_SelectionChanged было создано в WPF.WPF - как сказать, что повысило событие ComboBox_SelectionChanged

То есть, было ли событие возбуждено в результате взаимодействия пользователя или в результате изменения свойства, к которому оно привязано?

+0

Комментарий к парню, который был только что удален его ответ: Вы, наверное, догадались, что отправитель * всегда * выпадающий, но я думал, что вы хотели бы знать ваше тело метод может быть упрощено, если (отправитель это ComboBox), тогда {blah} else {blah} –

ответ

4

В событии ComboBox.SelectionChanged отправитель всегда ComboBox, и в SelectionChangedEventArgs нет ничего, что поможет вам.

Для меня это два решения. Вы можете использовать конвертер на привязке, или вы можете проверить трассировку стека, чтобы увидеть, находится ли в стеке System.Windows.Controls.Primitives.Selector.OnSelectedItemsCollectionChanged (объект, NotifyCollectionChangedArgs). Проверка стека крайне уродливая, плохая практика и не будет работать в среде с частичным доверием. Поэтому я опишу только один.

Использование нейтрализатора связывания для обнаружения источников изменения

Это решение является относительно чистым, но требует изменения связывания. Он также иногда уведомляет вас, когда все не изменилось.

Шаг 1: Создайте конвертер, который не делает преобразования, но имеет «преобразованное» событие и «ConvertedBack» событие:

public EventingConverter : IValueConverter 
{ 
    public event EventHandler Converted; 
    public event EventHandler ConvertedBack; 

    public object Convert(object value, ...) 
    { 
    if(Converted!=null) Converted(this, EventArgs.Empty); 
    return value; 
    } 
    public object ConvertBack(object value, ...) 
    { 
    if(ConvertedBack!=null) ConvertedBack(this, EventArgs.Empty); 
    return value; 
    } 
} 

Шаг 2: Установите ваши привязки использовать новый экземпляр этого преобразователя (не разделяют конвертер экземпляров с помощью словаря ресурсов или статическое свойство, как это обычно делается)

<ComboBox ...> 
    <ComboBox.SelectedValue> 
    <Binding Path="..." ...> 
     <Binding.Converter> 
     <local:EventingConverter 
      Converted="ComboBoxSelectedValue_Converted" 
      ConvertedBack="ComboBoxSelectedValue_ConvertedBack" /> 
     </Binding.Converter> 
    </Binding> 
    </ComboBox.SelectedValue> 
</ComboBox> 

Теперь ваши методы ComboBoxSelectedValue_Converted и ComboBoxSelectedValue_ConvertedBack будут вызываться внутри процесса связывания.

Предупреждение. Если вы сделаете исключение в этих событиях, вы нарушите привязку.

Если вы не можете изменить XAML, что делает привязку

Если вы не имеете никакого контроля над XAML, который создает привязку (например, вы используете вложенные свойства) вы можете прийти и добавить конвертер после факта. В этом случае вашему классу конвертера необходимо будет привязать к ранее объявленному конвертеру, вам придется клонировать привязку и устанавливать новую (они неизменяемы после их использования), и вам также придется иметь дело с MultiBindings (если вы хотите их поддержать).

Конечная нота

необходимость определить, было ли сделано изменение пользователем или свойство на самом деле может быть симптомом плохого дизайна пользовательского интерфейса, как правило, в результате чего от пользователей, которые на самом деле не понимают их собственные требования.

У меня было несколько проектов, над которыми я работал, когда конечный пользователь указал, что такое-то и должно было произойти «когда я меняю этот ComboBox».Практически в каждом случае оказалось, что приложение будет вести себя неожиданно в некоторых случаях использования, и мы нашли лучший способ достичь цели. Во многих случаях то, что действительно хотел пользователь, было «когда это значение сначала отличается от значения в базе данных» или «когда это значение больше не является значением по умолчанию» или «когда это значение равно 5».

+0

Красиво ответил. Большое вам спасибо за то, что нашли время, чтобы собрать это вместе. – mattdlong

+0

Отличный ответ :) – slugster

2

Короткий ответ: нет. Не должно быть разницы, в обоих случаях выбор изменился, это все, что имеет значение. Чтобы определить, было ли это взаимодействие с пользователем, вам нужно будет отслеживать комбинацию других событий, таких как DropDownOpened/Closed и KeyDown/Up, и Stylus *.

+0

Отправитель всегда является ComboBox, потому что это логический элемент управления, запускающий событие. –

+0

+1 не знаю, почему кто-то отказался от этого ответа. Это было правильно и содержало полезную информацию, хотя это не решило первоначальный вопрос. Дрю прав, что отправителем всегда является ComboBox, чей выбор был изменен. –

1

Я также встретил эту проблему и решил ее использовать переменную boolean bInternalChange.

Представьте интерфейс, преобразующий ° C в ° F и обратно с двумя ComboBoxes. При выборе значения в первом обновляется выбор второго и выбор значения во втором обновляется первым. Он создает бесконечный цикл, если вы не отличаете изменения пользовательского интерфейса от внутренних изменений.

bool bInternalChange = false; 
private void ComboBoxF_SelectionChanged(...) 
{ 
    if (!bInternalChange) 
    { 
     bInternalChange = true; 
     ComboBoxC.SelectedValue = ConvertFtoC(...); 
     bInternalChange = false; 
    } 
} 
private void ComboBoxC_SelectionChanged(...) 
{ 
    if (!bInternalChange) 
    { 
     bInternalChange = true; 
     ComboBoxF.SelectedValue = ConvertCtoF(...); 
     bInternalChange = false; 
    } 
} 
+0

Я тоже использовал это решение. Это более надежно, если вы используете try..finally для bInternalChange, иначе исключение в любом месте кода преобразования или обновление значения приведет к тому, что ваша синхронизация перестанет работать. Я часто инкапсулирую это в объект, например: using (var change = ChangeTracker.StartChange()) if (change) ComboBoxC.SelectedValue = ...; StartChange() возвращает IDisposable, который неявно конвертируется в bool. –

Смежные вопросы