change the group of item in ICollectionView
Asked Answered
A

2

7

I have ICollectionView looks like

public ICollectionView UsersCollectionView
{
    get
    {
        var view = CollectionViewSource.GetDefaultView(this);
        view.GroupDescriptions.Add(new PropertyGroupDescription("SeriesName"));
        view.SortDescriptions.Add(new SortDescription("CreationDate", ListSortDirection.Ascending));
        view.SortDescriptions.Add(new SortDescription("DocumentTypeId", ListSortDirection.Ascending));
        return view;
    }
}

I want to use drag & drop to change the item Series Name , and location on the list view any idea how to do that for example

--- ScienceFiction
------------> Book1 
------------> Book2
--- History 
------------> Book3 
------------> Book4

if Idraged and droped book3 in ScienceFiction the output should be

--- ScienceFiction
------------> Book1 
------------> Book2
------------> Book3 
--- History 
------------> Book4

I use xaml code like this :

    <UserControl.Resources>
    <Style x:Key="ContainerStyle" TargetType="{x:Type GroupItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Expander Header="{Binding Name}" IsExpanded="True">
                        <ItemsPresenter />
                    </Expander>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>
<Grid>
    <ListBox x:Name="lbPersonList" Margin="19,17,162,25" AlternationCount="2" ItemsSource="{Binding}">
        <ListBox.GroupStyle>
            <GroupStyle ContainerStyle="{StaticResource ContainerStyle}"/>
        </ListBox.GroupStyle>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

</Grid>

Aureomycin answered 16/9, 2015 at 18:5 Comment(0)
B
3

AMH,

First modify the ListviewItem Style. It is the container that contains every line (datatemplate instanciation) of the listbox. It is a good place to manage Drag and Drop at the line level ( not a control of a line, there could be many in the DataTemplate). In Visual Studio, select the listbox, right click, Edit additional templates/Edit Generated Item Container(ItemContainerStyle)/Edit a copy

In the ListBoxItemStyle created, add those three declarations amongst the setters :

        <EventSetter Event="ListBoxItem.DragOver" Handler="ListBoxItemDragOver"/>
        <EventSetter Event="ListBoxItem.Drop" Handler="ListBoxItemDrop"/>
        <EventSetter Event="ListBoxItem.PreviewMouseMove" Handler="ListBoxItemPreviewMouseMove"/>

Set the AllowDrop property to true on the ListBox :

<ListBox x:Name="listboxBooks" AllowDrop="True">

Then implement the handlers in the .xaml.cs code :

    #region DnD management

    private Book sourceBook;
    private void ListBoxItemPreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton != MouseButtonState.Pressed)
            return;
        var listboxItem = sender as ListBoxItem;
        if (listboxItem == null)
            return;
        sourceBook = listboxItem.DataContext as Book;
        if (sourceBook == null)
            return;
        var data = new DataObject();
        data.SetData(sourceBook);
        // provide some data for DnD in other applications (Word, ...)
        data.SetData(DataFormats.StringFormat, sourceBook.ToString());
        DragDropEffects effect = DragDrop.DoDragDrop(listboxItem, data, DragDropEffects.Move | DragDropEffects.Copy);
    }
    private void ListBoxItemDrop(object sender, DragEventArgs e)
    {
        if (!e.Data.GetDataPresent(typeof(Book)))
            return;
        var listBoxItem = sender as ListBoxItem;
        if (listBoxItem == null)
            return;
        var targetBook = listBoxItem.DataContext as Book;
        if (targetBook != null)
        {
            viewModel.RecategorizeBook(sourceBook, targetBook.Category);
        }
        e.Handled = true;
    }
    private void ListBoxItemDragOver(object sender, DragEventArgs e)
    {
        Debug.WriteLine(e.Effects);
        if (!e.Data.GetDataPresent(typeof(Book)))
        {
            e.Effects = DragDropEffects.None;
            e.Handled = true;
        }
    }
    private void GroupItemDrop(object sender, DragEventArgs e)
    {
        if (!e.Data.GetDataPresent(typeof(Book)))
            return;
        var groupItem = sender as GroupItem;
        if (groupItem == null)
            return;
        dynamic targetGroup = groupItem.DataContext;
        if (targetGroup != null)
        {
            // here I change the category of the book
            // and refresh the view of the collectionViewSource ( see link to project zipped further)
            viewModel.RecategorizeBook(sourceBook, targetGroup.Name as String);
        }
        e.Handled = true;
    }
    #endregion

Note that I also implemented Drop management on the group header in the handlers. So the handler needs to be declared in the XAML groupstyle :

<ListBox.GroupStyle>
    <GroupStyle>
        <GroupStyle.ContainerStyle>
            <Style TargetType="{x:Type GroupItem}">
                <EventSetter Event="GroupItem.Drop" Handler="GroupItemDrop"/>
                <EventSetter Event="GroupItem.PreviewMouseMove" Handler="ListBoxItemPreviewMouseMove"/>

It works, here is full working code : http://1drv.ms/1FhBZwr

Wish you the best possible code

Bookstack answered 24/9, 2015 at 13:32 Comment(3)
@AMH, isn't it the answer to you question ?Bookstack
AMH, do you expect something more/different to award the bounty ?Bookstack
To anyone around : would it be useful if I showed how to insert the dropped element at a given position in a group, not in the group, but at a random position ? That way the end user would be master of the group.Bookstack
H
1

Unfortunately .net does not yet provide an "easy to use" implementation of drag'n'drop. You have to build a bunch of things on your own. The starting point will be a Behavior starting the drag and a ContentControl being the area onto which the user can drop things. Having those defined you can easily reuse this concept. In the following example the StackPanel can be "dragged" onto an invisible "area" surrounding the TextBlock. This way you are able to implement manually sorting your books (drop before/behind the book under the mouse pointer).

If you want to drop books onto your headers surround them with a DropArea. You could also implement both ways.

Your xaml will look like:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
[...]
<ListBox x:Name="lbPersonList" Margin="19,17,162,25" AlternationCount="2" ItemsSource="{Binding}">
    <ListBox.GroupStyle>
        <GroupStyle ContainerStyle="{StaticResource ContainerStyle}"/>
    </ListBox.GroupStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <myOwn:DropArea>
                    <TextBlock Text="{Binding Name}"/>
                </myOwn:DropArea>
                <i:Interaction.Behaviors>
                    <myOwn:DragBehavior/>
                </i:Interaction.Behaviors>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

The DragBehavior will look like:

public class DragBehavior : Behavior<FrameworkElement>
    [...]
    protected override void OnAttached()
    {
        AssociatedObject.MouseMove += AssociatedObject_MouseMove;
        AssociatedObject.MouseDown += AssociatedObject_MouseLeftButtonDown;
        AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
        base.OnAttached();
    }
    protected override void OnDetaching()
    {
        AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
        AssociatedObject.MouseDown -= AssociatedObject_MouseLeftButtonDown;
        AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
        base.OnDetaching();
    }
    protected virtual void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
    {
        if (some condition of mouse button states or mouse moves)
        {
                DataObject data = new DataObject();
                data.SetData(typeof(anyKeyType), anyData);
                data.SetData(typeof(anyOtherKeyType), anyOtherData);
                DragDrop.DoDragDrop(fe, data, DragDropEffects.Move);                
        }
    }

The DropArea will look like:

public class DropArea : ContentControl
    [...]
    public DropArea()
    {
        DragEnter += AssociatedObjectDragEnter;
        DragLeave += AssociatedObjectDragLeave;
        DragOver += AssociatedObjectDragOver;
        IsTabStop = false;
        AllowDrop = true;
    }
    protected override void AssociatedObjectDrop(object sender, DragEventArgs e)
    {
        object o = e.Data.GetData(typeof(anyKeyType));
        //handle dropped data
    }

Hope that may help you on your way. There may be any frameworks or libraries to adress that issue, but this way you can adress your own needs.

Hugo answered 21/9, 2015 at 5:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.