1

I have UserControls which i have in a ListBox, which then hosts them on a Canvas. I've then made the UserControls draggable on the canvas by using the DraggableExtender(Dragging an image in WPF).

But ever since I changed from ItemsControl to ListBox, to be able to make the controls selectable, the Dragging works really bad. For instance if I'm dragging a control over another control the other control gets focused and sticks to the dragging. Also if I move the mouse outside the canvas the usercontrol gets stuck at the edge and i loose dragging focus, like if CaptureMouse doesn't work.

The ListBox looks like this(when it worked before it was an ItemsControl):

<ListBox ItemsSource="{Binding Components}" SelectedItem="{Binding SelectedItem}" Background="Transparent">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas ClipToBounds="True" Height="{Binding CurrentProject.Height, Converter={StaticResource SizeConverter}}"
                            Width="{Binding CurrentProject.Width, Converter={StaticResource SizeConverter}}" 
                            HorizontalAlignment="Left" VerticalAlignment="Top">
                        <Canvas.Background>
                            <SolidColorBrush Color="{DynamicResource {x:Static SystemColors.WindowFrameColorKey}}"/>
                        </Canvas.Background>
                    </Canvas>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="HorizontalAlignment" Value="Stretch" />
                    <Setter Property="Background" Value="Transparent" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <Grid>
                                    <Border Background="{TemplateBinding Background}" />
                                    <ContentPresenter/>
                                </Grid>
                                <ControlTemplate.Triggers>
                                    <MultiTrigger>
                                        <MultiTrigger.Conditions>
                                            <Condition Property="IsMouseOver" Value="True" />
                                            <Condition Property="IsSelected" Value="False"/>
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Background" Value="#8868D5FD" />
                                    </MultiTrigger>
                                    <Trigger Property="IsSelected" Value="True">
                                        <Setter Property="Background" Value="#4468D5FD" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Utils:DraggableExtender.CanDrag" Value="True" />
                    <Setter Property="Canvas.Top" Value="{Binding Path=Conveyor.Y, Converter={StaticResource SizeConverter},Mode=TwoWay}" />
                    <Setter Property="Canvas.Left" Value="{Binding Path=Conveyor.X, Converter={StaticResource SizeConverter},Mode=TwoWay}" />
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

My DraggableExtender looks like this:

public class DraggableExtender : DependencyObject
    {
        public static readonly DependencyProperty CanDragProperty = 
            DependencyProperty.RegisterAttached("CanDrag", typeof(bool), typeof(DraggableExtender), 
            new UIPropertyMetadata(false, OnChangeCanDragProperty));

        private static bool isDragging = false;
        private static Point offset;

        public static void SetCanDrag(UIElement element, bool o)
        {
            element.SetValue(CanDragProperty, o);
        }

        public static bool GetCanDrag(UIElement element, bool o)
        {
            return (bool)element.GetValue(CanDragProperty);
        }

        private static void OnChangeCanDragProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = d as UIElement;
            if (element == null) return;

            if (e.NewValue != e.OldValue)
            {
                if ((bool)e.NewValue)
                {
                    element.PreviewMouseDown += element_PreviewMouseDown;
                    element.PreviewMouseUp += element_PreviewMouseUp;
                    element.PreviewMouseMove += element_PreviewMouseMove;
                }
                else
                {
                    element.PreviewMouseDown -= element_PreviewMouseDown;
                    element.PreviewMouseUp -= element_PreviewMouseUp;
                    element.PreviewMouseMove -= element_PreviewMouseMove;
                }
            }
        }

        private static void element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            if (element == null) return;
            Debug.WriteLine(element);

            isDragging = true;
            element.CaptureMouse();
            offset = e.GetPosition(element);
        }

        private static void element_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (!isDragging) return;

            FrameworkElement element = sender as FrameworkElement;
            if (element == null) return;

            Canvas canvas = element.FindAncestor<Canvas>();
            if (canvas == null) return;

            Point mousePoint = e.GetPosition(canvas);

            mousePoint.Offset(-offset.X, -offset.Y);

            element.SetValue(Canvas.LeftProperty, mousePoint.X);
            element.SetValue(Canvas.TopProperty, mousePoint.Y);
        }

        private static void element_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            if (element == null) return;
            element.ReleaseMouseCapture();
            isDragging = false;
        }
 }

I've been trying to find a solution but so far I've been unsuccessful. Does anyone have any idea why this is happening and how I can solve it?

Community
  • 1
  • 1
chiefi
  • 101
  • 3
  • 12

1 Answers1

1

I was finally able to figure out how to solve this problem, or atleast how to make a workaround. The problem seems to be that ListBox also captures the mouse within its select code. So what I changed was to add e.Handled = true so the ListBox wouldn't get the event and thus override the mousecapture. This did render the select mechanism unusable but to workaround this I moved my code to only listen to MouseRight event which means that when I leftclick I select and when I rightclick I can drag.

private static void element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            if (element == null) return;
            Debug.WriteLine(element);

            isDragging = true;
            element.CaptureMouse();
            offset = e.GetPosition(element);
            e.Handled = true;
        }
chiefi
  • 101
  • 3
  • 12