1

I need to add several user controls to a Canvas. The size of the UserControl depends on the number of items present in the ItemsControl of the UserControl. To position the controls properly and to draw interconnecting lines between the usercontrols, I need the absolute width/height w.r.t the parent canvas. How to get these? ActualHeight and ActualWidth are returning 0.

I had asked similar question earlier, but could not get the right answer.

EDIT: Adding XAML od UserControl

<UserControl x:Class="SilverlightApplication2.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"   
DataContext="{Binding RelativeSource={RelativeSource Self}}" Loaded="UserControl_Loaded">

<Grid x:Name="LayoutRoot" Background="White">
    <Border CornerRadius="3" BorderThickness="1" BorderBrush="LightGray">
        <Grid  Name="grid1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
            <Grid.RowDefinitions>
                <RowDefinition Height="40*" />
                <RowDefinition Height="136*" />
            </Grid.RowDefinitions>
            ...
            <Grid Name="gridPC" Grid.Row="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="55*" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="55*" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>            
                ....

                <ItemsControl x:Name="pitems" ItemsSource="{Binding RowsP}" Grid.Row="1">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Width="250" Orientation="Horizontal">
                                <TextBlock Text="{Binding X}" Width="100" />
                                <TextBlock Text="{Binding Y}" Width="130" />
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>

               ......
            </Grid>
        </Grid>
    </Border>
</Grid>

Community
  • 1
  • 1
softwarematter
  • 28,015
  • 64
  • 169
  • 263

4 Answers4

2

You have few options you can do this, forcing to call Window.Measure and Window.Arrange will make all values to be calculated, or you can get those values in the Window.Loaded event. This same issue is discussed already on this question.

If you are sizing to content:

window.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
window.Arrange(new Rect(0, 0, window.DesiredWidth, window.DesiredHeight));

If you are using an explicit window size:

window.Measure(new Size(Width, Height));
window.Arrange(new Rect(0, 0, window.DesiredWidth, window.DesiredHeight));

or

public MyWindow()
{
    Loaded += delegate
    {
        // access ActualWidth and ActualHeight here
    };

}
Community
  • 1
  • 1
Tomislav Markovski
  • 12,331
  • 7
  • 50
  • 72
1

It has been a long time but I did something similar a few years ago. I don't recall all the details right now, I will look at this when I have more time this evening. I wanted to give you a quick idea of why you are getting 0 for the size.

This is primarily because the layout system in WPF occurs in two passes, the measure and the arrange pass. First the container control (in your case the Panel) asks it's children for their size once this pass is completed, the container will arrange it children using the sizes they calculated in the measure pass.

I would recommend reading the this MSDN article focusing on the Measuring and Arranging Children section. http://msdn.microsoft.com/en-us/library/ms745058.aspx

Let me know if this does not help, and I will spend some more time to refresh my memory on all the details.

Based on your edit you may want to check out this post describing how to create objects that are connected with lines. http://denisvuyka.wordpress.com/2007/10/21/wpf-diagramming-drawing-a-connection-line-between-two-elements-with-mouse/

I think this is more directed at your exact scenario.

Kerry H
  • 661
  • 5
  • 7
1

ActualWidth and ActualHeight work only after the control has been rendered.

To get the desired size of a control, you need to let it measure itself by calling it's Measure method. After that, you can use DesiredSize property which will contain the values you seek.

There is also a good article by Charles Petzold handling a similar situation:

Thinking Outside the Grid

decyclone
  • 30,394
  • 6
  • 63
  • 80
1

I am actively creating a Silverlight timeline control and I don't do a measure as the previous posters advised. I simply wait for the final onsize call or other dependent properties OnChanged event.

Here is what I do to load my control which has a canvas:

  1. Subscribe to the user control's Loaded event (which is the target for all dependent load events calls and on size event in (step #2)).
  2. Subscribe to the Size Changed event (which calls the controls loaded event as mentioned in #1).
  3. All dependent properties OnXXXPropertyChanged event call the OnLoad (#1).
  4. Within the loaded event I check for this.ActualWidth to be set (non zero) along with whether all of my dependent properties are valid. (If they are all not set and also my boolean global flag states that it hasn't been loaded yet; it does nothing and exits (waiting a subsequent call).
  5. Once my load event detects all dependent properties have been set and ActualWidth is not zero, it then begins the process of using the width and the dependent properties to begin to create my sub controls.

HTH

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122