RadMenu和RadMenuItem Caliburn.Micro

时间:2013-04-09 22:02:37

标签: wpf telerik caliburn.micro radmenu

我使用Caliburn.Micro以编程方式加载radMenu而没有任何问题,Xaml看起来像这样:

<telerik:RadMenu ItemsSource="{Binding .MenuItems}"
                 VerticalAlignment="Top"
                 cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource Self}}"
                 cal:Message.Attach="[Event ItemClick] = [Action MenuItemClick($this)]">
  <telerik:RadMenu.ItemContainerStyle>
    <Style TargetType="telerik:RadMenuItem">
      <Setter Property="Tag"
              Value="{Binding .Tag}" />
      <Setter Property="Header"
              Value="{Binding .Text}" />
      <Setter Property="Icon"
              Value="{Binding .Image}" />
      <Setter Property="ItemsSource"
              Value="{Binding .SubItems}" />
      <Setter Property="Command"
              Value="{Binding .SubItems}" />
    </Style>
  </telerik:RadMenu.ItemContainerStyle>
</telerik:RadMenu>

在我的ViewModel上,我有一个相应的MenuItems属性,我从数据库中填充。代码如下所示:

Property MenuItems As New ObservableCollection(Of MenuItem)

Public Sub MenuItemClick(item As MenuItem)
    MessageBox.Show(item.Tag)
End Sub

问题是ItemClick事件的连接,我需要接收radMenuItem对象,我的意思是,我需要知道单击了哪个MenuItem。

我在Action.TargetWithoutContext属性上尝试了各种组合,到目前为止,我只获取了MenuItems集合。

提前致谢

1 个答案:

答案 0 :(得分:1)

点击的项目将位于属性RadRoutedEventArgs中事件回调的OriginalSource中。 e.g。

void RadMenu_ItemClick(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
    var menuItem = e.OriginalSource as RadMenuItem;
}

RadRoutedEventArgs子类System.Windows.EventArgs起,您应该能够从中提取OriginalSource

有几种方法(我认为其中一种方法是更好方法)

方法1(在我喜欢的VM中工作但是工作太多了):

只需将事件args传递给VM上的处理程序方法,然后就可以在VM代码中提取所选项目

cal:Message.Attach="[Event ItemClick] = [Action MenuItemClick($eventargs)]"

public class ViewModel 
{
    public void MenuItemClick(System.Windows.EventArgs e) 
    {
        var menuItem = e.OriginalSource;
        // menuItem should be RadMenuItem, you can use FrameworkElement base type to get DataContext
        var fe = menuItem as FrameworkElement;
        var data = fe.DataContext; // (obviously do your null checks etc!)
    }
}

但是,这有一个问题,就是要让VM关注如何从eventargs中获取所选项目。我不喜欢这种方法,因为如果你改变你正在使用的控件等,它很容易破坏。

方法2:

我假设您的MenuItem类是自定义类?您真的不想在VM代码中依赖第三方类型(如果您更改为其他控制提供程序,例如Infragistics或您有什么),那么您应该将实际绑定对象传递回您的viewmodel 。如果不是这种方法仍然有效(但你最终会在你的虚拟机中使用RadMenuItem引用)

您可以通过自定义Caliburn.Micro的MessageBinder.SpecialValues集合来提取原始资源或实际绑定项目,然后将所选项目直接传递给VM。 (您可以将此代码放入Bootstrapper某处)

以下是获取绑定到所选菜单项的数据项的方法:

MessageBinder.SpecialValues.Add("$selecteditem", (context) =>
{
    if (context.EventArgs is EventArgs)
    {          
        var e = context.EventArgs as EventArgs;

        // If the control is a FrameworkElement it will have a DataContext which contains the bound item
        var fe = e.OriginalSource as FrameworkElement;

        if (fe != null)
            return fe.DataContext;
    }

    return null;
});

如果您想要实际的RadMenuItem本身,您只需将上述实现更改为:

MessageBinder.SpecialValues.Add("$selecteditem", (context) =>
{
    if (context.EventArgs is EventArgs)
    {          
        var e = context.EventArgs as EventArgs;
        return e.OriginalSource;
    }

    return null;
});

并在XAML中使用:

cal:Message.Attach="[Event ItemClick] = [Action MenuItemClick($selecteditem)]"

这种方法的好处是ViewModel只接收绑定项,并且不需要知道如何提取值:

public class ViewModel 
{
    public void MenuItemClick(TheActualTypeThatWasBound item) 
    {
        // Do stuff with item
    }
}

除非你传回实际的菜单项:

public class ViewModel 
{
    public void MenuItemClick(RadMenuItem item) 
    {
        // Do stuff with item
        var boundData = item.DataContext;
    }
}

但我强烈建议不要这样做(我有一个相当不错的大小项目使用Rad控件,我从来不需要从VM中引用任何Rad控件)

抱歉,我不能真正使用它,因为我不使用VB,但你可以在这个网站上转换:

http://www.developerfusion.com/tools/convert/vb-to-csharp/

声明:

$selecteditem可能是一个坏名字 - 也许是$originalsourcedatacontext,但这有点令人满口:)