2010-03-19 1 views
1

Объем вопроса расширен на 2010-03-25привязка данных к ObservableCollection в другом UserControl - как сохранить текущий выбор?

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

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

Предполагаю, что в этом случае MVVM будет затруднять запоминание последнего выбранного элемента. У меня есть идея, но это кажется немного неприятным. Я награду щедростью, кто придет с хорошим решением для этого!

Вопрос переписан на 2010-03-24

У меня есть два UserControls где один диалог, который имеет TabControl, а другой один, который появляется в упомянутом TabControl. Я просто позвоню им CandyDialog и CandyNameViewer для простоты. Существует также класс управления данными под названием Tracker, который управляет хранением информации, который для всех целей и задач просто предоставляет публичное свойство, которое является ObservableCollection.

отобразилась CandyNameViewer в CandyDialog через код позади, как это:

private void CandyDialog_Loaded(object sender, RoutedEventArgs e) 
{ 
    _candyviewer = new CandyViewer(); 
    _candyviewer.DataContext = _tracker; 
    candy_tab.Content = _candyviewer; 
} 

XAML В CandyViewer выглядит следующим образом (отредактированный для kaxaml):

<Page 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <Page.Resources> 
     <DataTemplate x:Key="CandyItemTemplate"> 
      <Grid> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="120"></ColumnDefinition> 
        <ColumnDefinition Width="150"></ColumnDefinition> 
       </Grid.ColumnDefinitions> 
       <TextBox Grid.Column="0" Text="{Binding CandyName}" Margin="3"></TextBox> 
       <!-- just binding to DataContext ends up using InventoryItem as parent, so we need to get to the UserControl --> 
       <ComboBox Grid.Column="1" SelectedItem="{Binding SelectedCandy, Mode=TwoWay}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CandyNames}" Margin="3"></ComboBox> 
      </Grid> 
     </DataTemplate> 
    </Page.Resources> 

    <Grid> 
     <ListBox DockPanel.Dock="Top" ItemsSource="{Binding CandyBoxContents, Mode=TwoWay}" ItemTemplate="{StaticResource CandyItemTemplate}" /> 
    </Grid> 
</Page> 

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

Проблема у меня в том, что, когда ObservableCollection изменяется от модели, эти изменения не отражение в потребительском UserControl! Раньше у меня никогда не было этой проблемы; все мои предыдущие применения ObservableCollection обновлены отлично, хотя в тех случаях я не привязывался к сборкам. Хотя я в настоящее время только добавляю и удаляю имена конфет в/из ObservableCollection, позднее я, вероятно, также разрешу переименование с модели.

Есть ли что-то, что я сделал не так? Есть ли хороший способ отладить это? Reed Copsey indicates here, что возможна привязка данных между UserControl. К сожалению, мой любимый Bea Stollnitz article on WPF databinding debugging не предлагает ничего, что я мог бы использовать для этой конкретной проблемы.

+0

Не могли бы вы описать изменения, которые производятся для наблюдаемой коллекции? Вы добавляете или удаляете строки или меняете значение существующих строк? Я подозреваю, что это может быть из-за того, что ваши строки не являются DependencyObjects или реализуют INotifyPropertyChanged – Daniel

+0

Я добавляю/удаляю строки. – Dave

ответ

0

Похоже, что ваш объект в ObservableCollection не хочет изменения отображения (или ObservableCollection не хочет изменения ???) Если во-первых, я бы использовал интерфейс INotifyPropertyChanged в моей объектной модели.

+0

INotifyPropertyChanged отлично подходит для свойств, но коллекции требуют INotifyCollectionChanged, а ObservableCollection делает это из коробки уже. – Dave

0

Я не Bea Stolnitz (к сожалению, поскольку она потрясающая), но мне интересно, возможно ли, что через тайны выражения отношений привязки данных в XAML (что НЕ удивительно в малейшей степени, IMO) combobox проигрывает трек того факта, что свойство, к которому оно привязано, является, по сути, ObservableCollection. Возможно, вам захочется попытаться разоблачить ObservableCollection, скажем, ваш datamodel или какой-либо другой слой, который combobox может связывать напрямую (а не через элемент управления вкладкой). Возможно, вам придется использовать немного C# для этого, а не XAML, и это может оскорбить некоторых пуристов MVVM, но многие часы и дни, которые я потратил на устранение таких проблем, вызвали у меня отвращение для XAML и MVVM. На мой взгляд, если я не смогу получить сложный сценарий привязки данных, работающий в течение часа или около того, возможно, пришло время попробовать другой подход.

+0

Спасибо, Кен. Я, должно быть, пропустил эту деталь, но даже несмотря на то, что TabControl «запускает» мой ChildWindow (т. Е. Tabcontrol.tabitem.content = childwindow), я не пытаюсь выполнить привязку к нему данных. Я установил DataContext для ChildWindow специально для объекта, который предоставляет общедоступное свойство ObservableCollection ... – Dave

+0

Вы хотите быть женщиной ??? – Daniel

+0

Я, конечно, очень хочу знать, как привязывать данные, как она делает :-). Однако вряд ли это произойдет в ближайшем будущем. –

0

Ваш XAML меня смущает, но я думаю, что вам нужен TextBox, чтобы связать так: {Binding SelectedCandy.CandyName}

Имея ObservableCollection говорит WPF, когда элементы добавлены или удалены, так что если вы удалите или добавьте CandyNames из модели вы должны увидеть соответствующее обновление combobox. Но если изменить CandyName то WPF требует реализовать INotifyPropertyChanged так, что вы даете связывании головки вверх

WPF, так что это довольно простой (если я понимаю вашу модель)

public class Candy : INotifyPropertyChanged 
{ 
    string _CandyName; 
    public string CandyName 
    { 
    get { return _CandyName;} 
    set { _CandyName = value; OnPropertyChanged("CandyName"); 
    } 

    public event PropertyChangedEventHandler OnPropertyChanged; 

    void OnPropertyChanged(string prop) 
    { 
    if (PropertyChanged != null) PropertyChanged(this,new PropertyChangedEventArgs(prop)); 
    } 
} 
+0

Текстовое поле связывается нормально, потому что я использую ItemTemplate, поэтому подразумевается, что я работаю на выбранные конфеты уже. Мне не нужно менять имена * еще *, но это, скорее всего, произойдет в ближайшее время. Поскольку ObservableCollection содержит только строки, интересно, проще ли заставить сам OC «рассказать» WPF о том, что * коллекция * была изменена. – Dave

0

Ну, теперь я чувствую себя глупо. Мой код плохо продуман, и только сегодня я выяснил, что я сделал неправильно. Я дважды установил DataContext, каждый из которых имел другой объект, и по какой-то причине у одного из них были CandyNames, определенные как List !!! Argh ...

Я думаю, что определенно стоит запланировать привязки и отношения в UML, чтобы было легко отследить этот материал. MVVM + привязки данных наверняка будут запутываться время от времени.

2

У меня была та же проблема, что и ComboBoxes, гасящие при изменении DataContext. У меня есть относительно простое решение, которое использует класс, который я написал, называемый «ComboBoxFixer». После внедрения, вы можете решить эту проблему, просто заменив это:

<ComboBox ItemsSource="..." SelectedItem="..." /> 

с этим:

<ComboBox ItemsSource="..." my:ComboBoxFixer.SelectedItem="..." /> 

Объяснение проблемы

Причина ваши ComboBoxes приходят пустыми, что привязка SelectedItems оценивается, когда ItemsSource не установлен. Это можно устранить, задерживая передачу данных в и из SelectedItems до тех пор, пока все остальные привязки данных не будут завершены.

Как реализовать ComboBoxFixer

Мой ComboBoxFixer класс реализован с использованием общего класса зависимостей свойство синхронизатора я написал.Мой класс DependencyPropertySynchronizer имеет следующий интерфейс:

public class DependencyPropertySynchronizer 
{ 
    public DispatcherPriority Priority { get; set; } 
    public DependencyProperty AutoSyncProperty { get; set; } 

    public DependencyProperty Register(... 
    public DependencyProperty RegisterAttached(... 
    public DependencyPropertyKey RegisterReadOnly(... 
    public DependencyPropertyKey RegisterAttachedReadOnly(... 
} 

И, как правило, используется так:

public SomeClass : DependencyObject 
{ 
    static DependencyPropertySynchronizer sync = 
    new DependencyPropertySynchronizer 
    { 
     Priority = DispatcherPriority.ApplicationIdle 
    }; 

    public static readonly DependencyProperty HappinessProperty = 
    sync.RegisterAttached("Happiness", typeof(int), typeof(SomeClass)); 

    public static readonly DependencyProperty JoyProperty = 
    sync.RegisterAttached("Joy", typeof(int), typeof(SomeClass)); 
} 

Приведенный выше код приведет к тому, прикрепленную счастье и радость свойства любого объекта синхронизироваться: Всякий раз, когда либо Счастье или Радость установлены, другая будет установлена ​​в DispatcherPriority.ApplicationIdle. DependencyPropertySynchronizer реализуется с использованием скрытого прикрепленного свойства, которое сохраняет последнее значение, заданное для любого свойства, и координирует планирование обновлений. Вы также можете синхронизировать с существующим свойством, установив AutoSyncProperty.

Используя этот класс, мой класс ComboBoxFixer очень прост:

public class ComboBoxFixer : DependencyObject 
{ 
    static DependencyPropertySynchronizer sync = 
    new DependencyPropertySynchronizer 
    { 
     Priority = DispatcherPriority.ApplicationIdle, 
     AutoSyncProperty = Selector.SelectedItemProperty, 
    }; 

    public static readonly DependencyProperty SelectedItemProperty = 
    sync.RegisterAttached("SelectedItem", typeof(object), typeof(ComboBoxFixer), 
    new FrameworkPropertyMetadata 
    { 
     BindsTwoWayByDefault = true, 
    }); 

    public static object GetSelectedItem(... // normal attached property stuff 
    public static void SetSelectedItem(... 
} 

Как это работает

Всякий раз, когда мои: изменения ComboBoxFixer.SelectedItem, синхронизатор обновляет Selector.SelectedItem с приоритетом ApplicationIdle, или наоборот.

Поток данных:

ViewModel property 
    <- bound from -> 
     my:ComboBoxFixer.SelectedItem 
     <- synced with -> 
      ComboBox.SelectedItem 

Дополнительное примечание

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

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

  • Нет связанных вопросов^_^