问题描述
我使用Caliburn.Micro以编程方式加载了radMenu,没有问题,Xaml看起来像这样:
I load programatically a radMenu with no problems using Caliburn.Micro, the Xaml looks like this:
<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属性。代码如下所示:
On my ViewModel I have the corresponding MenuItems property which I fill from a database. The code looks like this:
Property MenuItems As New ObservableCollection(Of MenuItem)
Public Sub MenuItemClick(item As MenuItem)
MessageBox.Show(item.Tag)
End Sub
问题是ItemClick事件的接线,我需要接收radMenuItem对象,我的意思是,我需要知道单击了哪个MenuItem。
The problem is the wiring of the ItemClick Event, I need to receive the radMenuItem object, I mean, I need to know which MenuItem was clicked.
I'v尝试对Action.TargetWithoutContext属性进行各种组合,到目前为止,我仅获得MenuItems集合。
I'v tried various combinations on the Action.TargetWithoutContext property, so far, I've only get the MenuItems collection.
预先感谢
推荐答案
所单击的项目将位于属性 OriginalSource中的事件回调的
。 RadRoutedEventArgs
中
例如
The clicked item will be in the RadRoutedEventArgs
on the event callback in the property OriginalSource
.e.g.
void RadMenu_ItemClick(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
var menuItem = e.OriginalSource as RadMenuItem;
}
由于 RadRoutedEventArgs
子类 System.Windows.EventArgs
,您应该可以从任何一个中提取 OriginalSource
。
Since RadRoutedEventArgs
subclasses System.Windows.EventArgs
you should be able to extract OriginalSource
from either.
有几种方法(我认为其中一种是更好的方法)
There are a couple of approaches (one of which I think is the better way to do it)
方法1(虽然可以,但是我喜欢在VM中工作太多):
只需将事件args传递到VM上的处理程序方法,然后您可以在VM代码中提取所选项目
Just pass the event args to the handler method on your VM, then you can extract the selected item in the VM code
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中获取所选项目。我不太喜欢这种方法,因为如果您更改正在使用的控件等,它很容易破坏。
However, this has the problem that it leaves the VM the concern of figuring out how to get the selected item from the eventargs. I don't like this approach much as it's prone to breaking if you change the control you are using etc.
方法2:
我假设您的 MenuItem
类是自定义类?您实际上并不想在您的VM代码中依赖第三者类型(以防您更改为另一个控件提供者,例如Infragistics或您拥有的东西),因此应该在单击时将实际的绑定对象传递回您的视图模型。如果不能,这种方法仍然可以使用(但是在VM中最终会得到 RadMenuItem
引用)
I assume that your MenuItem
class is a custom class? You don't really want to put a dependency on 3rd party types in your VM code (in case you change to another control provider such as Infragistics or what have you) so you should be passing the actual bound object back to your viewmodel on click. If not this approach will still work (but you'll end up with a RadMenuItem
reference in your VM)
您可以通过自定义Caliburn.Micro的 MessageBinder.SpecialValues
集合来提取原始资源或实际绑定项目,然后将所选项目直接传递给VM。 (您可以将此代码放入 Bootstrapper
的某个地方)
You can extract the originalsource or actual bound item by customising Caliburn.Micro's MessageBinder.SpecialValues
collection, and then pass the selected item directly to the VM. (You can put this code into your Bootstrapper
somewhere)
这里是将数据项绑定到所选菜单项:
Here is the approach to get the data item bound to the selected menu item:
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
本身,可以将上述实现更改为:
If you want the actual RadMenuItem
itself you can just change the above implementation to:
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只是接收绑定的项目,而无需知道如何提取值:
The good thing about this approach is that the ViewModel simply receives the bound item and it doesn't need to know about how to extract the value:
public class ViewModel
{
public void MenuItemClick(TheActualTypeThatWasBound item)
{
// Do stuff with item
}
}
除非您当然要传回实际的菜单项:
Unless of course you are passing back the actual menu item:
public class ViewModel
{
public void MenuItemClick(RadMenuItem item)
{
// Do stuff with item
var boundData = item.DataContext;
}
}
但我强烈建议您不要这样做(我有一个相当不错的使用Rad控件的项目,而且我从不需要在VM中引用任何Rad控件)
But I strongly suggest not to do this (I've got a pretty decent sized project using Rad controls and I've never needed a reference to any Rad control from within the VM)
对不起,我无法真正对其进行VB编程因为我不使用VB,但是您可以在此网站上进行转换:
Sorry I can't really VB it as I don't use VB but you can convert on this site:
免责声明:
$ selecteditem
可能是一个不好的名字-也许 $ originalsourcedatacontext
,但是有点大:)
$selecteditem
is probably a bad name for it - maybe $originalsourcedatacontext
but that's a bit of a mouthful :)
这篇关于RadMenu和RadMenuItem Caliburn.Micro的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!