WPF——关于控件显示坐标与通过PointToScreen拿到的不一致的问题?

一、问题描述

利用Grid定义列将其分成三列,为每列填充元素,元素为自定义控件,即通过Generic声明样式,继承基类控件实现类的行为和方法。此时,元素的通过`HorizontalAlignment被赋值Left或Right,但未对VerticalAlignment赋值,导致其垂直对齐方式为拉伸填充,虽然其样式内部设置了宽高。此时元素通过PointToScreen获取屏幕坐标,得到的并非其实际显示的坐标。

二、知识查阅

  1. 一些对齐方式的依赖属性?
    HorizontalAlignment/VerticalAlignment,指自身相对于父元素的位置
    HorizontalContentAlignment/VerticalContentAlignment,指子元素在自身元素中的相对位置
  2. VerticalAlignment 与 HorizontalAlignment的默认值是什么?
    两个属性的值可以是Stretch、Left、Right、Top、Bottom、Center。默认值为Stretch,即元素将会在其可用空间内拉伸以填充其父元素。
  3. Stretch何时起作用?
    如果元素设置了固定宽高,那么Stretch将不会影响其显示。如果元素的AlignmentLeft、Right、Top、Bottom、Center任意值,Stretch则不起作用。另外,如果你想元素保持原始宽高比展示,可以将其Stretch属性设为Uniform/UniformToFill
  4. 为什么Stretch会导致控件显示坐标与获取不一致的问题?
    其实并非控件显示坐标与获取不一致,而是获取的控件坐标实际是外层的Grid,对齐方式使用了Stretch导致元素对可用空间进行拉伸填充,而元素内部虽然指定了宽高不影响其显示,但借助控件x:Name得到的元素依旧是外层的Grid而不是控件模板内部的元素。

三、问题复现

<Window x:Class="WpfApp20.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp20"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid x:Name="rectanglegrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>


        <!--模板外的控件  模拟自定义控件-->
        <Grid x:Name="rec1">
            <!--模板内的grid  模拟自定义控件内部Panel-->
            <Grid>
                <Rectangle Fill="Pink" Width="100" Height="100"/>
            </Grid>
        </Grid>


        <Button Grid.Column="1" Width="200" Height="50" Click="Button_Click"/>
        
        <Rectangle Grid.Column="2" Fill="Green"/>

    </Grid>
</Window>


// 获取第一列显示元素的屏幕坐标
private void Button_Click(object sender, RoutedEventArgs e)
{
    Point point= rec1.PointToScreen(new Point(0, 0));
    Console.WriteLine($"x={ point.X},y={point.Y}");
}
  1. 直接运行程序,得到的rec1是外层Grid拉伸后的实际坐标;
  2. 如果为rec1的Grid定义 VerticalAlignment="Center",得到的rec1是内层粉红色矩形的实际坐标;因为其保证了rec1不是以拉伸方式填充可用空间。
  3. 如果为rec1的Grid定义 Width="100" Height="100",得到的rec1也是内层粉红色矩形的实际坐标;因为其已经有了固定大小,Strech将不会影响其显示。但是如果对内部Grid定义 `Width=“100” Height="100"将不起作用,因为rec1.PointToScreen()获取的是外层,即其本身的实际坐标。

四、反思与总结

对于自定义内部控件,在使用的时候还是需要指定其水平对齐方式和垂直对齐方式,即HorizontalAlignment/VerticalAlignment;如果没有指定,将会导致对齐方式使用Stretch方式对可用空间进行拉伸。尽管控件样式内部定死的宽高,但还是无法保证定义控件外层的Grid不被拉伸,即表面上看是获取了显示控件的实际位置,其实是获取wpf为自定义控件创建的一个隐形Grid的实际位置。

五、参考资料

对齐方式、边距和填充概述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值