根据一个控件的大小而非另一个控件来调整窗口大小

3
我有一个包含可变文本的文本块和一个包含可变数量按钮的堆栈面板的窗口。 My Window 当堆栈面板增加宽度时,如何让窗口的宽度(水平)增长,但让文本始终适应可用宽度(当文本变长时不增加窗口的宽度)?
我尝试了一些带有Width="Auto" / VerticalAlign="Stretch" / TextWrapping等的组合,但只有当文本长度/堆栈面板子项之一增加宽度时,才会使窗口增长。
我希望基于堆栈面板的宽度来换行文本。最好只使用XAML。
编辑:
按要求提供了一些简化的标记以重现问题:
<Window 
    Width="500" 
    Height="225" 
    MinWidth="500" 
    MinHeight="225"
    HorizontalAlignment="Stretch"
    SizeToContent="WidthAndHeight"
    ResizeMode="NoResize">

    <Grid HorizontalAlignment="Stretch" Width="Auto">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="35"/>
        </Grid.RowDefinitions>

        <!-- Text should adapt to the available width but not grow the window. Instead it should scroll vertically -->
        <ScrollViewer Grid.Row="0" Margin="15" VerticalScrollBarVisibility="Auto">
            <TextBlock TextWrapping="Wrap" Height="Auto">...Some Binding...</TextBlock>
        </ScrollViewer>

        <!-- Should be as wide as it needs to be, growing the window in width if neccessary -->
        <StackPanel x:Name="ButtonStackPanel" Grid.Row="1" FlowDirection="RightToLeft" Orientation="Horizontal" Width="Auto">
            ...Some Buttons...
        </StackPanel>
    </Grid>
</Window>

编辑2 如果Stackpanel中只有一个按钮,则窗口应该具有最小宽度为500,但仍应完全拉伸(500px)。

编辑3 - 更多图片

正确 - 文本很短,只有一个按钮 -> 最小宽度为500 enter image description here 错误 - 文本很长,应在最小宽度为500时换行,因为只允许Stackpanel增加窗口的宽度 enter image description here 正确 - 短文本但Stackpanel增加了窗口大小 enter image description here


3
请提供一些XAML标记以重现您的问题。TextBlock应该有多宽? - mm8
@mm8 正如我在第二段和第四段所写的那样,宽度应尽可能地大。但是它不应该使窗口变大,只有StackPanel应该增加窗口的宽度。 - Hexo
@mm8 标记已添加 - Hexo
你可以将一个控件的MaximumWidth绑定到另一个控件的ActualWidth - Sinatr
2个回答

3

你需要将WindowWidth绑定到外部GridWidth(正如Mighty Badaboom建议的那样),并将ScrollViewerWidth绑定到StackPanelActualWidth

以下是实现方法:

<Window SizeChanged="MainWindow_OnSizeChanged" Height="225" SizeToContent="Width" ResizeMode="NoResize">
<Grid Name="OuterGrid" MinWidth="500">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="35"/>
    </Grid.RowDefinitions>


    <ScrollViewer Grid.Row="0" Margin="15" VerticalScrollBarVisibility="Auto">
        <TextBlock TextWrapping="Wrap" Height="Auto">...Some Binding...</TextBlock>
    </ScrollViewer>

    <StackPanel Grid.Row="1" HorizontalAlignment="Stretch" Background="Blue">
        <StackPanel x:Name="ButtonStackPanel" HorizontalAlignment="Center" Grid.Row="1" FlowDirection="RightToLeft" Orientation="Horizontal" Width="Auto">
            ...Some Buttons...
        </StackPanel>
    </StackPanel>
</Grid>
</Window>

并且代码后端:

private void MainWindow_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
    if (e.WidthChanged)
        OuterGrid.Width = Panel.ActualWidth;
}


编辑:添加一个外部的StackPanel以支持拉伸和蓝色背景的要求。

编辑2:似乎你不能仅使用XAML实现所描述的行为。虽然修复只需要几行代码。


1
好主意,但我建议将窗口宽度绑定到外部的Grid而不是StackPanel,因为添加边距时可能会遇到问题。 - Mighty Badaboom
1
嗯,蓝色背景和拉伸有什么关系?你能否更新你的问题并提供一个例子吗? - George Chondrompilas
1
@SlapY 你可以使用一个外部的 StackPanel 来实现这个目的。我已经更新了我的答案。 - George Chondrompilas
1
@SlapY 看起来你无法在没有一些代码支持的情况下实现这种精确的行为。我更新了我的答案。 - George Chondrompilas
1
与大多数 outer.Width = inner.ActualWidth 方法一样,这种方法适用于数据增长,但如果删除按钮,则大小不会缩小,除非某些不同的大小更改触发事件。因此,只要布局不是动态缩小的,这种方法就可以正常工作,否则请更加注意 SizeChanged 事件源。 - grek40
显示剩余6条评论

2

您可以编写自己的自定义控件,并以一种方式覆盖测量/排列,使宽度和高度由不同的子元素主导。请注意,我在这里有点作弊,只是使用面板的前两个子元素,并根据您的问题赋予它们不同的含义,甚至没有先检查有效的子索引等:

public class SizeAwareControl : Panel
{
    private UIElement HeightDeterminingContent
    {
        get { return InternalChildren[0]; }
    }
    private UIElement WidthDeterminingContent
    {
        get { return InternalChildren[1]; }
    }

    protected override Size MeasureOverride(Size constraint)
    {
        Size result = new Size(double.PositiveInfinity, double.PositiveInfinity);

        WidthDeterminingContent.Measure(result);

        result.Width = WidthDeterminingContent.DesiredSize.Width;

        HeightDeterminingContent.Measure(result);

        result.Height = WidthDeterminingContent.DesiredSize.Height + HeightDeterminingContent.DesiredSize.Height;

        return result;
    }
    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        HeightDeterminingContent.Arrange(new Rect(0, 0, WidthDeterminingContent.DesiredSize.Width, HeightDeterminingContent.DesiredSize.Height));
        WidthDeterminingContent.Arrange(new Rect(0, HeightDeterminingContent.DesiredSize.Height, WidthDeterminingContent.DesiredSize.Width, WidthDeterminingContent.DesiredSize.Height));
        return new Size(WidthDeterminingContent.DesiredSize.Width, WidthDeterminingContent.DesiredSize.Height + HeightDeterminingContent.DesiredSize.Height);
    }
}

使用方法

<local:SizeAwareControl>
    <!-- First child determines the height -->
    <ScrollViewer Margin="15" VerticalScrollBarVisibility="Auto" MaxHeight="600">
        <TextBlock TextWrapping="Wrap" Height="Auto">...Some Binding...</TextBlock>
    </ScrollViewer>
    <!-- Second child determines the width -->
    <StackPanel x:Name="ButtonStackPanel" Orientation="Horizontal" MinWidth="500">
        <Button Width="100" Content="..." Click="MenuItem_Click"/>
        <Button Width="100" Content="..."/>
        <Button Width="100" Content="..."/>
    </StackPanel>
</local:SizeAwareControl>

这只是一个起点 - 你可能需要更详细的逻辑来确定好的宽度和高度值。第一个子元素上的MaxHeight允许长文本滚动(而不是增加非常高的窗口),第二个子元素上的MinWidth确保了少量按钮的宽度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接