wpf - Binding issue ActualWidth

4.2k views Asked by At

I have the following XAML code:

<Grid Grid.Row="2" Name="grid_StatusBar">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="{Binding ElementName=wrapPanel, Path=ActualWidth}" />
        <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>
    <ProgressBar Grid.Column="0" HorizontalAlignment="Stretch" Margin="5,5,5,5" Name="progressBar1" VerticalAlignment="Stretch" />
    <WrapPanel Grid.Column="1" Name="wrapPanel" HorizontalAlignment="Right">
        <Label Content="1" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="label_Dataset" VerticalAlignment="Stretch" Visibility="Collapsed" />
        <Label Content="/20" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="label_TotalDatasets" VerticalAlignment="Stretch" Visibility="Collapsed" />
        <Label Content="ID:" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="label_IDText" VerticalAlignment="Stretch" />
        <Label Content="no id" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="labelID" VerticalAlignment="Stretch" />
    </WrapPanel>
    <Button Grid.Column="2" Name="button_Help" Height="30" Width="30" Content="?" HorizontalAlignment="Right" VerticalAlignment="Stretch" Click="button_Help_Click" >
</Grid>

What i am trying to show the progressbar as wide as possible while changing the visibility of some of the labels and setting different texts (and therefore different lengths/widths.

I have then different functions:

  • At program start the "no id" text of the labelID Label is replaced with an internal value. The width is updated: OK
  • With the code running I change the visibility of the first two labels, and the ColumnDefinition.Width is not updated and only the first two labels are shown (because there is not place enough for all the 4 of them to fit in the wrapPanel ActualWidth: ERROR!
  • If I change the Visibility property from the first two Labels from Collapsed to Visible, from start the visibility of all the Labels are Visible, and all the Labels are shown: OK
  • From the previous state I change visibility of the first two Labels to Collapsed, the ColumnDefinition.Width is updated and the ProgressBar is as wide as possible and all the text is shown: OK

Could anyone please help me? I do not understand why the width is not updated...

NOTE: The visibility is changed using two buttons in the window running the following code:

label_Dataset.Visibility = System.Windows.Visibility.Visible;
label_TotalDatasets.Visibility = System.Windows.Visibility.Visible;

EDIT: My target is to show the Visible Labels using the minimum space (in one line) in order to have the ProgressBar using the maximum width possible.

1

There are 1 answers

2
Peter Holmes On BEST ANSWER

It is not possible to change the width of a parent element based on the width of a child. In your case, the column is the parent of the WrapPanel, so the ActualWidth of the WrapPanel is not available until after the grid/column has already been sized.

Even if you wrote code to try and circumvent this, you would still run into an issue, as the measure/layout sequence would become re-entrant. Whenever the size of the column changed, it would re-layout all the child controls, one of which is the WrapPanel. This would cause the ActualWidth of the WrapPanel to be changed -- effectively causing an infinite loop. To prevent this stack overflow scenario, the framework immediately detects any re-entrancy in the layout/measure cycle and throws an exception.

I'm not exactly sure what you are trying to achieve. Why do would you want the Labels in a WrapPanel? Surely you would always want the 4 labels to be on the same line, or at least on two lines.

If this is the case, I would set the width of the second Column to "Auto" and put the labels in another container, i.e. one of the following:

  • If you want all the labels to wrap, as in a WrapPanel being forced to its minimum width, use:

    <StackPanel Grid.Column="1" Orientation="Vertical">
        ... your labels here ...
    </StackPanel>
    
  • If you want all the labels to stay on the same line, use:

    <StackPanel Grid.Column="1" Orientation="Horizontal">
        ... your labels here ...
    </StackPanel>
    
  • If you want two lines use:

    <StackPanel Grid.Column="1" Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            ... 1st 2 labels here  ...
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            ... 2nd 2 labels here  ...
        </StackPanel>
    </StackPanel>