如何在 WPF 中正确定位控件 window

How to correctly locate a control in a WPF window

考虑以下用于在单击 Main Window 中的按钮后显示子项 window 的代码隐藏。期望的结果是让子 window 位置显示在菜单按钮的右侧,与菜单按钮的顶部对齐。 (因此 window 显示在激活它的控件旁边。)

    private void btnMenu2_Click(object sender, RoutedEventArgs e)
    {
        var menu2 = new Menu2Window();

        //var winLocation = this.btnMenu2.TranslatePoint(new Point(0, 0), Application.Current.MainWindow);
        //var winLocation = this.btnMenu2.TranslatePoint(new Point(0, 0), this.spLeftMenu);
        //var winLocation = this.spLeftMenu.TranslatePoint(new Point(0, 0), this.btnMenu2);
        var objLocation = this.spLeftMenu.TranslatePoint(new Point(0, 0), this.spLeftMenu);
        var scnLocation = this.btnMenu2.PointToScreen(objLocation);

        //menu2.Left = scnLocation.X + btnMenu2.Width;
        menu2.Left = scnLocation.X + 50; // <- Why does this work but using btnMenu2.Width causes placement to be all over the place???

        menu2.Top = scnLocation.Y;
        menu2.ShowDialog();
    }

所提供的代码确实以所需的方式工作,但我不喜欢在代码中使用硬编码值或幻数。

如果您注释掉带有硬编码控件宽度值 (50) 的行并使用按钮的宽度 属性 取消注释该行,则后续执行会导致菜单 window 显示在一系列违反逻辑的位置中。它似乎是从随机数生成器而不是控件的宽度获取值。当它 运行 几次时,我确实看到了一个模式,但是由于每次执行代码时 属性 值响应的变化而得到 4 或 5 个不同的位置是非常令人沮丧的。

这里正确或正确的方法是什么?如何从 WPF 控件返回可靠的值 属性 还是我要求太多?

XAML 主 Window:

<Window x:Class="LocateTest.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:LocateTest"
        mc:Ignorable="d"
        Background="DarkGray"
        Title="MainWindow" Height="400" Width="750">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <StackPanel x:Name="spLeftMenu"
                    DataContext="MainWindow"
                    Orientation="Vertical" 
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center"
                    Height="325" Width="50">
            <Button Content="B1" Height="50" Width="50"/>
            
            <Button x:Name="btnMenu2" 
                    Content="B2" 
                    Height="50" 
                    Click="btnMenu2_Click"/>
            
            <Button Content="B3" Height="50"/>
        </StackPanel>
    </Grid>
</Window>

XAML 菜单 2 window:

<Window x:Class="LocateTest.Menu2Window"
        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:LocateTest"
        mc:Ignorable="d"
        WindowStyle="None"
        AllowsTransparency="True"
        WindowStartupLocation="Manual"
        Height="150" Width="280">
    <Window.Background>
        <SolidColorBrush Opacity="0.5" Color="Black"></SolidColorBrush>
    </Window.Background>
    <Grid>
        <Button x:Name="btnClose" 
                Click="btnClose_Click"
                Width="25"
                Height="25"
                Content="X"
                Background="Black"
                Foreground="Red" Margin="245,10,10,115">
        </Button>
    </Grid>
</Window>```

改用 Popup 怎么样?您可以将您想要的所有内容放在 Popup 控件上而不是 window 并将其 StaysOpen 属性 设置为 true 以使用户通过关闭按钮或其他方式自动关闭它。

由于您没有明确设置 btnMenu2 的宽度,因此其 Width 属性 的值将是 double.NaN。请注意,Width 值不是实际宽度,而是请求的宽度。请改用 ActualWidth 属性:

menu2.Left = scnLocation.X + btnMenu2.ActualWidth;