none
How to position a window based on the position of the button that launched it or relative to the window that contains the button? RRS feed

  • Question

  • Hi all. I have a main form with menu options in the form of buttons in a list box. The content of the list box comes from an XML data provider so the position of each is assigned dynamically. When I click on the buttons whose option are still in construction I want to display the same custom form near the button that was clicked.

    Here is the XML dataprovider:

    <XmlDataProvider x:Key="xmlMenuOptions" XPath="/MenuItems">
                <x:XData>
                    <MenuItems xmlns="">
                        <MenuItem Value="Applications" BackgroundColor="#022737" Image ="/Images/Generic_Application.ico" Window="wndApplication" Description="All applications"/>
                        <MenuItem Value="Application Types" BackgroundColor="#91AB30" Image ="/Images/1381_cog.ico"  Window="wndApplicationType" Description="Types of applications that use the database objects"/>
                        <MenuItem Value="Databases" BackgroundColor="#6B4727" Image ="/Images/dbs.ico" Window="wndDatabase" Description="Databases with objects used by the different applications"/>
                        <MenuItem Value="Server Objects" BackgroundColor="#C03900" Image ="/Images/VPN.ico" Window="wndServerObject" Description="Database objects such as stored procedures, views, user-defined functions (UDF), etc."/>
                        <MenuItem Value="Server Object Types" BackgroundColor="#E48600" Image ="/Images/1381_cog.ico" Window="wndInConstruction" Description="Types of database objects (stored procedures, views, user-defined functions (UDF), etc)."/>
                        <MenuItem Value="Application Objects" BackgroundColor="#393144" Image ="/Images/ConnectionManager.ico" Window="wndApplicationObject" Description="Applications and the types of database objects they use (stored procedures, views, user-defined functions (UDF), etc)."/>
                        <MenuItem Value="Reports" BackgroundColor="#8D4C50" Image ="/Images/report.png" Window="wndInConstruction" Description="Reports"/>
                    </MenuItems>
                </x:XData>
            </XmlDataProvider>

    And here is the Listbox XAML definition:

    <Grid Grid.Row="2" HorizontalAlignment="Center"  VerticalAlignment="Top" >
                    <ListBox Name="lstMenu" Margin="10" Foreground="White" Background="Transparent" 
                              ItemsSource="{Binding Source={StaticResource xmlMenuOptions}, XPath=MenuItem}" 
                              BorderBrush="Transparent" HorizontalAlignment="Left" SelectionMode="Single" VerticalAlignment="Top" Width="329">
                        <ListBox.ItemContainerStyle>
                            <Style>
                                <Setter Property="Control.Padding" Value="0"></Setter>
    
                            </Style>
                        </ListBox.ItemContainerStyle>
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <Button Margin="0" Background="Black" Foreground="White" Name="btnMenuOption" Click="btnMenuOption_Click" Tag="{Binding XPath=@Window}">
                                    <Border Margin="5" Padding="3" BorderBrush="{Binding XPath=@BackgroundColor}" BorderThickness="1" CornerRadius="0" VerticalAlignment="Top" Width="300"
                                            Background="{Binding XPath=@BackgroundColor}">
                                        <Grid Margin="3" Background="{Binding XPath=@BackgroundColor}">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="60"></ColumnDefinition>
                                                <ColumnDefinition></ColumnDefinition>
                                            </Grid.ColumnDefinitions>
                                            <Image Source="{Binding XPath=@Image}" Width="50" Height="50" HorizontalAlignment="Left"></Image>
                                            <Grid Grid.Column="1">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition></RowDefinition>
                                                    <RowDefinition></RowDefinition>
                                                </Grid.RowDefinitions>
                                                <TextBlock Text="{Binding XPath=@Value}" FontWeight="Bold" FontSize="14" />
                                                <TextBlock Grid.Row="1" Text="{Binding XPath=@Description}" FontSize="12" TextWrapping="Wrap" />
                                            </Grid>
                                        </Grid>
                                    </Border>
                                </Button>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </Grid>

    In the code behind I try to position the form near the button that was clicked with no success- the position even seems to be random:

    Private Sub btnMenuOption_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Try
                ' Get the current button.
                Dim cmd As Button = CType(e.OriginalSource, Button)
                ' Create an instance of the window named by the current button. The name is the value of the XML Window attribute.
                Dim type As Type = Me.GetType()
                Dim currentAssembly As System.Reflection.Assembly = type.Assembly
                Dim win As Window = CType(currentAssembly.CreateInstance(type.Namespace & "." & DirectCast(cmd.Tag, System.Xml.XmlAttribute).Value), Window)
                If DirectCast(cmd.Tag, System.Xml.XmlAttribute).Value = "wndInConstruction" Then
                    Dim coordinates As Point = cmd.TransformToAncestor(Me).Transform(New Point(0, 0))
                    Dim view As ICollectionView = CollectionViewSource.GetDefaultView(lstMenu.ItemsSource)
                    Dim app As String = DirectCast(DirectCast(view, System.Windows.Data.ListCollectionView).CurrentItem, System.Xml.XmlElement).GetAttribute("Value")
    
                    'win.Left += LayoutInformation.GetLayoutSlot(lstMenu).X + lstMenu.Margin.Left
                    'win.Top += LayoutInformation.GetLayoutSlot(lstMenu).Y + lstMenu.Margin.Top
                    If app.Equals("Server Object Types", StringComparison.InvariantCultureIgnoreCase) Then
                        win.Top = coordinates.X
                        win.Left = coordinates.Y * 1.31
                    ElseIf app.Equals("Reports", StringComparison.InvariantCultureIgnoreCase) Then
                        win.Top = coordinates.X * 5.93
                        win.Left = coordinates.Y * 1.31
                    End If
                    'Dim res As Integer = win.Left / 0 'Generate an exception for testing Application exception handler.
                End If
                win.ShowDialog()
                win.Close()
            Catch ex As Exception
                MessageBox.Show("Unable to create window!", "AOM")
            End Try
        End Sub

    What is the best way to obtain the position of the currently clicked button (or listbox selected item) and position the form based on that?

    Thanks in advance.


    TheBugSlayer

    Tuesday, February 7, 2012 4:11 PM

Answers

  • You need to calculate teh actual position of the control in relation to the screen, then set the window's Left and Top properties.

    Below is a C# example I made for you. It's been a while since I did VB, but I'm sure you can convert such a simple example back to VB.

            void btnMenuOption_Click(object sender, RoutedEventArgs e)
            {
                var button = sender as Button;
    
                Point locationFromWindow = button.TranslatePoint(new Point(0, 0), this);
                Point locationFromScreen = button.PointToScreen(locationFromWindow);
    
    
                var win = new Window { Width = 100, Height = 100 };
                win.Left = locationFromScreen.X;
                win.Top = locationFromScreen.Y;
                win.Show();
            }

    Hope that answers your question.

    Regards,
    Pedro


    If you find my post helpful, please remember to "Mark As Answer" and/or "Vote as Helpful"

    • Marked as answer by TheBugSlayer Wednesday, February 8, 2012 5:42 PM
    Tuesday, February 7, 2012 4:52 PM
    Moderator

All replies

  • You need to calculate teh actual position of the control in relation to the screen, then set the window's Left and Top properties.

    Below is a C# example I made for you. It's been a while since I did VB, but I'm sure you can convert such a simple example back to VB.

            void btnMenuOption_Click(object sender, RoutedEventArgs e)
            {
                var button = sender as Button;
    
                Point locationFromWindow = button.TranslatePoint(new Point(0, 0), this);
                Point locationFromScreen = button.PointToScreen(locationFromWindow);
    
    
                var win = new Window { Width = 100, Height = 100 };
                win.Left = locationFromScreen.X;
                win.Top = locationFromScreen.Y;
                win.Show();
            }

    Hope that answers your question.

    Regards,
    Pedro


    If you find my post helpful, please remember to "Mark As Answer" and/or "Vote as Helpful"

    • Marked as answer by TheBugSlayer Wednesday, February 8, 2012 5:42 PM
    Tuesday, February 7, 2012 4:52 PM
    Moderator
  • Thanks Pedro.

    It does not position the form exactly where I thought it should be but I applied a perceentage to the X and Y coordinates and now no matter where the form opens (it saves position on close) or is dragged to, the second form always opens near the button that launched it; the relative position is the same.

    I appreciate your help.


    TheBugSlayer

    Wednesday, February 8, 2012 5:44 PM