2

I'm having some trouble understanding (and fixing) a bug I'm experiencing.

I have the UI like in the following picture:

All of those areas colored in lightblue are Canvases and they are movable. This is where I have my problem. The one from the top left can be moved without problems. The other two, when I'm dragging them, disappear. I can't explain why.

This is the code for the moving of elements:

// this is all inside the MouseMove event handler function

// If there is no dragged element
if (this.DraggedElement == null || !this.IsDragInProgress)
    return;

/*
 * Calculating the new position for the dragged element
 */

// Mouse current position
Point cursor = e.GetPosition(this);

double xMove = 0;
double yMove = 0;

// Movement detected
if (cursor != MouseClickLocation)
{
    // Moving on the x-axis and y-axis
    xMove = cursor.X - MouseClickLocation.X;
    yMove = cursor.Y - MouseClickLocation.Y;

    // Actually moving the element
    if (this.ConstrainToBounds(this.DraggedElement, mainWindow))
    {
        TranslateTransform translate = new TranslateTransform(xMove, yMove);

        this.DraggedElement.RenderTransform = translate;
    }
}

The code for ConstrainToBounds() method is supposed to not allow me to move any Canvas outside the window borders (it works perfectly for the top left Canvas, but not for the others) and is as follows:

private Boolean ConstrainToBounds(Canvas element, UIElement container)
{
    try
    {
        Boolean respects = true;

        // Values used to reset the element position to a proper location
        double xReset = 0;
        double yReset = 0;

        // Left x-axis constraint
        if (element.TranslatePoint(new Point(), container).X <= new Point(0, 0).X)
        {
            respects = false;

            // Get elements' current position and adjust
            xReset = element.TranslatePoint(new Point(), container).X + 1;
            yReset = element.TranslatePoint(new Point(), container).Y;

            TranslateTransform translate = new TranslateTransform(xReset, yReset);

            element.RenderTransform = translate;
        }

        // Right x-axis constraint
        if (element.TranslatePoint(new Point(), container).X + element.RenderSize.Width >= container.RenderSize.Width)
        {
            respects = false;

            // Get elements' current position and adjust
            xReset = element.TranslatePoint(new Point(), container).X - 1;
            yReset = element.TranslatePoint(new Point(), container).Y;

            TranslateTransform translate = new TranslateTransform(xReset, yReset);

            element.RenderTransform = translate;
        }

        // Top y-axis constraint
        if (element.TranslatePoint(new Point(), container).Y <= new Point(0, 0).Y)
        {
            respects = false;

            // Get elements' current position and adjust
            xReset = element.TranslatePoint(new Point(), container).X;
            yReset = element.TranslatePoint(new Point(), container).Y + 1;

            TranslateTransform translate = new TranslateTransform(xReset, yReset);

            element.RenderTransform = translate;
        }

        // Bottom y-axis constraint
        if (element.TranslatePoint(new Point(), container).Y + element.RenderSize.Height >= container.RenderSize.Height)
        {
            respects = false;

            // Get elements' current position and adjust
            xReset = element.TranslatePoint(new Point(), container).X;
            yReset = element.TranslatePoint(new Point(), container).Y - 1;

            TranslateTransform translate = new TranslateTransform(xReset, yReset);

            element.RenderTransform = translate;
        }

        return respects;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

Edit_1: Added code from MainWindow.xaml:

<Window 
Name="mainWindow"
x:Class="WPF_TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="480" Width="555">

<Grid Name="mainGrid">
    <Canvas Grid.Row="0" 
            Grid.Column="0" 
            Background="AliceBlue" 
            Name="LeftTop"
            MouseDown="onMouseDown"
            MouseMove="onMouseMove" 
            MouseUp="onMouseUp" >

        <Ellipse Fill="Blue"
       Width="100"
       Height="100"/>
    </Canvas>

    <Canvas Grid.Row="0" 
            Grid.Column="2" 
            Background="AliceBlue" 
            Name="RightTop"
            MouseDown="onMouseDown"
            MouseMove="onMouseMove"
            MouseUp="onMouseUp">
        <Ellipse Fill="Blue"
       Width="100"
       Height="100"/>
    </Canvas>

    <Label Grid.Row="2" 
            Grid.Column="0"  
            Name="LeftBottom">
    </Label>

    <Canvas Grid.Row="2" 
            Grid.Column="3" 
            Background="AliceBlue" 
            Name="RightBottom"
            MouseDown="onMouseDown"
            MouseMove="onMouseMove"
            MouseUp="onMouseUp">
        <Ellipse Fill="Blue"
       Width="100"
       Height="100"/>
    </Canvas>

    <Grid.RowDefinitions>
        <RowDefinition Height="200" />
        <RowDefinition Height="50" />
        <RowDefinition Height="200" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="250" />
        <ColumnDefinition Width="50" />
        <ColumnDefinition Width="250" />
    </Grid.ColumnDefinitions>
</Grid>
</Window>

Edit_2: So, I've discovered that when I move the top right canvas, it actually moves outside of the view (at position 600,0). Currently trying to understand why this happens.

animuson
  • 53,861
  • 28
  • 137
  • 147
Andrei
  • 63
  • 4
  • What's containing those canvases, that is, what panel is used in `MainWindow`? A `Grid` or `Canvas`? – Chris Sinclair Mar 08 '13 at 14:33
  • In the main window there's a grid in which I have all these other canvases and spaces. I'll edit my post and add the XAML code also. – Andrei Mar 08 '13 at 14:35
  • 1
    Instead of using render transforms, why don't you just update their `Margin` property and then clamp the values to be within the bounds? So if you calculate the `Margin.Left` to be less than `0`, clamp it to `0`. If it's greater than `250 - Diameter` clamp it to `250 - Diameter`. Similarly handle it for `Top`; leave `Right` and `Bottom` margins at 0. EDIT: fixed math :P – Chris Sinclair Mar 08 '13 at 14:38
  • I don't know how this will affect the Canvases further positioning inside the grid, since I'll need their position to detect intersection with the other canvases and calculate intersected areas. Though, I will give this suggestion with margins a try, just to see if it works. I don't understand why 1 canvas works perfectly and the other 2 simply won't work. – Andrei Mar 08 '13 at 14:42

1 Answers1

0

Instead of using render transforms, might be easier to update the Canvas's Margin property:

if (cursor != MouseClickLocation)
{
    // Moving on the x-axis and y-axis
    xMove = cursor.X - MouseClickLocation.X;
    yMove = cursor.Y - MouseClickLocation.Y;

    // Actually moving the element
    this.DraggedElement.Margin = this.CalculateNewPosition(this.DraggedElement, mainWindow, xMove, yMove);
}

Where CalculateNewPosition might look something like this (warning, untested):

private Thickness CalculateNewPosition(Canvas element, UIElement container, double translationX, double translationY)
{
    Thickness currentPosition = element.Margin;
    Thickness newPosition = new Thickness(currentPosition.Left + translationX, currentPosition.Top + translationY, 0, 0);

    int containerWidth = container.ActualWidth;
    int containerHeight = container.ActualHeight;
    int elementWidth = element.ActualWidth;
    int elementHeight = element.ActualHeight;

    if (newPosition.Left < 0)
        newPosition.Left = 0;
    else if (newPosition.Left + elementWidth > containerWidth)
        newPosition.Left = containerWidth - elementWidth;

    if (newPosition.Top < 0)
        newPosition.Top = 0;
    else if (newPosition.Top + elementHeight > containerHeight)
        newPosition.Top = containerHeight - elementHeight;

    return newPosition;
}

I'm not sure why specifically your code isn't working for the other circles. Possibly it has to do with the bounds checks like:

if (element.TranslatePoint(new Point(), container).X <= new Point(0, 0).X)
if (element.TranslatePoint(new Point(), container).X + element.RenderSize.Width >= container.RenderSize.Width)

The assumption is that the new Point(0,0) and TranslatePoint are returning points relative to each containing grid cell. I'm not sure if that assumption is correct; one (or both) of the comparisons may be absolute with respect to the application or something to that effect. It would be difficult to ascertain exactly just via a cursory examination of your code; you would need to run the debugger and check your values and see where they diverge from what you expect.

Chris Sinclair
  • 22,858
  • 3
  • 52
  • 93
  • It's not just for the circles, it is for the full canvases. I've tried your suggestion now, but it is not really working as expected. As for running the debugger these are the exact steps that happen: I click the top right canvas and try to drag it towards the middle of the window. everything is fine. It enters the IF for the Top y-axis constraint. after this it exits the ConstrainToBounds() function. It goes to where the movement is actually done, but it does not execute because respects was false. and after finishing the MouseMove function, the DraggedElement disappears. – Andrei Mar 08 '13 at 15:18