2014-01-08 6 views
0

У меня есть набор usercontrols в форме ввода данных. Чтобы максимально использовать пространство, эти пользовательские элементы управления помещаются в оболочку, которая расширяется вертикально. Моя форма работает хорошо, но визуально она не выглядит полированной. Я печатаю на мобильном устройстве, поэтому не могу опубликовать изображение, но позвольте мне описать его.WrapPanel с LastChildFill

Скажем, что форма имеет 3 пользовательских контроля демографии (ширина 800), телефон #s (ширина 300), адреса (ширина 600). Сама форма имеет ширину 1000 пикселей.

Теперь, когда эти элементы управления расположены (в указанном выше порядке), демография будет находиться в верхней строке, а остальные два пользовательских элемента управления будут в строке ниже.

Но управление демографией будет занимать только 800 пикселей из доступных 1000 пикселей в строке. Аналогично, нижняя строка будет занимать только 900 пикселей. Это делает форму уродливой.

Мне нужно, чтобы последний ребенок в каждом ряду растягивал свой ограничивающий бикс (то есть его границу), чтобы занять оставшееся пространство. Поэтому в этом случае граница демографии будет растягиваться до 1000 пикселей, а граница адресов будет растягиваться до 700 пикселей.

коллекция usercontrol является динамической. Пользователь может создавать новые формы «на лету» и изменять элементы и их порядок.

Если размер экрана изменяется, растянутые элементы восстанавливаются до их первоначальной ширины, панель обложки перестраивает элементы, а последний ребенок в каждой строке снова заполняет оставшееся пространство.

Надеюсь, я смог правильно объяснить ситуацию. Спасибо за ваше время.

+0

Вы имеете в виду там, а может быть только одна строки с последним элементом, заполняющим его, или три рядом, где каждый элемент заполняет всю строку? – Clemens

+0

И это Silverlight или WPF? Это не может быть и то, и другое. – Clemens

+0

В описанном выше сценарии есть две строки. Строка 1 содержит демографические данные, а строка2 содержит телефон и адреса, если оболочка имеет ширину 1000 пикселей, но говорят, что панель уменьшилась до 800 пикселей, тогда будет 3 строки. Один ели каждый пользовательский контроль. В этом случае пользовательский контроль демографии уже имеет такую ​​же ширину, как и обертка, но два других пользовательских элемента управления должны быть растянуты до 800. Это для серебра. Спасибо – user559788

ответ

1

Закончено создание собственной пользовательской панели. В MeasureOverride, jyst перед созданием новой строки, вернитесь к последнему обработанному элементу управления и установите его длину в control.length + (maximumSize- lineSize).

Это похоже на работу

+0

Я предполагаю, что ваша пользовательская панель наследуется от WrapPanel? Кажется, что классная функциональность, которую MS легко может реализовать, с ее подходящей DependencyProperty ... – almulo

0

Я сделал для этого контроль. Я опубликовал его по адресу: CodeProject.

Использование:

<wrapPanelWithFill:WrapPanelFill Grid.Row="3" Grid.Column="1"> 
     <TextBlock Text="Path: " TextWrapping="Wrap"></TextBlock> 
     <TextBox MinWidth="120" wrapPanelWithFill:WrapPanelFill.UseToFill="True">*</TextBox> 
     <Button>Browse...</Button> 
</wrapPanelWithFill:WrapPanelFill> 

Код:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Windows; 
using System.Windows.Controls; 

namespace WrapPanelWithFill 
{ 
    public class WrapPanelFill : WrapPanel 
    { 
     // ****************************************************************** 
     public static readonly DependencyProperty UseToFillProperty = DependencyProperty.RegisterAttached("UseToFill", typeof(Boolean), 
      typeof(WrapPanelFill), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)); 

     // ****************************************************************** 
     public static void SetUseToFill(UIElement element, Boolean value) 
     { 
      element.SetValue(UseToFillProperty, value); 
     } 
     // ****************************************************************** 
     public static Boolean GetUseToFill(UIElement element) 
     { 
      return (Boolean)element.GetValue(UseToFillProperty); 
     } 

     // ****************************************************************** 
     const double DBL_EPSILON = 2.2204460492503131e-016; /* smallest such that 1.0+DBL_EPSILON != 1.0 */ 

     // ****************************************************************** 
     private static bool DoubleAreClose(double value1, double value2) 
     { 
      //in case they are Infinities (then epsilon check does not work) 
      if (value1 == value2) return true; 
      // This computes (|value1-value2|/(|value1| + |value2| + 10.0)) < DBL_EPSILON 
      double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON; 
      double delta = value1 - value2; 
      return (-eps < delta) && (eps > delta); 
     } 

     // ****************************************************************** 
     private static bool DoubleGreaterThan(double value1, double value2) 
     { 
      return (value1 > value2) && !DoubleAreClose(value1, value2); 
     } 

     // ****************************************************************** 
     private bool _atLeastOneElementCanHasItsWidthExpanded = false; 

     // ****************************************************************** 
     /// <summary> 
     /// <see cref="FrameworkElement.MeasureOverride"/> 
     /// </summary> 
     protected override Size MeasureOverride(Size constraint) 
     { 
      UVSize curLineSize = new UVSize(Orientation); 
      UVSize panelSize = new UVSize(Orientation); 
      UVSize uvConstraint = new UVSize(Orientation, constraint.Width, constraint.Height); 
      double itemWidth = ItemWidth; 
      double itemHeight = ItemHeight; 
      bool itemWidthSet = !Double.IsNaN(itemWidth); 
      bool itemHeightSet = !Double.IsNaN(itemHeight); 

      Size childConstraint = new Size(
       (itemWidthSet ? itemWidth : constraint.Width), 
       (itemHeightSet ? itemHeight : constraint.Height)); 

      UIElementCollection children = InternalChildren; 

      // EO 
      LineInfo currentLineInfo = new LineInfo(); // EO, the way it works it is always like we are on the current line 
      _lineInfos.Clear(); 
      _atLeastOneElementCanHasItsWidthExpanded = false; 

      for (int i = 0, count = children.Count; i < count; i++) 
      { 
       UIElement child = children[i] as UIElement; 
       if (child == null) continue; 

       //Flow passes its own constrint to children 
       child.Measure(childConstraint); 

       //this is the size of the child in UV space 
       UVSize sz = new UVSize(
        Orientation, 
        (itemWidthSet ? itemWidth : child.DesiredSize.Width), 
        (itemHeightSet ? itemHeight : child.DesiredSize.Height)); 

       if (DoubleGreaterThan(curLineSize.U + sz.U, uvConstraint.U)) //need to switch to another line 
       { 
        // EO 
        currentLineInfo.Size = curLineSize; 
        _lineInfos.Add(currentLineInfo); 

        panelSize.U = Math.Max(curLineSize.U, panelSize.U); 
        panelSize.V += curLineSize.V; 
        curLineSize = sz; 

        // EO 
        currentLineInfo = new LineInfo(); 
        var feChild = child as FrameworkElement; 
        if (GetUseToFill(feChild)) 
        { 
         currentLineInfo.ElementsWithNoWidthSet.Add(feChild); 
         _atLeastOneElementCanHasItsWidthExpanded = true; 
        } 

        if (DoubleGreaterThan(sz.U, uvConstraint.U)) //the element is wider then the constrint - give it a separate line 
        { 
         currentLineInfo = new LineInfo(); 

         panelSize.U = Math.Max(sz.U, panelSize.U); 
         panelSize.V += sz.V; 
         curLineSize = new UVSize(Orientation); 
        } 
       } 
       else //continue to accumulate a line 
       { 
        curLineSize.U += sz.U; 
        curLineSize.V = Math.Max(sz.V, curLineSize.V); 

        // EO 
        var feChild = child as FrameworkElement; 
        if (GetUseToFill(feChild)) 
        { 
         currentLineInfo.ElementsWithNoWidthSet.Add(feChild); 
         _atLeastOneElementCanHasItsWidthExpanded = true; 
        } 
       } 
      } 

      if (curLineSize.U > 0) 
      { 
       currentLineInfo.Size = curLineSize; 
       _lineInfos.Add(currentLineInfo); 
      } 

      //the last line size, if any should be added 
      panelSize.U = Math.Max(curLineSize.U, panelSize.U); 
      panelSize.V += curLineSize.V; 

      // EO 
      if (_atLeastOneElementCanHasItsWidthExpanded) 
      { 
       return new Size(constraint.Width, panelSize.Height); 
      } 

      //go from UV space to W/H space 
      return new Size(panelSize.Width, panelSize.Height); 
     } 

     // ************************************************************************ 
     private struct UVSize 
     { 
      internal UVSize(Orientation orientation, double width, double height) 
      { 
       U = V = 0d; 
       _orientation = orientation; 
       Width = width; 
       Height = height; 
      } 

      internal UVSize(Orientation orientation) 
      { 
       U = V = 0d; 
       _orientation = orientation; 
      } 

      internal double U; 
      internal double V; 
      private Orientation _orientation; 

      internal double Width 
      { 
       get { return (_orientation == Orientation.Horizontal ? U : V); } 
       set { if (_orientation == Orientation.Horizontal) U = value; else V = value; } 
      } 
      internal double Height 
      { 
       get { return (_orientation == Orientation.Horizontal ? V : U); } 
       set { if (_orientation == Orientation.Horizontal) V = value; else U = value; } 
      } 
     } 

     // ************************************************************************ 
     private class LineInfo 
     { 
      public List<UIElement> ElementsWithNoWidthSet = new List<UIElement>(); 
      //   public double SpaceLeft = 0; 
      //   public double WidthCorrectionPerElement = 0; 
      public UVSize Size; 
      public double Correction = 0; 
     } 

     private List<LineInfo> _lineInfos = new List<LineInfo>(); 

     // ************************************************************************ 
     /// <summary> 
     /// <see cref="FrameworkElement.ArrangeOverride"/> 
     /// </summary> 
     protected override Size ArrangeOverride(Size finalSize) 
     { 
      int lineIndex = 0; 
      int firstInLine = 0; 
      double itemWidth = ItemWidth; 
      double itemHeight = ItemHeight; 
      double accumulatedV = 0; 
      double itemU = (Orientation == Orientation.Horizontal ? itemWidth : itemHeight); 
      UVSize curLineSize = new UVSize(Orientation); 
      UVSize uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height); 
      bool itemWidthSet = !Double.IsNaN(itemWidth); 
      bool itemHeightSet = !Double.IsNaN(itemHeight); 
      bool useItemU = (Orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet); 

      UIElementCollection children = InternalChildren; 

      for (int i = 0, count = children.Count; i < count; i++) 
      { 
       UIElement child = children[i] as UIElement; 
       if (child == null) continue; 

       UVSize sz = new UVSize(
        Orientation, 
        (itemWidthSet ? itemWidth : child.DesiredSize.Width), 
        (itemHeightSet ? itemHeight : child.DesiredSize.Height)); 

       if (DoubleGreaterThan(curLineSize.U + sz.U, uvFinalSize.U)) //need to switch to another line 
       { 
        arrangeLine(lineIndex, accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU, uvFinalSize); 
        lineIndex++; 

        accumulatedV += curLineSize.V; 
        curLineSize = sz; 

        if (DoubleGreaterThan(sz.U, uvFinalSize.U)) //the element is wider then the constraint - give it a separate line 
        { 
         //switch to next line which only contain one element 
         arrangeLine(lineIndex, accumulatedV, sz.V, i, ++i, useItemU, itemU, uvFinalSize); 

         accumulatedV += sz.V; 
         curLineSize = new UVSize(Orientation); 
        } 
        firstInLine = i; 
       } 
       else //continue to accumulate a line 
       { 
        curLineSize.U += sz.U; 
        curLineSize.V = Math.Max(sz.V, curLineSize.V); 
       } 
      } 

      //arrange the last line, if any 
      if (firstInLine < children.Count) 
      { 
       arrangeLine(lineIndex, accumulatedV, curLineSize.V, firstInLine, children.Count, useItemU, itemU, uvFinalSize); 
      } 

      return finalSize; 
     } 

     // ************************************************************************ 
     private void arrangeLine(int lineIndex, double v, double lineV, int start, int end, bool useItemU, double itemU, UVSize uvFinalSize) 
     { 
      double u = 0; 
      bool isHorizontal = (Orientation == Orientation.Horizontal); 

      Debug.Assert(lineIndex < _lineInfos.Count); 

      LineInfo lineInfo = _lineInfos[lineIndex]; 
      double lineSpaceAvailableForCorrection = Math.Max(uvFinalSize.U - lineInfo.Size.U, 0); 
      double perControlCorrection = 0; 
      if (lineSpaceAvailableForCorrection > 0 && lineInfo.Size.U > 0) 
      { 
       perControlCorrection = lineSpaceAvailableForCorrection/lineInfo.ElementsWithNoWidthSet.Count; 
       if (double.IsInfinity(perControlCorrection)) 
       { 
        perControlCorrection = 0; 
       } 
      } 
      int indexOfControlToAdjustSizeToFill = 0; 
      UIElement uIElementToAdjustNext = indexOfControlToAdjustSizeToFill < lineInfo.ElementsWithNoWidthSet.Count ? lineInfo.ElementsWithNoWidthSet[indexOfControlToAdjustSizeToFill] : null; 

      UIElementCollection children = InternalChildren; 
      for (int i = start; i < end; i++) 
      { 
       UIElement child = children[i] as UIElement; 
       if (child != null) 
       { 
        UVSize childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); 
        double layoutSlotU = (useItemU ? itemU : childSize.U); 

        if (perControlCorrection > 0 && child == uIElementToAdjustNext) 
        { 
         layoutSlotU += perControlCorrection; 

         indexOfControlToAdjustSizeToFill++; 
         uIElementToAdjustNext = indexOfControlToAdjustSizeToFill < lineInfo.ElementsWithNoWidthSet.Count ? lineInfo.ElementsWithNoWidthSet[indexOfControlToAdjustSizeToFill] : null; 
        } 

        child.Arrange(new Rect(
         (isHorizontal ? u : v), 
         (isHorizontal ? v : u), 
         (isHorizontal ? layoutSlotU : lineV), 
         (isHorizontal ? lineV : layoutSlotU))); 
        u += layoutSlotU; 
       } 
      } 
     } 

     // ************************************************************************ 

    } 
} 
+0

Nice. Код выглядит намного чище, чем у меня :) Я сейчас переношу код в приложение Windows (из Silverlight). Я отдам это, когда я доберусь до части пользовательского интерфейса. благодаря – user559788

Смежные вопросы