views:

70

answers:

3

Hi,

I created a custom canvas control inheriting from WPF Canvas. I am using it like this in main window -

<ScrollViewer
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <RTD:RTDesignerCanvas
        Margin="5"
        Background="White"
        x:Name="canvas1"
        Focusable="True"
        AllowDrop="True">
    </RTD:RTDesignerCanvas>
</ScrollViewer>

Everyhting works fine but when I try to set the position of controls inside it like this

Canvas.SetTop(item, 200);

scrollbars are not visible and control is hiddedn down somewhere. Intrestingly, if I add another control to it scroll bars are visible and I can scroll downwards to see the previous control.

I tried to use

base.InvalidateVisual();
base.UpdateLayout();
base.InvalidateArrange();

after changing items Top or Left but nothing happens; Am I missing something or this happens due to some bug?

Update:

to clarify say I have a canvas having its width, height as 100, 100. Now if I move a control(already added in canvas) using Canvas.SetLeft(myControl, 200) then it will move to a position which is not visible by default and scroll bars are also disabled, so there is no way to see that control. Now if I add another control to Canvas scroll bars appear correctly and I can see the previous control by scrolling.

+3  A: 

Did you override MeasureOverride in your custom Canvas? Canvas will always report a DesiredSize of (0, 0), so the ScrollViewer will never think it needs to scroll.

See this StackOverflow answer which suggests using a Grid instead of a Canvas and using the Margin property for positioning. The Grid will report its size based on the size and position of its children, so the ScrollViewer will know it needs to scroll.

Update:

ScrollViewer will give its child ask much size as it asks for, and will only need to scroll if the child is larger than the ScrollViewer. In order to have it scroll properly, you'll need to report a DesiredSize that is large enough to include all of your child controls. You can do that by overriding MeasureOverride like this:

protected override Size MeasureOverride(Size constraint)
{
    base.MeasureOverride(constraint);
    var desiredSize = new Size();
    foreach (UIElement child in Children)
    {
        desiredSize = new Size(
            Math.Max(desiredSize.Width, GetLeft(child) + child.DesiredSize.Width),
            Math.Max(desiredSize.Height, GetTop(child) + child.DesiredSize.Height));
    }
    return desiredSize;
}

An easier solution, however, is to take advantage of the fact that the Grid class will already measure like this. You can use the Margin property of the child elements to position them exactly instead of the Canvas.Left and Canvas.Top properties. If you are currently doing

Canvas.SetLeft(item, 100);
Canvas.SetTop(item, 200);

for an item in the Canvas, you could instead do

item.Margin = new Thickness(100, 200, 0, 0);

to position it in the same place within a one-cell Grid.

Quartermeister
Thanks for replying Quartermeister, I am not overriding MeasureUverride in my custom canvas; I am not clear from your answer whether I am supposed to override it or not? But yes you are right in that "the ScrollViewer will never think it needs to scroll", this is exactly whats happening. Using Grid is not an option as I am having functionalities of moving, resizing etc for which canvas is must.
akjoshi
My mistake, I don't know how I missed it. I am actually overriding this and was performing the same operation in it. Today I found that this method was not at all called for some operations(thats why scroll bars were not visible).I had to use InvalidateMeasure() explicitly after each operation to refresh the canvas.
akjoshi
A: 

Akjoshi,

It has to be a bug. Next time the canvas becomes somewhere hidden, run Snoop on it, and check where it is. Makes sure to check the following canvas properties: ActualWidth, ActualHeight, Opacity, Visibility

Anvaka
Thanks Anvaka, Actually my canvas is not getting hidden but the control inside it. I have updated the question to make it more clear.
akjoshi
A: 

Quartermeister answer helped me in solving this issue.

I had to use base.InvalidateMeasure() explicitly after each operation to refresh the canvas and make the scrollbars visible.

akjoshi