2014-09-24 42 views
0

我需要什麼:列表框與文本框裏面,文本框環繞,以及最後行罷了,剩餘空間:WrapPanel憑藉最後行內填寫

|word 1||word 2___| 
|word 3___________| 

我試圖實現使用that advice此行爲。我的XAML:

<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
     ItemsSource="{Binding Tags}" 
     HorizontalContentAlignment="Stretch"> 
    <ListBox.ItemsPanel> 
     <ItemsPanelTemplate> 
      <controls:WrapPanelLastChildFill /> 
     </ItemsPanelTemplate> 
    </ListBox.ItemsPanel> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <TextBox Text="{Binding Text}"/> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

MyWrapPanel(繼承形成WrapPanel)代碼:

protected override Size MeasureOverride(Size constraint) 
{ 
    Size curLineSize = new Size(); 
    Size panelSize = new Size(constraint.Width, 0); 
    UIElementCollection children = base.InternalChildren; 

    for (int i = 0; i < children.Count; i++) 
    { 
     UIElement child = children[i] as UIElement; 

     child.Measure(constraint); 
     Size sz = child.DesiredSize; 
     if (curLineSize.Width + sz.Width > constraint.Width) // new line 
     { 
      panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width); 
      panelSize.Height += curLineSize.Height; 
      if (i > 0) 
      { 
       // change width of prev control here 
       var lastChildInRow = children[i - 1] as Control; 
       lastChildInRow.Width = lastChildInRow.ActualWidth + panelSize.Width - curLineSize.Width; 
      } 
      curLineSize = sz; 
     } 
     else 
     { 
      curLineSize.Width += sz.Width; 
      curLineSize.Height = Math.Max(sz.Height, curLineSize.Height); 
     } 
    } 
    panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width); 
    panelSize.Height += curLineSize.Height; 

    return panelSize; 
} 

那是工作,但只有一邊 - 文本框寬度永遠不會收縮。

任何幫助表示讚賞。

回答

0

這是誤解寬度校正的地方。這必須在ArrangeOverride:

private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end) 
{ 
    double x = 0; 
    UIElementCollection children = InternalChildren; 
    for (int i = start; i < end; i++) 
    { 
     UIElement child = children[i]; 
     var w = child.DesiredSize.Width; 
     if (LastChildFill && i == end - 1) // last сhild fills remaining space 
     { 
      w = boundsWidth - x; 
     } 
     child.Arrange(new Rect(x, y, w, lineSize.Height)); 
     x += w; 
    } 
} 
+0

它是如何運作的,你可以發佈完整的用法? – 2016-12-03 01:06:47

+1

你好,你可以看到來源:https://bitbucket.org/smg/diagnosis/src/40ca71f6bde01a8b5cad17169fd33b85994eec8a/Diagnosis.Common.Presentation/Controls/Common/WrapPanelLastChildFill.cs – smg 2016-12-03 10:16:02

0

我最近做了它。 您可以看到我的代碼:CodeProject 或直接使用這個類,如下所示:

用法:

<TextBox MinWidth="120" wrapPanelWithFill:WrapPanelFill.UseToFill="True">*</TextBox> 

代碼:

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; 
       } 
      } 
     } 

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

    } 
}