Двусторонняя привязка данных к ObservableCollection в Silverlight TreeView с использованием DragDropTarget - PullRequest
3 голосов
/ 01 августа 2011

Вот основной вопрос: как мне прослушать обновление изменений в элементе управления TreeView, измененном с помощью DragDropTarget?

Итак, вот мое предложение: у меня естьTreeView, который содержит пункты повестки дня.Все они имеют один и тот же тип данных (WCFAgendaItem) и загружаются в иерархию с дочерними элементами, выраженными как свойство ChildItems.Все это заключено в ObservableCollection и привязано к TreeView с помощью MVVM Light.Прекрасно работает для просмотра.Я также хочу, чтобы пользователи могли использовать перетаскивание для изменения порядка, реорганизации и добавления новых элементов в эту повестку дня из различных других источников (одним из примеров является ListView слайдов изображений).Все новые элементы также будут иметь один и тот же тип данных WCFAgendaItem для обеспечения согласованности и упрощения сериализации.

Вот моя проблема: перетаскивание прекрасно работает в пользовательском интерфейсе с использованием функциональности перетаскивания в наборе инструментов.Но я понятия не имею, как заставить ViewModel понимать изменения в содержимом TreeView.

Код из представления (Agenda.xaml):

(вверх)

<UserControl.Resources>
    <AHHSTeam_SLClassroomManagerMVVM_Helpers_Converters:BooleanVisibilityConverter x:Key="BooleanVisibilityConverter"/>
    <sdk:HierarchicalDataTemplate x:Key="hdtAgenda" ItemsSource="{Binding ChildItems, Mode=TwoWay}" >
        <Grid HorizontalAlignment="Left">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="{Binding ImageThumbnailWidth}" />
                <ColumnDefinition Width="250" />
            </Grid.ColumnDefinitions>
            <Image Grid.Column="0" Source="{Binding ThumbnailURL}" Width="{Binding ImageThumbnailWidth}" Height="{Binding ImageThumbnailHeight}" Visibility="{Binding HasImage, Converter={StaticResource BooleanVisibilityConverter}}" >
                <ToolTipService.ToolTip>
                    <Image Source="{Binding ResizedImageURL}" />
                </ToolTipService.ToolTip>
            </Image>
            <TextBlock Grid.Column="1" Text="{Binding Title}" TextWrapping="Wrap" />
        </Grid>
    </sdk:HierarchicalDataTemplate>
    <Style TargetType="sdk:TreeViewItem" >
        <Setter Property="IsExpanded" Value="True" />
    </Style>
</UserControl.Resources>

(позже)

<controlsToolkit:TreeViewDragDropTarget Grid.Row="1" Grid.Column="0" x:Name="ddtAgenda" AllowDrop="True"
                                            HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
        <sdk:TreeView Width="375" ScrollViewer.HorizontalScrollBarVisibility="Auto"
                      ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding DailyAgenda, Mode=TwoWay}" ItemTemplate="{StaticResource hdtAgenda}">
        </sdk:TreeView>
    </controlsToolkit:TreeViewDragDropTarget>

Код ViewModel (AgendaViewModel.cs) -> Я пытался прослушивать CollectionChanged, но пока что он не работает

(в конструкторе)

            //add notification of agenda changes
            DailyAgenda.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DailyAgenda_CollectionChanged);

(событие)

    void DailyAgenda_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        System.Windows.MessageBox.Show("Daily agenda updated, now has " + e.NewItems.Count.ToString() + " top-level elements.");
    }

Код из модели (WCFAgendaItem.cs)

[ContentProperty("ChildItems")]
public partial class WCFAgendaItem: INotifyPropertyChanged
{
    private ObservableCollection<WCFAgendaItem> _childItems = new ObservableCollection<WCFAgendaItem>();

    public ObservableCollection<WCFAgendaItem> ChildItems
    {
        get
        {
            return _childItems;
        }
        set
        {
            _childItems = value;
        }
    }
...

Я почти уверен, что получаю прослушиваниедля CollectionChanged не подходит ни в коем случае, учитывая, что эти данные не просто изменяются на верхнем уровне.Я посмотрел на EventToCommand в Blend (MVVM Light, помните), но единственное специфичное для TreeView событие - это SelectionChanged, что тоже не совсем правильно.Я смотрел на помещение триггера EventToCommand на TreeViewDragDropTarget, но разве эти методы не переопределяют, как происходят взаимодействия с пользовательским интерфейсом?Я не думаю, что INotifyPropertyChanged для WCFAgendaItem также подходит для этого: хотя я хочу, чтобы позже это понадобилось для редактирования названий элементов, не похоже, что это поможет мне, когда элементы будут перемещаться.

Может быть, то, что я ищу, - это растяжка, но я действительно хочу, чтобы Silverlight понял, что привязка данных работает в обоих направлениях при упорядочении и содержимом коллекции WCFAgendaItem, и выполняет всю работу по переработке самой коллекции на основеUI взаимодействияТогда я мог бы просто прослушать событие обновления после того, как коллекция была переработана - после этого я могу просто отсканировать измененную коллекцию ObservableCollection, привязанную к TreeView, и сгладить / сериализовать / обновить через WCF.

В противном случае идеальная ситуация: IЯ хочу сканировать TreeViewItems, если это необходимо, но даже если это то, что мне нужно сделать, я застрял, когда это сделать.Кроме того, мне нужен способ передать все это обратно в ViewModel, чтобы я не писал код позади.Нужно ли присоединяться к Drop () и переделывать логику удаления?Я нашел несколько старых статей о пользовательских реализациях перетаскивания, начиная с Toolkit, но никто не упоминает, как сохранить измененный TreeView, особенно в ситуации MVVM.

finally {

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

}

1 Ответ

0 голосов
/ 11 октября 2011

У меня также были проблемы с реализацией этого типа функциональности DragDrop. Основная причина, по-видимому, заключается в том, что ни событие ItemDragCompleted (EventHandler), ни ItemDroppedOnSource (DragEventHandler) не проходят индекс, при котором элемент был удален.

В итоге я создал подкласс DragDropTarget для предоставления защищенного метода:

int GetDropTargetInsertionIndex(TItemsControlType dropTarget, DragEventArgs args)

Затем я использовал прикрепленное поведение, чтобы взять на себя ответственность за вставку элементов с указанным индексом в базовые коллекции.

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

Ian

...