WPF 根据窗口大小定位元素

3
我正在开发一个应用程序,从SQL数据库中检索数据并在用户界面中呈现。我已经成功地实现了整个功能,但现在卡在了GUI部分。我希望UI能够适应窗口大小。元素(img、labels、textboxes)具有最小高度和最小宽度,但也可以根据可用空间增长到最大值。如果窗口变得太小,我希望UI像响应式网站一样进行调整。
最大化的窗口会像这样: 最大化的窗口 窗口变得太小时,元素会相应地调整: 较小的窗口 我的最佳方法是:
<Grid DataContext="{Binding CurrentPerson}">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="5*"/>
    </Grid.ColumnDefinitions>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Image Grid.Row="0" Source="{Binding Person.Photo}"/>
    </Grid>

    <Viewbox Grid.Column="1" StretchDirection="Both" Stretch="Uniform" HorizontalAlignment="Left" VerticalAlignment="Top">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <Label Grid.Column="0" Grid.Row="0" VerticalAlignment="Center">Title:</Label>
            <Label Grid.Column="0" Grid.Row="1" VerticalAlignment="Center">Name:</Label>
            <Label Grid.Column="0" Grid.Row="2" VerticalAlignment="Center">Street:</Label>
            <Label Grid.Column="0" Grid.Row="3" VerticalAlignment="Center">City:</Label>
            <Label Grid.Column="0" Grid.Row="4" VerticalAlignment="Center">Number:</Label>
            <TextBox Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="80"Text="{Binding Person.Title}"/>
            <TextBox Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300">
                <TextBox.Text>
                    <MultiBinding StringFormat="{}{0} {1}">
                        <Binding Path="Person.LastName"/>
                        <Binding Path="Person.FirstName"/>
                    </MultiBinding>
                </TextBox.Text>
            </TextBox>
            <TextBox Grid.Column="1" Grid.Row="2" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300" Text="{Binding Person.Street}"/>
            <TextBox Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300" Text="{Binding Person.City}"/>
            <TextBox Grid.Column="1" Grid.Row="4" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="30" Text="{Binding Person.Number}"/>
        </Grid>
    </Viewbox>
</Grid>

这个解决方案的问题在于当窗口缩小时,内容会缩小以适应窗口,变得不可读。如果图像可以移到人员数据之上,就可以节省很多空间,而且人员数据也可以被读取。
我尝试过使用wrappanel、viewbox、grid、uniformgrid等方式,但无法以我想要的方式实现它。
非常感谢任何帮助。
提前致谢!
2个回答

5
这将涉及某种C#代码。您可以编写触发器来更改控件上的Grid.Row和Grid.Column值,并使用值转换器来决定何时,但这更简单。
首先,将主网格分解为两个单独的网格。您基本上有两个窗格,因此需要在单独的网格中获取它们的内容。
<StackPanel x:Name="MainLayout" Orientation="Horizontal">
    <Grid>
        <!-- img -->
    </Grid>

    <Grid>
        <Viewbox Stretch="Uniform">
            <!-- Title, name, etc. -->
        </Viewbox>
    </Grid>
</StackPanel>

给窗口添加一个 SizeChanged 处理程序:
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if (ActualWidth < 400)
    {
        MainLayout.Orientation = Orientation.Vertical;
    }
    else
    {
        MainLayout.Orientation = Orientation.Horizontal;
    }
}

更新

如果您喜欢,也可以使用 UniformGrid 进行操作。

<UniformGrid x:Name="MainLayout" Columns="2">
    <Grid
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        >
        <!-- img -->
    </Grid>

    <Viewbox 
        Stretch="Uniform"
        HorizontalAlignment="Left"
        >
        <!-- Title, name, etc. -->
    </Viewbox>
</UniformGrid>

代码后端
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if (ActualWidth < 400)
    {
       //MainLayout.Orientation = Orientation.Vertical;
       MainLayout.Columns = 1;
    }
    else
    {
        //MainLayout.Orientation = Orientation.Horizontal;
        MainLayout.Columns = 2;
    }
}

更新2

您还可以在右侧窗格上切换网格列和行:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        >
        <!-- Img -->
    </Grid>

    <Viewbox 
        x:Name="RightPane"
        Grid.Column="1"
        Grid.Row="0"
        Stretch="Uniform" 
        HorizontalAlignment="Left">
        <StackPanel 
            Orientation="Vertical" 
            >
            <!-- Title, name, etc. -->
        </StackPanel>
    </Viewbox>
</Grid>

代码后台:

private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if (ActualWidth < 400)
    {
        //MainLayout.Orientation = Orientation.Vertical;
        //MainLayout.Columns = 1;
        Grid.SetColumn(RightPane, 0);
        Grid.SetRow(RightPane, 1);
    }
    else
    {
        //MainLayout.Orientation = Orientation.Horizontal;
        //MainLayout.Columns = 2;
        Grid.SetColumn(RightPane, 1);
        Grid.SetRow(RightPane, 0);
    }
}

我希望你考虑不使用Viewbox。将字体和控件缩放到窗口大小通常被认为不太可用,但这是你的项目。
如果您确实想使用Viewbox,请了解其Stretch属性,该属性控制其如何缩放其内容。
也请查看ViewBox.StretchDirection

嗨,感谢您的回复。您的解决方案非常好,但我正在使用viewbox来拉伸我的第二个网格(人员数据)。使用viewbox,我不必指定字体大小、文本框宽度等,所有元素都会根据窗口大小进行拉伸。似乎viewbox无法在stackpanel内以这种方式工作链接。有什么想法可以解决这个问题吗? - bego
@bego 我不确定你想要实现什么,也看不到你修改后的代码版本,所以很难猜测如何修复它。如果你将 ViewBox 包裹在一个 Grid 中,它就会自动缩放。但我想补充一点,根据窗口大小自动缩放字体和控件是一个用户界面的想法,我最后一次见到这种做法是在90年代早期的Windows API文档中。实际上,这并不是一个非常好的想法。 - 15ee8f99-57ff-4f92-890c-b56153
1
我的问题是我需要考虑不同的屏幕尺寸和分辨率。因此,我认为Viewbox将是一个实用的解决方案。我正在开发的应用程序应该可以从比平常更远的距离阅读,因为一些屏幕被用作“公共显示器”。然而它们并不比23英寸大,所以标签和控件必须相当大。 感谢您的努力。 - bego
@bego 如果它在你的应用程序中运行良好,那就可以了。永远不要说永远! - 15ee8f99-57ff-4f92-890c-b56153
我实现了你在第二次更新中提供的解决方案。由于格式原因,我不得不尝试使用列和行跨度,但现在它运行得非常好!再次感谢你的帮助! - bego
愚蠢的问题,我是C#的新手。如何使用private void Window_SizeChanged.....?你有MainWindows.xaml.cs的完整示例吗?我已经将它放在了public MainWindow() {.....下面,但它没有任何作用。谢谢。 - EPurpl3

0
作为对Ed答案的变化,您可以使用值转换器来检查图像的宽度是否小于您想要应用的最小尺寸。因此,您最终会得到类似于响应式Web设计中断点概念的东西。
这篇博客文章解释了这一点: https://www.iambacon.co.uk/blog/a-pattern-for-responsive-applications-in-wpf 如果应用程序中有许多需要此功能的窗口,则可以重复使用所有窗口上的值转换器,这将非常有用。
在更简单的情况下,如果图像大小固定且不需要随窗口大小调整,则可以像这样简单地使用WrapPanel:
<WrapPanel DataContext="{Binding CurrentPerson}">
    <Border BorderBrush="Black" BorderThickness="1">
        <Image Grid.Row="0" Width="200" Height="200" />
    </Border>
    <Grid VerticalAlignment="Center">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Label Grid.Column="0" Grid.Row="0" VerticalAlignment="Center">Title:</Label>
        <Label Grid.Column="0" Grid.Row="1" VerticalAlignment="Center">Name:</Label>
        <Label Grid.Column="0" Grid.Row="2" VerticalAlignment="Center">Street:</Label>
        <Label Grid.Column="0" Grid.Row="3" VerticalAlignment="Center">City:</Label>
        <Label Grid.Column="0" Grid.Row="4" VerticalAlignment="Center">Number:</Label>
        <TextBox Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="80" Text="{Binding Person.Title}"/>
        <TextBox Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300">
            <TextBox.Text>
                <MultiBinding StringFormat="{}{0} {1}">
                    <Binding Path="Person.LastName"/>
                    <Binding Path="Person.FirstName"/>
                </MultiBinding>
            </TextBox.Text>
        </TextBox>
        <TextBox Grid.Column="1" Grid.Row="2" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300" Text="{Binding Person.Street}"/>
        <TextBox Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300" Text="{Binding Person.City}"/>
        <TextBox Grid.Column="1" Grid.Row="4" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="30" Text="{Binding Person.Number}"/>
    </Grid>
</WrapPanel>

嗨,谢谢!不幸的是,我还需要调整图像大小。我将尝试使用值转换器并稍后再评论。 - bego

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