使用DragDropTarget对Silverlight TreeView上的ObservableCollection进行双向数据绑定

时间:2011-08-01 15:41:58

标签: silverlight treeview mvvm-light observablecollection silverlight-toolkit

以下是最基本的问题:如何监听通过DragDropTarget修改的TreeView控件中更改内容的更新?

所以这是我的交易:我有一个持有议程项目的TreeView。所有都具有相同的数据类型(WCFAgendaItem),并加载到层次结构中,子项表示为属性ChildItems。整个事情都包含在一个ObservableCollection中,并使用MVVM Light绑定到TreeView。非常适合查看。我还希望用户能够使用拖放来重新排序,重新组织和添加来自各种其他来源的新议程(一个示例是图像幻灯片的ListView)。为了保持一致性和易于序列化,所有新项目也将具有相同的WCFAgendaItem数据类型。

这是我的问题:使用Toolkit的拖放功能在UI上进行精美的拖放工作。但我不知道如何让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) - &gt;我试着收听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在任何情况下都是不对的,因为这些数据不仅仅是在顶层更改。我查看了Blend中的EventToCommand(MVVM Light,请记住),但唯一的TreeView特定事件似乎是SelectionChanged,这似乎也不正确。我看了在TreeViewDragDropTarget上放置一个EventToCommand触发器,但是不是那些覆盖UI交互发生方式的方法吗?我不认为WCFAgendaItem上的INotifyPropertyChanged也适合这个:虽然我以后想要编辑项目标题,但是当项目移动时它似乎不会帮助我。

也许我正在寻找的是一个延伸,但我真正希望发生的是Silverlight了解数据绑定在WCFAgendaItem集合的排序和内容两方面都有效,并且完成所有集合的重新处理基于UI交互。然后我可以在重新设计集合之后监听更新事件 - 之后我可以抓取绑定到TreeView的修改后的ObservableCollection,并通过WCF展平/序列化/更新。

失败理想情况:如果需要,我愿意抓取TreeViewItems,但即使这是我需要做的事情,我仍然坚持要做什么。另外,我需要一种方法将所有这些传递回ViewModel,因此我不会编写代码。我是否需要附加到Drop()并重写掉线逻辑?我从Toolkit开始发现了一些关于自定义拖放实现的旧文章,但没有人提到如何保存修改后的TreeView,尤其是在MVVM情况下。

终于{

在输入时我发现this article可能有用,虽然这在ViewModel中是相当多的工作。这是有希望的,我会调查,但我仍然希望更简单的事情。此外,自撰写文章以来,工具包事件似乎发生了一些变化。

}

1 个答案:

答案 0 :(得分:0)

我在实现这种类型的DragDrop功能时也遇到了问题。根本原因似乎是ItemDragCompleted事件(EventHandler)和ItemDroppedOnSource(DragEventHandler)都没有传递项目被删除的索引。

我最终继承了DragDropTarget,以便公开受保护的方法:

int GetDropTargetInsertionIndex(TItemsControlType dropTarget, DragEventArgs args)

然后,我使用附加行为来承担将指定索引处的项插入到基础集合的责任。

我担心底层代码太过庞大而无法包含在StackOverflow答案中(主要是由于广泛的解耦),但它在我的博客主题列表中。与此同时,我希望上面的信息有所帮助,这对我来说无疑是解决方案的关键。

伊恩