title: Why child control ignores the size set with Arrange in parent container?
body: <p>I wrote an <code>EditorPanel</code> container, which orders controls in a label/editor order. The complete source of the control follows:</p>
  <pre class="lang-cs prettyprint-override"><code>public class EditorPanel : Panel
  {
      private enum GeneralAlignment
      {
          Begin,
          Center,
          End,
          Stretch
      }
  
      private static GeneralAlignment ToGeneralAlignment(VerticalAlignment verticalAlignment)
      {
          switch (verticalAlignment)
          {
              case VerticalAlignment.Top:
                  return GeneralAlignment.Begin;                    
              case VerticalAlignment.Center:
                  return GeneralAlignment.Center;
              case VerticalAlignment.Bottom:
                  return GeneralAlignment.End;
              case VerticalAlignment.Stretch:
                  return GeneralAlignment.Stretch;
              default:
                  throw new InvalidEnumArgumentException(&quot;Unsupported vertical alignment!&quot;);
          }
      }
  
      private static GeneralAlignment ToGeneralAlignment(HorizontalAlignment horizontalAlignment)
      {
          switch (horizontalAlignment)
          {
              case HorizontalAlignment.Left:
                  return GeneralAlignment.Begin;
              case HorizontalAlignment.Center:
                  return GeneralAlignment.Center;
              case HorizontalAlignment.Right:
                  return GeneralAlignment.End;
              case HorizontalAlignment.Stretch:
                  return GeneralAlignment.Stretch;
              default:
                  throw new InvalidEnumArgumentException(&quot;Unsupported horizontal alignment!&quot;);
          }
      }
  
      private Size DesiredSizeWithMargin(UIElement element)
      {
          if (element == null)
              return Size.Empty;
  
          if (element is FrameworkElement frameworkElement)
              return new Size(frameworkElement.DesiredSize.Width + frameworkElement.Margin.Left + frameworkElement.Margin.Right,
                frameworkElement.DesiredSize.Height + frameworkElement.Margin.Top + frameworkElement.Margin.Bottom);
          else
              return element.DesiredSize;
      }
  
      private static (double elementStart, double elementSize) EvalPlacement(UIElement element,
          double placementRectStart,
          double placementRectSize,
          double elementMarginBegin,
          double elementMarginEnd,
          double elementDesiredSize,
          GeneralAlignment elementAlignment)
      {
          double resultSize;
          double resultStart;
  
          switch (elementAlignment)
          {
              case GeneralAlignment.Begin:
                  resultSize = Math.Max(0, Math.Min(elementDesiredSize, placementRectSize - (elementMarginBegin + elementMarginEnd)));
                  resultStart = placementRectStart + elementMarginBegin;
                  break;
  
              case GeneralAlignment.Center:
                  resultSize = Math.Max(0, Math.Min(elementDesiredSize, placementRectSize - (elementMarginBegin + elementMarginEnd)));
                  resultStart = placementRectStart + (placementRectSize - (resultSize + elementMarginBegin + elementMarginEnd)) / 2 + elementMarginBegin;
                  break;
  
              case GeneralAlignment.End:
                  resultSize = Math.Max(0, Math.Min(elementDesiredSize, placementRectSize - (elementMarginBegin + elementMarginEnd)));
                  resultStart = placementRectStart + placementRectSize - elementMarginEnd - resultSize;
                  break;
  
              case GeneralAlignment.Stretch:
                  resultSize = Math.Max(0, placementRectSize - (elementMarginBegin + elementMarginEnd));
                  resultStart = placementRectStart + elementMarginBegin;
                  break;
  
              default:
                  throw new InvalidEnumArgumentException(&quot;Unsupported alignment!&quot;);
          }
  
          return (resultStart, resultSize);
      }
  
  
      private void ArrangeWithAlignment(UIElement element, Rect placementRect, Size cachedDesiredSize)
      {
          if (cachedDesiredSize == Size.Empty)
              cachedDesiredSize = DesiredSizeWithMargin(element);
  
          Thickness elementMargin = new Thickness();
          HorizontalAlignment elementHorizontalAlignment = HorizontalAlignment.Stretch;
          VerticalAlignment elementVerticalAlignment = VerticalAlignment.Top;
  
          if (element is FrameworkElement frameworkElement)
          {
              elementMargin = frameworkElement.Margin;
              elementHorizontalAlignment = frameworkElement.HorizontalAlignment;
              elementVerticalAlignment = frameworkElement.VerticalAlignment;
          }
  
          (double elementTop, double elementHeight) = EvalPlacement(element, 
              placementRect.Top, 
              placementRect.Height, 
              elementMargin.Top, 
              elementMargin.Bottom, 
              cachedDesiredSize.Height,
              ToGeneralAlignment(elementVerticalAlignment));
  
          (double elementLeft, double elementWidth) = EvalPlacement(element,
              placementRect.Left,
              placementRect.Width,
              elementMargin.Left,
              elementMargin.Right,
              cachedDesiredSize.Width,
              ToGeneralAlignment(elementHorizontalAlignment));
         
          element.Arrange(new Rect(elementLeft, elementTop, elementWidth, elementHeight));
      }
  
      protected override Size MeasureOverride(Size availableSize)
      {
          double maxLabelWidth = 0.0;
          double maxEditorWidth = 0.0;
          double totalLabelEditorPairHeight = 0.0;
  
          for (int i = 0; i &lt; InternalChildren.Count; i += 2)
          {
              // Measure label
              InternalChildren[i].Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
              Size labelDesiredSize = DesiredSizeWithMargin(InternalChildren[i]);
  
              // Measure editor (if any)
              Size editorDesiredSize = Size.Empty;
              if (i + 1 &lt; InternalChildren.Count)
              {
                  InternalChildren[i + 1].Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                  editorDesiredSize = DesiredSizeWithMargin(InternalChildren[i + 1]);
              }
  
              maxLabelWidth = Math.Max(maxLabelWidth, labelDesiredSize.Width);
              maxEditorWidth = Math.Max(maxEditorWidth, editorDesiredSize.Width);
              totalLabelEditorPairHeight += Math.Max(labelDesiredSize.Height, editorDesiredSize.Height);
          }
  
          // This is required height, regardless of how much space is available
          double resultHeight = totalLabelEditorPairHeight;
  
          // If space is not constrained, pick as much as labels &amp; editors want. Else, use
          // as much, as is given.
          double resultWidth = double.IsInfinity(availableSize.Width) ? maxLabelWidth + maxEditorWidth : availableSize.Width;
  
          return new Size(resultWidth, resultHeight);
      }
  
      protected override Size ArrangeOverride(Size finalSize)
      {
          // Label area width
  
          double labelAreaWidth = 0;
          for (int i = 0; i &lt; InternalChildren.Count; i += 2)
              labelAreaWidth = Math.Max(labelAreaWidth, DesiredSizeWithMargin(InternalChildren[i]).Width);
  
          labelAreaWidth = Math.Min(labelAreaWidth, finalSize.Width);
  
          // Editor area width
  
          double editorAreaWidth = Math.Max(0, finalSize.Width - labelAreaWidth);
  
          // Arranging controls
  
          double y = 0;
          int controlIndex = 0;
  
          while (controlIndex &lt; InternalChildren.Count)
          {
              // Retrieve label and editor
  
              UIElement label = InternalChildren[controlIndex++];
              Size labelDesiredSize = DesiredSizeWithMargin(label);
  
              UIElement editor = (controlIndex &lt; InternalChildren.Count) ? InternalChildren[controlIndex++] : null;
              Size editorDesiredSize = DesiredSizeWithMargin(editor);                
  
              double rowHeight = Math.Max(labelDesiredSize.Height, editorDesiredSize.Height);
  
              var labelArea = new Rect(0, y, labelAreaWidth, rowHeight);
              ArrangeWithAlignment(label, labelArea, label.DesiredSize);
  
              // Arrange editor
  
              if (editor != null)
              {
                  var editorArea = new Rect(labelAreaWidth, y, editorAreaWidth, rowHeight);
                  ArrangeWithAlignment(editor, editorArea, editor.DesiredSize);
              }
  
              y += Math.Max(labelDesiredSize.Height, editorDesiredSize.Height);
          }
  
          return finalSize;
      }
  }
  </code></pre>
  <p>Example of usage may look like following:</p>
  <pre class="lang-xml prettyprint-override"><code>&lt;Border BorderBrush=&quot;Black&quot; BorderThickness=&quot;1&quot;
          Width=&quot;400&quot; Height=&quot;100&quot;&gt;
      &lt;controls:EditorPanel&gt;
          &lt;Label&gt;First label&lt;/Label&gt;
          &lt;TextBox /&gt;
          &lt;Label&gt;Second label&lt;/Label&gt;
          &lt;TextBox /&gt;
      &lt;/controls:EditorPanel&gt;
  &lt;/Border&gt;
  </code></pre>
  <p>When I run the application, it looks like following:</p>
  <p><a href="https://i.stack.imgur.com/FPmem.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/FPmem.png" alt="Editor Panel" /></a></p>
  <p>However, if I write more text in the textbox, it starts to escape the boundaries set by the EditorPanel itself.</p>
  <p><a href="https://i.stack.imgur.com/XwhMc.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/XwhMc.png" alt="Editor Panel - broken" /></a></p>
  <p>What is interesting is that the container seems to work properly and allocates proper amount of space for the control:</p>
  <p><a href="https://i.stack.imgur.com/xxyLD.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/xxyLD.png" alt="Allocated space" /></a></p>
  <p>But the control seems to ignore it completely and ends up bigger than it actually should be:</p>
  <p><a href="https://i.stack.imgur.com/Rzw9n.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Rzw9n.png" alt="Too big control" /></a></p>
  <p>That leads to my question: why the child control ignores the space it was given through the Arrange call?</p>