【问题标题】:WPF position elements depending on window sizeWPF 位置元素取决于窗口大小
【发布时间】:2017-06-16 14:29:39
【问题描述】:

我目前正在开发一个应用程序来从 SQL 数据库中检索数据并将其呈现在 UI 中。我让整个功能顺利运行,但现在我被困在 der GUI 部分。我希望 UI 适应窗口大小。元素(img、标签、文本框)具有最小高度和最小宽度,但也可以增长到最大可用空间。如果窗口太小,我希望 UI 能够像响应式网站一样进行调整。

最大化的窗口会是这样的: Maximized window

窗口宽度变小,元素相应调整: smaller window

我最好的方法是:

<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 等等,但我无法让它按照我想要的方式工作。

非常感谢任何帮助。

提前致谢!

【问题讨论】:

    标签: c# wpf xaml layout screen-resolution


    【解决方案1】:

    这将涉及某种 C# 代码。您可以编写触发器来更改控件上的Grid.RowGrid.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

    【讨论】:

    • 您好,感谢您的回复。您的解决方案效果很好,但我正在使用视图框来拉伸我的第二个网格(个人数据)。使用视图框,我不必指定字体大小、文本框宽度等,所有元素都会根据窗口大小进行拉伸。似乎视图框在堆栈面板中无法以这种方式工作link 有什么想法可以解决这个问题吗?
    • @bego 我不确切知道您要实现的目标,也看不到您的代码版本,因此无法准确猜测如何修复它。如果您将ViewBox 包装在Grid 中,它将缩放。不过,我想补充一点,使用窗口大小缩放字体和控件是我最后一次在 90 年代初期的旧 Windows API 文档中看到的用户界面理念。在实践中,结果证明这不是一个好主意。
    • 我的问题是我需要考虑不同的屏幕尺寸和分辨率。因此,我认为 Viewbox 将是一个实用的解决方案。我正在开发的应用程序应该可以从比平常更远的距离读取,因为某些屏幕被用作“公共显示器”。不过它们不大于 23",因此标签和控件必须非常大。感谢您的努力
    • @bego 好吧,如果它在您的应用程序中有效,它就有效。永远不要说永远!
    • 我在第二次更新中实施了您的解决方案。出于格式化原因,我不得不尝试使用列和行跨度,但现在效果很好!再次感谢您的帮助!
    【解决方案2】:

    作为 Ed 答案的变体,您可以使用值转换器来检查图像的宽度现在是否小于您要应用于它的最小尺寸。所以你最终会得到类似于响应式网页设计中的断点概念的东西。

    这篇博客文章解释了这一点: 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>
    

    【讨论】:

    • 您好,谢谢!不幸的是,我还必须调整图像大小。我打算玩一下价值转换器,稍后再评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 2011-04-12
    • 1970-01-01
    • 2011-07-14
    • 2020-12-09
    • 1970-01-01
    相关资源
    最近更新 更多