请参阅我所附的示例代码:
<StackPanel Width="300">
<StackPanel Orientation="Horizontal">
<Label Content="Label" Width="100" />
<TextBox Text="Content" />
</StackPanel>
</StackPanel>
如何进行TextBox
拉伸以水平填充所有剩余空间? HorizontalAlignment
不能按我期望的方式工作。
内部StackPanel
将是UserControl
,所以我不能只用StackPanel
替换两个Grid
。
答案 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
}
}