Двустороннее связывание данных с конвертером не обновляет источник - PullRequest
5 голосов
/ 16 июня 2011

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

Вот XAML для сайта использования:

        <local:SampleConverter x:Key="SampleConverter" />
        <Expander Header="Sample" >
            <local:SampleControl 
                Sample="{Binding Path=XmlSource, 
                                 Converter={StaticResource SampleConverter}, 
                                 Mode=TwoWay}" />
        </Expander>

XmlSource - это свойство чтения-записи CLR (не DependencyProperty) родительского объекта, связанного с данными. Это тип .NET, сгенерированный из XSD.

SampleConverter реализует IValueConverter. Метод Convert вызывается и возвращает ненулевые данные, но метод ConvertBack никогда не вызывается.

SampleControl - это UserControl, который инкапсулирует взаимодействие пользовательского интерфейса с деревом данных образца. Это XAML выглядит так:

<UserControl x:Class="SampleControl">
    [... other stuff ...]

    <UserControl.Content>
        <Binding Path="Sample" RelativeSource="{RelativeSource Mode=Self}" Mode="TwoWay" TargetNullValue="{StaticResource EmptySampleText}" />
    </UserControl.Content>

    <UserControl.ContentTemplateSelector>
        <local:BoxedItemTemplateSelector />
    </UserControl.ContentTemplateSelector>
</UserControl>

Свойство Sample является свойством DependencyProperty в коде SampleControl:

public static readonly DependencyProperty SampleProperty =
    DependencyProperty.Register("Sample", typeof(SampleType), typeof(SampleControl), new PropertyMetadata(new PropertyChangedCallback(OnSampleChanged)));

public SampleType Sample
{
    get { return (SampleType)GetValue(SampleProperty); }
    set { SetValue(SampleProperty, value); }
}

private static void OnSampleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != null)
    {
        ((INotifyPropertyChanged)e.NewValue).PropertyChanged += ((SampleControl)d).MyPropertyChanged;
    }
    else if (e.OldValue != null)
    {
        ((INotifyPropertyChanged)e.OldValue).PropertyChanged -= ((SampleControl)d).MyPropertyChanged;
    }
}

private void MyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    ;  // breakpoint here shows change notices are happening
}

Внутренние классы, которые XmlSource преобразует для реализации INotifyPropertyChanged и отправляют уведомления об изменениях вверх по дереву, как указано точкой останова в MyPropertyChanged выше.

Итак, если данные сообщают, что они изменились, почему WPF не вызывает метод ConvertBack моего конвертера?

Ответы [ 3 ]

2 голосов
/ 08 июля 2011

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

private void mycontrol_LostFocus(object sender, RoutedEventArgs e)
{
    if (mycontrol.IsModified)
    {
        var binding = mycontrol.GetBindingExpression(MyControl.SampleProperty);
        binding.UpdateSource();
    }
}
0 голосов
/ 29 января 2013

Была похожая проблема.Решение было следующим:

Вместо двухстороннего связывания я использовал один способ и конвертер.Преобразователь преобразует (инкапсулирует) объект (в вашем случае, родительский объект свойства xmlsource) в viewModel, и элемент управления привязывается к нему.

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

Таким образом, у вас есть чистое представление, вам не нужен код в xaml.cs

0 голосов
/ 16 июня 2011

XmlSource - свойство чтения и записи CLR (не DependencyProperty) родительского связанный с данными объект. Это тип .NET генерируется из XSD.

Я верю, что это твоя проблема. Базовое свойство CLR не уведомляет и, следовательно, не может участвовать в двустороннем связывании данных. Мое предположение, что вы получаете одноразовую привязку? У вас есть ошибки привязки данных в окне вывода?

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

Также, относительно этого комментария:

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

Образцы реализаций INotifyPropertyChanged обычно имеют уведомление, возникающее при изменении самого свойства, а не при изменении дочерних или других свойств. Тем не менее, вы можете в любой момент запустить событие уведомления для любого объекта недвижимости. Например, у вас может быть свойство «Полное имя», которое уведомляет об изменении «Имя» или «Фамилия».

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

Edit:

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

private static void OnSampleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{
    var oldValue = Sample;
    Sample = new SampleType();
    Sample = oldValue;
}

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

...