我最终结合了丹尼尔斯的答案和海纳的答案。我决定发布整个解决方案,以便人们在需要时更容易采用它。这是我的装饰类:
public class RestrictDesiredSizeDecorator : Decorator
{
public static readonly DependencyProperty KeepWidth;
public static readonly DependencyProperty KeepHeight;
#region Dependency property setters and getters
public static void SetKeepWidth(UIElement element, bool value)
{
element.SetValue(KeepWidth, value);
}
public static bool GetKeepWidth(UIElement element)
{
return (bool)element.GetValue(KeepWidth);
}
public static void SetKeepHeight(UIElement element, bool value)
{
element.SetValue(KeepHeight, value);
}
public static bool GetKeepHeight(UIElement element)
{
return (bool)element.GetValue(KeepHeight);
}
#endregion
private Size _lastArrangeSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
static RestrictDesiredSizeDecorator()
{
KeepWidth = DependencyProperty.RegisterAttached(
nameof(KeepWidth),
typeof(bool),
typeof(RestrictDesiredSizeDecorator));
KeepHeight = DependencyProperty.RegisterAttached(
nameof(KeepHeight),
typeof(bool),
typeof(RestrictDesiredSizeDecorator));
}
protected override Size MeasureOverride(Size constraint)
{
Debug.WriteLine("Measure: " + constraint);
var keepWidth = GetValue(KeepWidth) as bool? ?? false;
var keepHeight = GetValue(KeepHeight) as bool? ?? false;
var innerWidth = keepWidth ? constraint.Width : Math.Min(this._lastArrangeSize.Width, constraint.Width);
var innerHeight = keepHeight ? constraint.Height : Math.Min(this._lastArrangeSize.Height, constraint.Height);
base.MeasureOverride(new Size(innerWidth, innerHeight));
var outerWidth = keepWidth ? Child.DesiredSize.Width : 0;
var outerHeight = keepHeight ? Child.DesiredSize.Height : 0;
return new Size(outerWidth, outerHeight);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
Debug.WriteLine("Arrange: " + arrangeSize);
if (_lastArrangeSize != arrangeSize)
{
_lastArrangeSize = arrangeSize;
base.MeasureOverride(arrangeSize);
}
return base.ArrangeOverride(arrangeSize);
}
}
这是我在 xaml 中使用它的方式:
<ScrollViewer>
<StackPanel Orientation="Vertical">
<Whatever />
<decorators:RestrictDesiredSizeDecorator MinWidth="100" KeepHeight="True">
<TextBox
Text="{Binding Comment, UpdateSourceTrigger=PropertyChanged}"
Height="Auto"
MaxHeight="360"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
AcceptsReturn="True"
AcceptsTab="True"
TextWrapping="WrapWithOverflow"
/>
</decorators:RestrictDesiredSizeDecorator>
<Whatever />
</StackPanel>
</ScrollViewer
上面创建了一个文本框,它将垂直增长(直到达到 MaxHeight),但将匹配父级的宽度而不增长外部 ScrollViewer。将窗口/ScrollViewer 调整为小于 100 宽将强制外部 ScrollViewer 显示水平滚动条。也可以使用其他带有内部 ScrollViewers 的控件,包括复杂的网格。