views:

1722

answers:

3

I'm using Silverlight 3/C#, and I'm trying to create some XAML objects programatically in response to a user clicking on a button.

However, I can't seem to position a Canvas object that I have created - when I call SetValue() on the new Canvas with the Canvas.LeftProperty value, the browser window clears to an empty screen.

I have a simple function that exhibits the problem (I started out with something more complex):

    private Canvas MakeNewCanvas()
    {
        Canvas newCanvas = new Canvas();
        newCanvas.Width = newCanvas.Height = 50;
        newCanvas.SetValue(Canvas.LeftProperty, 10);
        return newCanvas;
    }

and a simple button click handler that calls this:

    private void MyButton_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        myPage.Children.Add(MakeNewCanvas());
    }

NB: myPage is a Canvas object defined in my app's MainPage XAML file.

I've traced this through in the VS debugger with the SL app executing in Firefox, and whenever it executes the SetValue() line, the browser goes white and starts trying to download data (according to the status bar). I tried calling SetValue() after I've put the Canvas in the XAML tree with something like:

    myPage.Children.Add(newCanvas);
    newCanvas.SetValue(Canvas.LeftProperty, 10);

but it makes no difference - as soon as the SetValue() call is hit, the browser goes white.

The Canvas class seems to have no direct method of setting Left/Top on itself, apart from the SetValue() function with dependency property enum values. There's also Canvas.SetLeft() and Canvas.SetTop() but I guess they're just shims onto SetValue(). Using them didn't help anyway.

If I don't call SetValue(), then my canvas appears (and child objects I've added to it) in the SL app as you might expect, in the very top left.

If I create and position a Canvas object in Expression Blend, then the XAML generated includes Canvas.Left and Canvas.Top property values on the Canvas itself, as I would expect, so it seems to me that what I'm trying in C# should work.

But I don't seem to be able to set the left/top values myself from C# without the SL in the browser going all weird.

Any ideas?

Edit: my approach is correct, but canvas coords need to be floating point, not integer - see accepted answer for details.

A: 

Hi there.
I am not sure to have well understand, but the Canvas.Left/Top property have effect related to a container canvas and not the element you are creating itself.
I guess you should place a Canvas as main container (e.g. instead of the Grid) and then any element who owns Left/Top attached property it will be placed accordingly withing the main canvas.

venezia
Thanks, but am already doing this - have updated my question to make this clear.
Slacker
A: 

The behaviour of attached properties can be a little confusing at first.

The properties Canvas.LeftProperty and Canvas.TopProperty are applied to child objects of a canvas. They are therefore only meaningful when the child object is placed on a Canvas. Its important to understand the in WPF/SL objects do not position themselves, its up to the containing panel to decide where to put them.

I suspect the myPage is not of the Canvas type, its probably a Grid, hence it would have no idea what to do with such properties even if it bothered to look for them (which it doesn't).

In order for you to specifically position you new Canvas you need to be adding it to a Canvas.

AnthonyWJones
Yes, it took me a few minutes to figure that (Canvas properties apply to child objects of Canvas), but I know that. myPage is a Canvas, as defined in my XAML, and I just examined this.myPage in the VS debugger to make sure just before I add the new Canvas as a child, and VS reported that myPage is indeed a Canvas object.
Slacker
+4  A: 

Trying your code, but with the debugger catching exceptions, I get:

DependencyProperty of type System.Double cannot be set on an object of type System.Int32.

which is a really stupid gotcha - SetValue only takes Object, so you're prone to a problem like this.

Try either:

newCanvas.SetValue(Canvas.LeftProperty, 10.0);

or

Canvas.SetLeft(newCanvas, 10);

and it will probably work.

Jim Lynn
+1 Good catch, well spotted
AnthonyWJones