Почему был бы, не помещая никакой-op Преобразователь на Обязательное изменение его поведение?

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

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

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

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

Я также использую небольшой преобразователь значения 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 присоединен к Привязке, пользовательский элемент управления отображает значение. Если это не, это не делает.

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

Править:

Не то, чтобы это, вероятно, поможет, но здесь является 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 действительно содержит правильное значение. Это происходит независимо от того, присоединил ли преобразователь или нет.

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

7
задан Robert Rossney 18 February 2010 в 07:14
поделиться

4 ответа

Хорошая новость: я знаю, почему 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 равен null.

И здесь кроется корень проблемы. Я по глупости предположил, что порядок, в котором привязки обновляют свою цель, совпадает с порядком, в котором они объявлены в 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
2
ответ дан 7 December 2019 в 18:42
поделиться

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

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

  • Режим по умолчанию (т.е. убрать TwoWay)
  • Убрать ValidatesOnDataErrors
  • Добавить AffectsRender к SelectedCode DP
0
ответ дан 7 December 2019 в 18:42
поделиться

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

0
ответ дан 7 December 2019 в 18:42
поделиться

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

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

0
ответ дан 7 December 2019 в 18:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: