65.9K
CodeProject 正在变化。 阅读更多。
Home

可重用的 Silverlight 2 弹出菜单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.53/5 (7投票s)

2009年1月15日

CPOL

1分钟阅读

viewsIcon

48716

downloadIcon

519

一个带有子菜单的弹出菜单实现。

Menu in action

引言

虽然 Silverlight 拥有许多有用的控件,但似乎缺乏对我们都已习惯的一些功能的的支持。最近,我“荣幸”地构建了一个从数据库构建的带有子菜单的下拉菜单。这很棘手,原因有很多。这里提供的代码是一个由项目列表驱动,并支持子菜单的可重用菜单。

Using the Code

要在页面中嵌入菜单,只需像往常一样将 PopupMenuExample 命名空间添加到 XAML 中即可。

xmlns:pop="clr-namespace:PopupMenuExample"

然后,将其添加到 XAML 布局中(最好是在一个垂直的 StackPanel 中,与将触发它的项目一起)。

<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
    <Border x:Name="PopupBorder" HorizontalAlignment="Left" 
            Height="30" Width="100" BorderBrush="Beige" 
            BorderThickness="1" Background="LightGray">
        <TextBlock HorizontalAlignment="Center" 
             VerticalAlignment="Center">Menu Root</TextBlock>
    </Border>
    <pop:PopupMenu x:Name="PopupMenu1" />
    <TextBlock HorizontalAlignment="Left" x:Name="txtClicked" 
               Margin="0 100 0 0">You last clicked on: Nothing</TextBlock>
</StackPanel>

需要添加一些代码来添加菜单项并将菜单链接到弹出触发器。这可能在某个时候在 XAML 中完成,尽管我还没有掌握这项技能。这是示例项目初始化菜单的代码

PopupMenu1.SetMenuItems(new List&;lt;popupmenuitem>()
{
    new PopupMenuItem(){ Heading = "Item with no submenu. (Tag: Bacon)", 
                         Tag="Bacon", Id=0, ParentId=null},
    new PopupMenuItem(){ Heading = "Item with submenu. (Tag: Eggs)", 
                         Tag="Eggs", Id=1, ParentId=null},
    new PopupMenuItem(){ Heading = "Submenu Item. {Tag: Easter}", 
                         Tag="Easter", Id=2, ParentId=1},
    new PopupMenuItem(){ Heading = "Submenu Item with submenu. (Tag: Foo)", 
                         Tag="Foo", Id=3, ParentId=1},
    new PopupMenuItem(){ Heading = "Sub-Submenu Item. (Tag: Bar}", 
                         Tag="Bar", Id=4, ParentId=3}
});
PopupMenu1.PopupFrom = PopupBorder;

现在,这并不是特别有用,因为大多数情况下,它将是数据驱动的(或者至少对我来说是这样)。这是一个使用 LINQ 查询设置菜单项的示例

PopupMenu1.SetMenuItems(
from item in db.MainMenu 
select new PopupMenuItem()
{ 
    Heading = item.Heading, 
    Tag=item.Uri, 
    Id=item.Id, 
    ParentId=item.ParentId
});

要捕获项目点击,只需处理 ItemClick 事件即可。

PopupMenu1.ItemClick += new PopupMenuItemClickHandler(PopupMenu1_ItemClick);

    ...

void PopupMenu1_ItemClick(object sender, PopupMenuItem item)
{
    txtClicked.Text = "You last clicked on: " + (string)item.Tag;
}

关注点

为了捕获鼠标离开菜单(以及所有子菜单或触发对象)的时间,使用定时器给鼠标一些时间进入新对象(或者 UI 赶上并发送 MouseEnter 事件)。

/// <summary>
/// Catches the mouse leaving this menu (or a child menu).
/// </summary>
void Menu_MouseLeave(object sender, MouseEventArgs e)
{
    //set the timer to see if the user is out of the menu.
    _popTimer.Change(100, System.Threading.Timeout.Infinite);

    //Bubble up an event for parents to see.
    if (_childMouseLeave != null)
    {
        _childMouseLeave(sender, e);
    }
}

/// <summary>
/// Catches the mouse entring this menu (or a child menu).
/// </summary>
void Menu_MouseEnter(object sender, MouseEventArgs e)
{
    //make sure we are still open.
    popMenu.IsOpen = true;

    //stop the timer if its running.
    _popTimer.Change(System.Threading.Timeout.Infinite, 
                     System.Threading.Timeout.Infinite);

    //Bubble up an event for parents to see.
    if (_childMouseEnter != null)
    {
        _childMouseEnter(sender, e);
    }
}

/// <summary>
/// When the timer elapses, the menu is closed.
/// </summary>
void PopupTimer_Elapsed(object state)
{
    popMenu.Dispatcher.BeginInvoke(() => popMenu.IsOpen = false);
}

/// <summary>
/// Catches the mouse leaving this menu's popup trigger.
/// </summary>
void _popupFrom_MouseLeave(object sender, MouseEventArgs e)
{
    _popTimer.Change(100, System.Threading.Timeout.Infinite);
}

/// <summary>
/// Catches the mouse entering this menu's popup trigger.
/// </summary>
void _popupFrom_MouseEnter(object sender, MouseEventArgs e)
{
    popMenu.IsOpen = true;
    _popTimer.Change(System.Threading.Timeout.Infinite, 
                     System.Threading.Timeout.Infinite);
}

历史

  • 2009/01/16:修复了源代码下载链接。
© . All rights reserved.