开发者

WPF/XAML bind width of an element to a fraction of screen size

I am writing an application in C#/WPF and am trying to figure out how to databind the width of the grids column definitions to a fraction of the screen width. Is this possible? Essentially I want something like this:

Grid = 2x2

Row 1 Height = 2/3 of screen height

Row 2 Height = 1/3 of screen height

Row 1 Width = 2/3 of screen width

Row 2 Width = 1/3 of screen width

I think that this correctly binds the full width to a column definition:

<ColumnDefinition Width="{Binding ElementName=Window1, Path=Width}"/>

but what I don't know how to do is perform an operation on the value it gets throu开发者_如何学Cgh the databinding... is this even possible? I feel like this is something I should be able to code into the XAML and not have to implement programmatically but I have little experience with UI design :( I would want something like:

<ColumnDefinition Width="{Binding ElementName=Window1, Path=Width} * 2 / 3"/>

but that is invalid

Should I just be writing a function to re-layout UI elements whenever the screen resizes? I feel like that is redundant... or is there some easy way of doing this that I don't know about? Any input welcome! Thanks!


It sounds like you just want to use star sizing:

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

This will always give the first row 2/3 of the height and the second row 1/3 of the height, and the first column 2/3 of the width and the second column 1/3 of the width. It will be based on the size of the Grid, but if the Grid is the only child of the Window then that will be the same as the size of the Window.


I ran into a situation where I needed to bind the width of a control to a fraction of the height of another and I don't think there's a handy built-in way of dividing a bound property like that. I ended up making a converter that would divide a bound value by the converter parameter to solve this issue without resorting to code-behind and figured I'd share it in case it helps save someone else some time. I'm posting it here because this post was the first result when I was searching if there was a cleaner way.

Converter class:

[ValueConversion(typeof(double), typeof(double))]
public class DoubleDivisionConverter : System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if(value == null) { return 0.0; }
        if(parameter == null) { return value; }

        double param;
        if(Double.TryParse(parameter.ToString(), out param))
        {
            if(param == 0) { return 0.0; }
            return (double)value / param;
        }
        else
        {
            throw new ArgumentException("Could not parse converter parameter as double.");
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if(value == null) { return 0.0; }
        if(parameter == null) { return value; }

        double param;
        if(Double.TryParse(parameter.ToString(), out param))
        {
            if(param == 0) { return 0.0; }
            return (double)value * param;
        }
        else
        {
            throw new ArgumentException("Could not parse converter parameter as double.");
        }
    }
}

XAML: (Converter used on UniformGrid Width property)

<Window.Resources>
    <converters:DoubleDivisionConverter x:Key="DoubleDivisionConverter"/>
</Window.Resources>

<Grid x:Name="sampleGrid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.Resources>
        <Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="FontWeight" Value="Bold"/>
        </Style>
    </Grid.Resources>

    <Border x:Name="rSample" Grid.Column="0" Grid.Row="0" Background="PaleVioletRed">
        <TextBlock>
            <TextBlock.Inlines>
                <Run Text="{Binding ElementName=rSample, Path=ActualWidth, StringFormat={}{0:N1}px, Mode=OneWay}"/><Run Text=", "/><Run Text="{Binding ElementName=rSample, Path=ActualHeight, StringFormat={}{0:N1}px, Mode=OneWay}"/>
            </TextBlock.Inlines>
        </TextBlock>
    </Border>

    <!-- Set the UniformGrids width to 1/3 the height of the parent grid using the converter -->
    <UniformGrid Grid.Column="1" Grid.Row="0" Columns="1"
    Width="{Binding ElementName=sampleGrid, Path=ActualHeight, Converter={StaticResource DoubleDivisionConverter}, ConverterParameter=3, Mode=OneWay}">
        <Border x:Name="gSample" Background="LightGreen">
            <TextBlock>
                <TextBlock.Inlines>
                    <Run Text="{Binding ElementName=gSample, Path=ActualWidth, StringFormat={}{0:N1}px, Mode=OneWay}"/><Run Text=", "/><Run Text="{Binding ElementName=gSample, Path=ActualHeight, StringFormat={}{0:N1}px, Mode=OneWay}"/>
                </TextBlock.Inlines>
            </TextBlock>
        </Border>
        <Border x:Name="ySample" Background="LightGoldenrodYellow">
            <TextBlock>
                <TextBlock.Inlines>
                    <Run Text="{Binding ElementName=ySample, Path=ActualWidth, StringFormat={}{0:N1}px, Mode=OneWay}"/><Run Text=", "/><Run Text="{Binding ElementName=ySample, Path=ActualHeight, StringFormat={}{0:N1}px, Mode=OneWay}"/>
                </TextBlock.Inlines>
            </TextBlock>
        </Border>
        <Border x:Name="bSample" Background="LightBlue">
            <TextBlock>
                <TextBlock.Inlines>
                    <Run Text="{Binding ElementName=bSample, Path=ActualWidth, StringFormat={}{0:N1}px, Mode=OneWay}"/><Run Text=", "/><Run Text="{Binding ElementName=bSample, Path=ActualHeight, StringFormat={}{0:N1}px, Mode=OneWay}"/>
                </TextBlock.Inlines>
            </TextBlock>
        </Border>
    </UniformGrid>
</Grid>

Result:

WPF/XAML bind width of an element to a fraction of screen size

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜