Tags

, , ,

On my current project, there was a need for a dropdown menu to appear while hovering over an image or text control. Here are the requirements of the control:

  • When hovering over the target control, a menu control needs to be displayed containing a set of link or button controls
  • The menu needs to stay present while hovering over the target control & the menu control
  • The menu needs to disappear when no longer hovering over the target control or the menu control
  • The links & buttons in the menu control need to be interactive, allowing click events

Our first attempt to solve this problem was to use the built in tooltip control. This approach was fraught with issues. While the tooltip (ie: menu) would appear while hovering over the target control, it would vanish as soon as you attempted to hover onto the tooltip. With some timer trickery, we were able to get the tooltip to remain visible when moving the mouse from the target control to the tooltip but all the controls contained within the tooltip were disabled and could not be interacted with.

After some trial and error we happened by the built-in popup control which was able to fulfill all the needs listed above. Here is how to put it together:

XAML

<StackPanel MouseEnter="OnMouseEnter" MouseLeave="OnMouseLeave">
    <StackPanel Orientation="Horizontal">
        <TextBlock>Root</TextBlock>
        <Image Source="./images/ExpandedArrow.png" Width="15" Height="13"></Image>
    </StackPanel>
    <Popup x:Name="popup">
        <StackPanel x:Name="leaves" Background="Black" HorizontalAlignment="Left" MouseEnter="OnMouseEnter" MouseLeave="OnMouseLeave">
            <HyperlinkButton Content="Leaf 1" Click="OnClick" />
            <HyperlinkButton Content="Leaf 2" Click="OnClick" />
            <Button Click="OnClick">Leaf 3</Button>
        </StackPanel>
    </Popup>
</StackPanel>

Code Behind

public partial class PopupMenu : UserControl
{
    #region Members

    private readonly DispatcherTimer _popupTimer = new DispatcherTimer();

    #endregion

    public PopupMenu()
    {
        InitializeComponent();

        _popupTimer.Interval = TimeSpan.FromMilliseconds(100);
        _popupTimer.Tick += new EventHandler(ClosePopup);
    }

    #region Events

    private void OnMouseEnter(object sender, MouseEventArgs e)
    {
        _popupTimer.Stop();
        this.popup.IsOpen = true;
    }

    private void OnMouseLeave(object sender, MouseEventArgs e)
    {
        _popupTimer.Start();
    }

    private void OnClick(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Clicked!");
    }

    #endregion Events

    void ClosePopup(object sender, EventArgs e)
    {
        _popupTimer.Stop();
        popup.IsOpen = false;
    }
}

As you can see, the code is quite simple. The next step would be to refactor this into a reusable control.