TextBox不在Stackpanel内伸展

时间:2019-04-27 17:28:17

标签: wpf

请参阅我所附的示例代码:

<StackPanel Width="300">
    <StackPanel Orientation="Horizontal">
        <Label Content="Label" Width="100" />
        <TextBox Text="Content" />
    </StackPanel>
</StackPanel>

如何进行TextBox拉伸以水平填充所有剩余空间? HorizontalAlignment不能按我期望的方式工作。

内部StackPanel将是UserControl,所以我不能只用StackPanel替换两个Grid

3 个答案:

答案 0 :(得分:0)

可以使用StackPanel代替内部DockPanel

<StackPanel Width="300">
    <DockPanel>
        <Label Content="Label" Width="100" />
        <TextBox Text="Content" />
    </DockPanel>
</StackPanel>

答案 1 :(得分:0)

我建议使用DockPanel作为UserControl的根控件,以便在其子控件中获得所需的行为。但是,如果您绝对需要它基于StackPanel,则只需使用后面的代码即可动态设置文本框宽度。

Xaml:

    <StackPanel Name="stpnlOuterControl" Width="300" SizeChanged="StackPanel_SizeChanged">
        <StackPanel Orientation="Horizontal">
            <Label Content="Test" Width="100"/>
            <TextBox Name="txtbxTest" Text="Content"/>
        </StackPanel>
    </StackPanel>

后面的代码:

Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)

    txtbxTest.Width = stpnlOuterControl.ActualWidth - 100


End Sub

Private Sub StackPanel_SizeChanged(sender As Object, e As SizeChangedEventArgs)

    txtbxTest.Width = stpnlOuterControl.ActualWidth - 100

End Sub

答案 2 :(得分:0)

替代方法是使用自定义StackPanel https://github.com/SpicyTaco/SpicyTaco.AutoGrid/blob/master/src/AutoGrid/StackPanel.cs

MainWindow

    <Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:System="clr-namespace:System;assembly=mscorlib"
        xmlns:VM="clr-namespace:WpfApplication4" >   

 <StackPanel Background="Red" Width="300">
    <VM:StackPanel   Orientation="Horizontal">
        <Label Background="Blue" Content="Label" Width="100" />
        <TextBox  VM:StackPanel.Fill="Fill"    Text="Content" />
    </VM:StackPanel>
</StackPanel>

</Window>

SpicyTaco的“自定义堆栈”面板的代码

 using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;

    namespace WpfApplication4
    {
        public class StackPanel : Panel
        {
            protected override Size MeasureOverride(Size constraint)
            {
                UIElementCollection children = InternalChildren;

                double parentWidth = 0;
                double parentHeight = 0;
                double accumulatedWidth = 0;
                double accumulatedHeight = 0;

                var isHorizontal = Orientation == Orientation.Horizontal;
                var totalMarginToAdd = CalculateTotalMarginToAdd(children, MarginBetweenChildren);

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

                    if (child == null) { continue; }

                    // Handle only the Auto's first to calculate remaining space for Fill's
                    if (GetFill(child) != StackPanelFill.Auto) { continue; }

                    // Child constraint is the remaining size; this is total size minus size consumed by previous children.
                    var childConstraint = new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
                                                   Math.Max(0.0, constraint.Height - accumulatedHeight));

                    // Measure child.
                    child.Measure(childConstraint);
                    var childDesiredSize = child.DesiredSize;

                    if (isHorizontal)
                    {
                        accumulatedWidth += childDesiredSize.Width;
                        parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height);
                    }
                    else
                    {
                        parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width);
                        accumulatedHeight += childDesiredSize.Height;
                    }
                }

                // Add all margin to accumulated size before calculating remaining space for
                // Fill elements.
                if (isHorizontal)
                {
                    accumulatedWidth += totalMarginToAdd;
                }
                else
                {
                    accumulatedHeight += totalMarginToAdd;
                }

                var totalCountOfFillTypes = children
                    .OfType<UIElement>()
                    .Count(x => GetFill(x) == StackPanelFill.Fill
                             && x.Visibility != Visibility.Collapsed);

                var availableSpaceRemaining = isHorizontal
                    ? Math.Max(0, constraint.Width - accumulatedWidth)
                    : Math.Max(0, constraint.Height - accumulatedHeight);

                var eachFillTypeSize = totalCountOfFillTypes > 0
                    ? availableSpaceRemaining / totalCountOfFillTypes
                    : 0;

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

                    if (child == null) { continue; }

                    // Handle all the Fill's giving them a portion of the remaining space
                    if (GetFill(child) != StackPanelFill.Fill) { continue; }

                    // Child constraint is the remaining size; this is total size minus size consumed by previous children.
                    var childConstraint = isHorizontal
                        ? new Size(eachFillTypeSize,
                                   Math.Max(0.0, constraint.Height - accumulatedHeight))
                        : new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
                                   eachFillTypeSize);

                    // Measure child.
                    child.Measure(childConstraint);
                    var childDesiredSize = child.DesiredSize;

                    if (isHorizontal)
                    {
                        accumulatedWidth += childDesiredSize.Width;
                        parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height);
                    }
                    else
                    {
                        parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width);
                        accumulatedHeight += childDesiredSize.Height;
                    }
                }

                // Make sure the final accumulated size is reflected in parentSize. 
                parentWidth = Math.Max(parentWidth, accumulatedWidth);
                parentHeight = Math.Max(parentHeight, accumulatedHeight);
                var parent = new Size(parentWidth, parentHeight);

                return parent;
            }

            protected override Size ArrangeOverride(Size arrangeSize)
            {
                UIElementCollection children = InternalChildren;
                int totalChildrenCount = children.Count;

                double accumulatedLeft = 0;
                double accumulatedTop = 0;

                var isHorizontal = Orientation == Orientation.Horizontal;
                var marginBetweenChildren = MarginBetweenChildren;

                var totalMarginToAdd = CalculateTotalMarginToAdd(children, marginBetweenChildren);

                double allAutoSizedSum = 0.0;
                int countOfFillTypes = 0;
                foreach (var child in children.OfType<UIElement>())
                {
                    var fillType = GetFill(child);
                    if (fillType != StackPanelFill.Auto)
                    {
                        if (child.Visibility != Visibility.Collapsed && fillType != StackPanelFill.Ignored)
                            countOfFillTypes += 1;
                    }
                    else
                    {
                        var desiredSize = isHorizontal ? child.DesiredSize.Width : child.DesiredSize.Height;
                        allAutoSizedSum += desiredSize;
                    }
                }

                var remainingForFillTypes = isHorizontal
                    ? Math.Max(0, arrangeSize.Width - allAutoSizedSum - totalMarginToAdd)
                    : Math.Max(0, arrangeSize.Height - allAutoSizedSum - totalMarginToAdd);
                var fillTypeSize = remainingForFillTypes / countOfFillTypes;

                for (int i = 0; i < totalChildrenCount; ++i)
                {
                    UIElement child = children[i];
                    if (child == null) { continue; }
                    Size childDesiredSize = child.DesiredSize;
                    var fillType = GetFill(child);
                    var isCollapsed = child.Visibility == Visibility.Collapsed || fillType == StackPanelFill.Ignored;
                    var isLastChild = i == totalChildrenCount - 1;
                    var marginToAdd = isLastChild || isCollapsed ? 0 : marginBetweenChildren;

                    Rect rcChild = new Rect(
                        accumulatedLeft,
                        accumulatedTop,
                        Math.Max(0.0, arrangeSize.Width - accumulatedLeft),
                        Math.Max(0.0, arrangeSize.Height - accumulatedTop));

                    if (isHorizontal)
                    {
                        rcChild.Width = fillType == StackPanelFill.Auto || isCollapsed ? childDesiredSize.Width : fillTypeSize;
                        rcChild.Height = arrangeSize.Height;
                        accumulatedLeft += rcChild.Width + marginToAdd;
                    }
                    else
                    {
                        rcChild.Width = arrangeSize.Width;
                        rcChild.Height = fillType == StackPanelFill.Auto || isCollapsed ? childDesiredSize.Height : fillTypeSize;
                        accumulatedTop += rcChild.Height + marginToAdd;
                    }

                    child.Arrange(rcChild);
                }

                return arrangeSize;
            }

            static double CalculateTotalMarginToAdd(UIElementCollection children, double marginBetweenChildren)
            {
                var visibleChildrenCount = children
                    .OfType<UIElement>()
                    .Count(x => x.Visibility != Visibility.Collapsed && GetFill(x) != StackPanelFill.Ignored);
                var marginMultiplier = Math.Max(visibleChildrenCount - 1, 0);
                var totalMarginToAdd = marginBetweenChildren * marginMultiplier;
                return totalMarginToAdd;
            }

            public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
                "Orientation", typeof(Orientation), typeof(StackPanel),
                new FrameworkPropertyMetadata(
                    Orientation.Vertical,
                    FrameworkPropertyMetadataOptions.AffectsArrange |
                    FrameworkPropertyMetadataOptions.AffectsMeasure));

            public Orientation Orientation
            {
                get { return (Orientation)GetValue(OrientationProperty); }
                set { SetValue(OrientationProperty, value); }
            }

            public static readonly DependencyProperty MarginBetweenChildrenProperty = DependencyProperty.Register(
                "MarginBetweenChildren", typeof(double), typeof(StackPanel),
                new FrameworkPropertyMetadata(
                    0.0,
                    FrameworkPropertyMetadataOptions.AffectsArrange |
                    FrameworkPropertyMetadataOptions.AffectsMeasure));

            public double MarginBetweenChildren
            {
                get { return (double)GetValue(MarginBetweenChildrenProperty); }
                set { SetValue(MarginBetweenChildrenProperty, value); }
            }

            public static readonly DependencyProperty FillProperty = DependencyProperty.RegisterAttached(
                "Fill", typeof(StackPanelFill), typeof(StackPanel),
                new FrameworkPropertyMetadata(
                    StackPanelFill.Auto,
                    FrameworkPropertyMetadataOptions.AffectsArrange |
                    FrameworkPropertyMetadataOptions.AffectsMeasure |
                    FrameworkPropertyMetadataOptions.AffectsParentArrange |
                    FrameworkPropertyMetadataOptions.AffectsParentMeasure));

            public static void SetFill(DependencyObject element, StackPanelFill value)
            {
                element.SetValue(FillProperty, value);
            }

            public static StackPanelFill GetFill(DependencyObject element)
            {
                return (StackPanelFill)element.GetValue(FillProperty);
            }
        }

        public enum StackPanelFill
        {
            Auto, Fill, Ignored
        }
    }