Dominic Jonas
Dominic Jonas

Reputation: 5005

Lock DragMove inside of an area | only horizontal vertical movement | DragMove Preview Event

I have a WindowContainer class where all ChildWindows gets automatically registered. The idea is, all ChildWindows are "locked" inside of the WindowContainer and you cannot move/drag them outside of this area.

ChildWindow inherits from Window.

I have already a solution where it is working:

C# Snippet

// Events

private void ChildWindow_OnLocationChanged(object sender, EventArgs eventArgs)
{
    _CheckBounds(sender as HtChildWindow);
}

private void ChildWindow_OnSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
{
    _CheckBounds(sender as HtChildWindow);
}

// Private Methods

private void _CheckBounds(HtChildWindow window)
{
    Rect trueWindowContainer = WindowContainer.TransformToAncestor(System.Windows.Application.Current.MainWindow).TransformBounds(new Rect(new Point(0, 0), WindowContainer.RenderSize));

    if (window.Left < trueWindowContainer.X)
        window.Left = trueWindowContainer.X;
    if (window.Top < trueWindowContainer.Y)
        window.Top = trueWindowContainer.Y;
    if (window.Left + window.ActualWidth > trueWindowContainer.X + trueWindowContainer.Width)
        window.Left = (trueWindowContainer.X + trueWindowContainer.Width) - window.ActualWidth;
    if (window.Top + window.ActualHeight > trueWindowContainer.Y + trueWindowContainer.Height)
        window.Top = (trueWindowContainer.Y + trueWindowContainer.Height) - window.ActualHeight;            
}

So problem is, that the Window can still get moved outside of the area and it is lagging. I want to avoid this behavior by locking vertical or horizontal movement.

My solution approach would be catch a PreviewMouseMove or PreviewDragMove event. But I don't know how to do this. PreviewDragMove does not exist.

Preview

preview


Final working solution

ChildWindow

    // ##############################################################################################################################
    // Properties
    // ##############################################################################################################################

    /// <summary>
    /// True when when mouse is down on the dragmove area
    /// </summary>
    public bool IsMouseDownOnDrag { get; private set; }

    private Rectangle _PartDragMove;
    private Point _AnchorPoint;
    private bool _IsDragging;
    private Rect _DragMoveArea = Rect.Empty;

    // ##############################################################################################################################
    // Konstructor
    // ##############################################################################################################################

    private HtChildWindow()
    {
        Unloaded += _OnUnloaded;
        Loaded += _OnLoaded;
    }

    private void _OnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        if(_DragMoveArea == Rect.Empty)
            _DragMoveArea = SystemParameters.WorkArea;
    }

    private void _OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
    {
        if (_PartDragMove != null)
        {
            _PartDragMove.PreviewMouseLeftButtonDown -= PartDragMove_OnPreviewMouseLeftButtonDown;
        }
    }

    // ##############################################################################################################################
    // Overrides
    // ##############################################################################################################################

    public override void OnApplyTemplate()
    {
        _PartDragMove = GetTemplateChild("PART_DragMove") as Rectangle;
        if (_PartDragMove != null)
        {
            _PartDragMove.PreviewMouseLeftButtonDown += PartDragMove_OnPreviewMouseLeftButtonDown;
        }

        base.OnApplyTemplate();
    }

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        if (IsMouseDownOnDrag)
        {
            _AnchorPoint = PointToScreen(e.GetPosition(this));
            _IsDragging = true;
            CaptureMouse();
            e.Handled = true;
        }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (_IsDragging)
        {
            Point currentPoint = PointToScreen(e.GetPosition(this));

            double newLeftPosition = Left + currentPoint.X - _AnchorPoint.X;
            double newTopPosition = Top + currentPoint.Y - _AnchorPoint.Y;

            //control area
            if (newLeftPosition < _DragMoveArea.X)
                newLeftPosition = _DragMoveArea.X;
            if (newTopPosition < _DragMoveArea.Y)
                newTopPosition = _DragMoveArea.Y;
            if(newLeftPosition + ActualWidth > _DragMoveArea.X + _DragMoveArea.Width)
                newLeftPosition = _DragMoveArea.X + _DragMoveArea.Width - ActualWidth;
            if (newTopPosition + ActualHeight > _DragMoveArea.Y + _DragMoveArea.Height)
                newTopPosition = _DragMoveArea.Y + _DragMoveArea.Height - ActualHeight;

            Left = newLeftPosition;
            Top = newTopPosition;
            _AnchorPoint = currentPoint;
        }
    }

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        IsMouseDownOnDrag = false;
        if (_IsDragging)
        {
            _IsDragging = false;
            e.Handled = true;
            ReleaseMouseCapture();
        }
    }

    // ##############################################################################################################################
    // Events
    // ##############################################################################################################################

    private void PartDragMove_OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        IsMouseDownOnDrag = true;
    }

    // ##############################################################################################################################
    // Public Methods
    // ##############################################################################################################################

    public void SetDragMoveArea(Rect area)
    {
        _DragMoveArea = area;
    }

So now I'm only calling SetDragMoveArea() if my ChildWindow should get locked inside of my WindowContainer. I'm extracting the information for this area with the following code

WorkingArea = WindowContainer.TransformToAncestor(System.Windows.Application.Current.MainWindow).TransformBounds(new Rect(new Point(0, 0), WindowContainer.RenderSize));

As default, is it locked inside of the Screen (See _OnLoaded).

Preview of working solution

Working_Preview

Upvotes: 2

Views: 600

Answers (1)

Niko
Niko

Reputation: 832

I have found a solution for you. If you want to customize movements you cannot use DragMove method. Instead you have to implement this functionality on your own, overriding the following three functions: OnMouseLeftButtonDown, OnMouseMove and OnMouseLeftButtonUp. I have implemented something like that:

 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            anchorPoint = e.GetPosition(this);
            inDrag = true;
            CaptureMouse();
            e.Handled = true;
        }

Where anchorPoint is the point that I am using for Binging window position, inDrag is private boolean and CaptureMouse is UIElement method.

And then:

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (inDrag)
        {
            Point currentPoint = e.GetPosition(this);
            this.Left = this.Left + currentPoint.X - anchorPoint.X;
            /*this.Top = this.Top + currentPoint.Y - anchorPoint.Y*/; // this is not changing in your case
            anchorPoint = currentPoint;
        }
    }

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        if (inDrag)
        {
            ReleaseMouseCapture();
            inDrag = false;
            e.Handled = true;
        }
    }

    // Loss of capture e.g. when you press Alt + Tab
    protected override void OnLostMouseCapture(MouseEventArgs e)
    {
        if (_inDrag)
        {
            ReleaseMouseCapture();
            _inDrag = false;
            e.Handled = true;
        }
    }

where ReleaseMouseCapture is UIElement method. I think it should help you.

Upvotes: 4

Related Questions