Home > Enterprise >  How to correctly locate a control in a WPF window
How to correctly locate a control in a WPF window

Time:11-06

Consider the following code-behind for displaying a child window after clicking a button in the Main Window. The desired result is to have the child window placement show up just to the right of the menu button, in-line with the top of the menu button. (So the window shows right next to the control that activated it.)

    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();
    }

The code as presented does work in the fashion needed, however I don't like using hard-coded values or magic numbers in code.

If you comment out the line with the hard-coded control width value (50) and un-comment the line using the button's width property, subsequent execution results in the menu window displaying in a sequence of locations that defy logic. It appears that it is getting a value from a random number generator rather than the control's width. I do see a pattern when its run several times, but getting 4 or 5 different locations because of a variation in property value responses each time the code is executed is quite frustrating.

What would be the RIGHT or correct approach here? How do I get a reliable value back from a WPF control property or am I asking too much?

XAML of Main 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 of Menu2 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>```

CodePudding user response:

Since you don't explicitly set the width of btnMenu2, the value of its Width property will be double.NaN. Note that the Width value is not the actual width, but the requested width. Use the ActualWidth proeprty instead:

menu2.Left = scnLocation.X   btnMenu2.ActualWidth;

CodePudding user response:

What about using Popup instead? You can put all you want on a Popup control instead of a window and set its StaysOpen property to true to make the user close it automatically through close button or whatever.

  • Related