Reputation: 814
I'm trying to create a drag and drop functionality for a couple of buttons which are contained in an ObservableCollection<>
in the view model, the collection is later used as an ItemsSource
for a StackPanel
:
This is the xaml structure:
<ItemsControl x:Name="RingHolder" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding Rings}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<customControls:RingsStackpanel Orientation="Vertical" VerticalAlignment="Bottom"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<customControls:RingControl VerticalAlignment="Stretch" Height="50"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Where RingsStackPanel
inherits from StackPanel
and RingControl
inherits from Button
.
In order to allow the button to be dragged around freely I want to detach it from it's parent like so:
if (VisualTreeHelper.GetParent(this) is ContentPresenter contentPresenter)
{
if (VisualTreeHelper.GetParent(contentPresenter) is RingsStackpanel ringStackPanel)
{
ringStackPanel.Children.Remove(contentPresenter);
}
}
However I get the following exception:
InvalidOperationException:
Cannot explicitly modify Children collection of Panel used as ItemsPanel for ItemsControl. ItemsControl generates child elements for Panel
Which makes sense, I can only modify it by modifying the ItemsSource
that is bound to it, however in this scenario, removing an item from the ObservableCollection<>
causes the selected Button
to disappear as it's being destroyed. Is there anyway I can allow my Button
to move around freely, without creating a clone of it (this would be nigh impossible as the project currently is).
Upvotes: 0
Views: 531
Reputation: 169420
You could remove the data item from the source collection and add a public method to your RingsStackpanel
class that removes the visual from the logical tree:
public class RingsStackpanel : StackPanel
{
...
public void RemoveElement(Visual visual) => RemoveVisualChild(visual);
}
You should then be able to handle the Unloaded
event of the ContentPresenter
and remove its parent-child with the StackPanel
. Something like this:
if (VisualTreeHelper.GetParent(this) is ContentPresenter contentPresenter)
{
if (VisualTreeHelper.GetParent(contentPresenter) is RingsStackpanel ringStackPanel
&& RingHolder.ItemsSource is ObservableCollection<YourItemType> dataItems
&& contentPresenter.DataContext is YourItemType dataItem)
{
//wait for the ContentPresenter to get unloaded
RoutedEventHandler handler = null;
handler = (ss, ee) =>
{
//remove the parent-child relationship:
ringStackPanel.RemoveElement(contentPresenter);
contentPresenter.Unloaded -= handler;
};
//remove the data object
dataItems.Remove(dataItem);
}
}
Upvotes: 1