Зачем помещать неактивный конвертер в привязку, чтобы изменить его поведение? - PullRequest
7 голосов
/ 18 февраля 2010

Я тестирую созданный мной пользовательский элемент управления и сталкиваюсь с чем-то необъяснимым для меня.

Элемент управления является расширением ComboBox, которое обрабатывает значения определенного пользовательского типа. У него есть свойство зависимости этого пользовательского типа, которое является целевым свойством Binding.

У меня есть оператор трассировки в установщике, и я вижу, что свойство устанавливается. Но он не отображается в моем пользовательском элементе управления.

Теперь, как правило, я бы сказал, хорошо, у меня есть ошибка в моем пользовательском элементе управления. Я, вероятно, знаю, хотя я озадачен этим. Но этот вопрос не о том, чтобы найти ошибку в моем контроле. Читать дальше; вот где это становится странным.

Я также использую небольшой конвертер значений Bea Stollnitz для отладки привязки:

public class DebuggingConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value; // Add the breakpoint here!!
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException("This method should never be called");
    }
}

Идея, стоящая за этим, заключается в том, что я добавляю этот конвертер в свою привязку и могу установить точку останова, чтобы увидеть, какое значение выдвигается к цели. Хорошо, это работает просто отлично. Я вижу, что значение выталкивается.

На самом деле, он работает немного тоже отлично. Если DebuggingConverter присоединен к Binding, пользовательский элемент управления отображает значение. Если это не так, то это не так.

Как это вообще возможно? Как преобразователь значений, который ничего не меняет, влияет на поведение привязанного элемента управления?

Edit:

Не то чтобы это могло помочь, но вот XAML для пользовательского элемента управления:

<a:CodeLookupBox
    Grid.Column="1"
    Grid.IsSharedSizeScope="True"
    MinWidth="100"
    Style="{Binding Style}">
    <a:CodeLookupBox.CodeLookupTable>
        <Binding Path="Codes" Mode="OneWay"/>
    </a:CodeLookupBox.CodeLookupTable>
    <a:CodeLookupBox.SelectedCode>
        <Binding Path="Value" Mode="TwoWay" ValidatesOnDataErrors="True"/>
    </a:CodeLookupBox.SelectedCode>
</a:CodeLookupBox>

Без преобразователя на второй привязке управление ведет себя так, как будто я не установил SelectedCode. Несмотря на то, что оператор трассировки в обработчике OnSelectedCodePropertyChanged показывает, что e.Value действительно содержит правильное значение. Это происходит независимо от того, подключен конвертер или нет.

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

Ответы [ 4 ]

2 голосов
/ 26 февраля 2010

Что ж, хорошая новость в том, что я знаю, почему SelectedCode не устанавливается, когда я не использую конвертер значений.Плохая новость в том, что у меня все еще есть что-то загадочное, но проблема немного подтолкнула вверх по пищевой цепочке, и у меня есть обходной путь.из дополнительных функций, которые стали возможными благодаря тому, что он знает, какие элементы в нем.Свойства SelectedCode и CodeLookupTable строго типизированы и скрывают базовые свойства SelectedItem и ItemsSource, которых нет.(Кстати, именно поэтому это пользовательский элемент управления, а не подкласс ComboBox; я не хочу, чтобы эти свойства были видны, потому что много вещей может произойти, если они установлены неправильно, ни одно из них не является хорошим.)

Вот что происходит.Это мой отладочный вывод, когда конвертер значений подключен (число - это хэш-код элемента управления, потому что у меня есть куча из них, которые все рисуются одновременно при инициализации программы):

14626603: OnCodeLookupTablePropertyChanged
   CodeLookupTable property set to Proceedings.Model.CodeLookupTable
   box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView
14626603: OnSelectedCodePropertyChanged:
   SelectedCode property set to Unlicensed Driver [VC12500(A)]
   box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView

Это ожидаемое поведение.Свойство CodeLookupTable установлено, поэтому установка SelectedCode для одного из элементов в этой коллекции правильно устанавливает SelectedItem для базового ComboBox.

Но без преобразователя значений мы получаем это:

16143157: OnSelectedCodePropertyChanged:
   SelectedCode property set to Unlicensed Driver [VC12500(A)]
   box.MainComboBox.ItemsSource = 
16143157: OnCodeLookupTablePropertyChanged
   CodeLookupTable property set to Proceedings.Model.CodeLookupTable
   box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView

Здесь свойство SelectedCode устанавливается до , свойство CodeLookupTable имеет значение.Поэтому, когда метод пытается установить SelectedItem на базовый ComboBox, ничего не происходит, потому что ItemsSource равен нулю.

А вот корень проблемы.Я по глупости предположил, что порядок, в котором привязки обновляют свою цель, совпадает с порядком, в котором они объявлены в XAML.(Одна из причин, по которой я выразил привязки в виде элементов вместо атрибутов, заключается в том, что порядок элементов в документе XML является детерминированным, а порядок атрибутов - нет. Не то чтобы я об этом не думал.)видимо не тот случай.

Я также предположил, может быть, немного менее глупо, что порядок, в котором привязки обновляют свою цель, не зависит от того, подключены ли к ним преобразователи значений.Ну, это так.Интересно, от чего еще это зависит?

К счастью, у меня есть способ обойти это.Поскольку мой CodeLookup объект содержит ссылку на CodeLookupTable, я могу сделать так, чтобы установщик SelectedCode установил свойство CodeLookupTable (и, следовательно, ItemsSource) в первую очередь, если оно еще не было установлено.Это позволит решить эту проблему, не привязывая к привязке фальшивый преобразователь значений, и надеюсь, что поведение привязок никогда не изменится.

Редактировать

Вот чтообъявления собственности выглядят так:

#region SelectedCode

public static readonly DependencyProperty SelectedCodeProperty = DependencyProperty.Register(
    "SelectedCode", typeof(CodeLookup), typeof(CodeLookupBox),
    new FrameworkPropertyMetadata(OnSelectedCodePropertyChanged));

private static void OnSelectedCodePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
    CodeLookupBox box = (CodeLookupBox)source;
    CodeLookup code = e.NewValue as CodeLookup;
    // this right here is the fix to the original problem:
    if (box.CodeLookupTable == null && code != null)
    {
        box.CodeLookupTable = code.Table;
    }
    box.MainComboBox.SelectedItem = e.NewValue;
}

public CodeLookup SelectedCode
{
    get { return GetValue(SelectedCodeProperty) as CodeLookup; }
    set { SetValue(SelectedCodeProperty, value); }
}

#endregion

#region CodeLookupTable

public static readonly DependencyProperty CodeLookupTableProperty = DependencyProperty.Register(
    "CodeLookupTable", typeof(CodeLookupTable), typeof(CodeLookupBox),
    new FrameworkPropertyMetadata(OnCodeLookupTablePropertyChanged));

private static void OnCodeLookupTablePropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
    CodeLookupBox box = (CodeLookupBox)source;
    CodeLookupTable table = (CodeLookupTable)e.NewValue;

    box.ViewSource = new CollectionViewSource { Source = table.Codes };
    box.View = box.ViewSource.View;
    box.MainComboBox.ItemsSource = box.View;

}

public CodeLookupTable CodeLookupTable
{
    get { return GetValue(CodeLookupTableProperty) as CodeLookupTable; }
    set { SetValue(CodeLookupTableProperty, value); }
}

#endregion
0 голосов
/ 19 февраля 2010

Хм ... очень странно. Я видел похожее поведение при работе с MultiBindings с несколькими конвертерами, но не с простой привязкой. Из любопытства, как вы определяете свои DP на контроле? (включая обратные вызовы, параметры метаданных и т. д.)

Некоторые вещи, которые нужно попробовать (после удаления конвертера):

  • Режим по умолчанию (т.е. удалить TwoWay)
  • Удалить ошибки ValidatesOnDataErrors
  • Добавление AffectsRender к выбранному коду DP
0 голосов
/ 26 февраля 2010

Что делает настройка SelectedCode? Можете ли вы опубликовать код обработчика измененного свойства?

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

0 голосов
/ 19 февраля 2010

попробуйте также реализовать функцию ConvertBack, вы используете двустороннее связывание, поэтому проблема может заключаться в том, что исключение будет игнорироваться "xaml", но при переходе в режим отладки вы можете остановить "отправку значения обратно" -operation

...