WPF位置元素取决于窗口大小

时间:2017-06-16 14:29:39

标签: c# wpf xaml layout screen-resolution

我目前正在开发一个应用程序,用于从SQL数据库中检索数据并将其呈现在UI中。我的整个功能运行顺利,但现在我被困在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等等,但我无法按照我想要的方式工作。

非常感谢任何帮助。

提前致谢!

2 个答案:

答案 0 :(得分:0)

这将涉及某种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

答案 1 :(得分:0)

作为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>