WPF:如何使用 MVVM 将命令绑定到 ListBoxItem?
我刚刚开始学习MVVM。我按照这个 MVVM 教程(我强烈向所有 MVVM 初学者推荐它)。基本上,到目前为止我创建的是几个文本框,用户在其中添加他或她的数据,一个用于保存该数据的按钮,该按钮随后将使用所做的所有条目填充列表框。
这就是我陷入困境的地方:我希望能够双击 ListBoxItem 并触发我创建并添加到 ViewModel 中的命令。我不知道如何完成 XAML 方面,即我不知道如何将该命令绑定到 ListBox(Item)。
这是 XAML:
...
<ListBox
Name="EntriesListBox"
Width="228"
Height="208"
Margin="138,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Entries}" />
...
这是 ViewModel:
public class MainWindowViewModel : DependencyObject
{
...
public IEntriesProvider Entries
{
get { return entries; }
}
private IEntriesProvider entries;
public OpenEntryCommand OpenEntryCmd { get; set; }
public MainWindowViewModel(IEntriesProvider source)
{
this.entries = source;
...
this.OpenEntryCmd = new OpenEntryCommand(this);
}
...
}
最后,这是我希望在用户双击 EntriesListBox 中的项目后执行的 OpenEntryCommand:
public class OpenEntryCommand : ICommand
{
private MainWindowViewModel viewModel;
public OpenEntryCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return parameter is Entry;
}
public void Execute(object parameter)
{
string messageFormat = "Subject: {0}\nStart: {1}\nEnd: {2}";
Entry entry = parameter as Entry;
string message = string.Format(messageFormat,
entry.Subject,
entry.StartDate.ToShortDateString(),
entry.EndDate.ToShortDateString());
MessageBox.Show(message, "Appointment");
}
}
请帮忙,我将不胜感激。
I have just started learning MVVM. I've made the application from scratch by following this MVVM tutorial (I highly recommend it to all MVVM beginners out there). Basically, what I have created so far is a couple of text boxes where user adds his or her data, a button to save that data which subsequently populates the ListBox with all entries made.
Here's where I got stuck: I want to be able to double-click on a ListBoxItem and to trigger a command that I have created and added to my ViewModel. I don't know how to finish the XAML side, i.e. I don't know how to bind that command to the ListBox(Item).
Here's XAML:
...
<ListBox
Name="EntriesListBox"
Width="228"
Height="208"
Margin="138,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Entries}" />
...
Here's ViewModel:
public class MainWindowViewModel : DependencyObject
{
...
public IEntriesProvider Entries
{
get { return entries; }
}
private IEntriesProvider entries;
public OpenEntryCommand OpenEntryCmd { get; set; }
public MainWindowViewModel(IEntriesProvider source)
{
this.entries = source;
...
this.OpenEntryCmd = new OpenEntryCommand(this);
}
...
}
And finally, here's the OpenEntryCommand that I want to be executed once the user double-clicks the item in the EntriesListBox:
public class OpenEntryCommand : ICommand
{
private MainWindowViewModel viewModel;
public OpenEntryCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return parameter is Entry;
}
public void Execute(object parameter)
{
string messageFormat = "Subject: {0}\nStart: {1}\nEnd: {2}";
Entry entry = parameter as Entry;
string message = string.Format(messageFormat,
entry.Subject,
entry.StartDate.ToShortDateString(),
entry.EndDate.ToShortDateString());
MessageBox.Show(message, "Appointment");
}
}
Please help, I'd appreciate it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
不幸的是,只有
ButtonBase
派生控件才有可能将ICommand
对象绑定到其Command
属性(对于Click
事件) )。但是,您可以使用 Blend 提供的 API 将事件(例如
ListBox
上的MouseDoubleClick
)映射到ICommand
对象。您必须定义:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
并引用System.Windows.Interactivity.dll
。- 编辑 -
这是 WPF4 的一部分,但如果您不使用 WPF4,则可以使用 Microsoft.Windows.Interactivity。这个dll来自Blend SDK,它不需要Blend,来自这里:
http://www .microsoft.com/downloads/en/details.aspx?FamilyID=f1ae9a30-4928-411d-970b-e682ab179e17&displaylang=en
更新:我找到了一些可以帮助您的内容。检查 此链接MVVM Light Toolkit 包含有关如何执行此操作的演练,以及 链接到所需的库。 MVVM Light Toolkit 是一个非常有趣的框架,用于将 MVVM 与 Silverlight、WPF 和 WP7 一起应用。
希望这有帮助:)
Unfortunately, only
ButtonBase
derived controls have the possibility for bindingICommand
objects to theirCommand
properties (for theClick
event).However, you can use an API provided by Blend to map an event (like in your case
MouseDoubleClick
on theListBox
) to anICommand
object.You'll have to define:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
and have a reference toSystem.Windows.Interactivity.dll
.-- EDIT --
This is part of WPF4, but u can use Microsoft.Windows.Interactivity if you're not using WPF4. This dll is from Blend SDK, which doesn't require Blend, from here:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=f1ae9a30-4928-411d-970b-e682ab179e17&displaylang=en
Update: I found something that should help you. check this link on MVVM Light Toolkit which contains a walkthrough on how to do this, along with a link to the needed libraries. MVVM Light Toolkit is a very interesting framework for applying MVVM with Silverlight, WPF, and WP7.
Hope this helps :)
由于 DoubleClick 事件,这变得很棘手。有几种方法可以做到这一点:
2 和 3 可能更纯粹,但坦率地说,1 更容易,更简单,而且不是世界上最糟糕的事情。对于一次性情况,我可能会使用方法#1。
现在,如果您更改要求以在每个项目上使用超链接等,那就更容易了。首先在 XAML 中命名根元素 - 例如,对于窗口:
现在,在 ListBox 项的 DataTemplate 中,使用如下内容:
ElementName 绑定允许您从 ViewModel 的上下文中解析 OpenEntryCmd,而不是从具体数据项。
This is made tricky because of the DoubleClick event. There are a few ways to do this:
2 and 3 might be more pure, but frankly, 1 is easier, less complex, and not the worst thing in the world. For a one-off case, I'd probably use approach #1.
Now, if you changed your requirements to use, say, a hyperlink on each item, it would be easier. Start out by naming the root element in your XAML - e.g., for a Window:
Now, in the DataTemplate for your ListBox items, use something like this:
The ElementName binding lets you resolve the OpenEntryCmd from the context of your ViewModel, rather than the specific data item.
编辑:我作为一个没有经验的 WPF 开发人员写了这篇文章,现在我要么使用提供事件到命令绑定的框架,要么简单地使用按钮并重新设计它的样式。当然,为了获得最大的灵活性,这可能更好。
我发现最好的方法是为我的内容创建一个简单的用户控件包装器,并使用命令和参数的依赖属性。
我这样做的原因是因为按钮没有将单击事件冒泡到我的列表框,这阻止了它选择列表框项。
CommandControl.xaml.cs:
CommandControl.xaml:
用法:
内容可以是任何内容,当单击该控件时,它将执行该命令。
编辑:向 UserControl 添加了
Background="Transparent"
以在控件的整个区域上启用单击事件。EDIT: I wrote this post as an inexperienced WPF developer, nowadays I'd either use a framework that provides event to command binding, or simply use a button and restyle it. Of course for maximum flexibility this is maybe better.
I find the best way to do this is to create a simple user control wrapper for my content, with dependency properties for the command and parameter.
The reason I did this was due to the Button not bubbling the click event to my ListBox which prevented it from selecting the ListBoxItem.
CommandControl.xaml.cs:
CommandControl.xaml:
Usage:
The Content can be whatever, and when the control is clicked, it will execute the command.
EDIT: Added
Background="Transparent"
to UserControl to enable click events on the entire area of the control.这有点破解,但效果很好,允许您使用命令并避免代码隐藏。这还有一个额外的好处,即当您在空的
ScrollView
区域中双击(或无论您的触发器是什么)时,不会触发命令,假设您的ListBoxItems
没有填充整个容器。基本上,只需为由
TextBlock
组成的ListBox
创建一个DataTemplate
并绑定TextBlock
的宽度即可为ListBox
的宽度,将边距和填充设置为 0,并禁用水平滚动(因为TextBlock
会超出ScrollView< 的可见边界) /code> 否则触发水平滚动条)。我发现的唯一错误是,如果用户精确单击
ListBoxItem
的边框,则该命令不会触发,这是我可以忍受的。这是一个例子:
This is a bit of a hack, but it works well and allows you to use commands and avoid code behind. This also has the added benefit of not triggering the command when you double-click (or whatever your trigger is) in the empty
ScrollView
area assuming yourListBoxItems
don't fill the entire container.Basically, just create a
DataTemplate
for yourListBox
that is composed of aTextBlock
and bind the width of theTextBlock
to the width of theListBox
, set the margins and padding to 0, and disable horizontal scrolling (because theTextBlock
will bleed beyond the visible bounds of theScrollView
triggering the horizontal scroll bar otherwise). The only bug I've found is that the command won't fire if the user clicks precisely on the border of theListBoxItem
, which I can live with.Here is an example:
我最近还需要在双击
ListBoxItem
时触发ICommand
。就我个人而言,我不喜欢
DataTemplate
方法,因为它绑定到ListBoxItem
容器内的内容,而不是容器本身。我选择使用附加属性在容器上分配InputBinding
。这需要更多的努力,但效果很好。首先,我们需要创建一个附加属性类。我已经针对从
FrameworkElement
派生的任何类更通用地创建了我的类,以防万一我再次以不同的视觉效果遇到这种情况。最后一步是覆盖
ListBoxItem
的基本容器样式。现在,只要双击
ListBoxItem
,它就会触发OnListBoxItemDoubleClickCommand
。I recently needed to trigger an
ICommand
upon double clicking aListBoxItem
as well.Personally, I don't like the
DataTemplate
method as it is binding to the content inside theListBoxItem
container, and not the container itself. I've opted to use an Attached Property to assign anInputBinding
on the container. It takes a little more elbow grease, but it works well.First, we need to create an attached property class. I've created mine a little more generically towards any class that derives from
FrameworkElement
, just in case I run into this again with a different visual.Then the final step is to override the base container style for the
ListBoxItem
.Now, anytime a
ListBoxItem
is double clicked, it will fire ourOnListBoxItemDoubleClickCommand
.如果您正在寻找一个很好的简单解决方案,该解决方案使用交互而不是与用户控件、代码隐藏、输入绑定、自定义附加属性等混在一起。
并且您想要在 ListBoxItem 级别(即不是按照 ListBox 级别)工作的东西(错误地)接受的解决方案。
然后这是一个简单的“类似按钮”单击操作的片段。
注意,需要background =“Transparent”以确保整个网格可单击,而不仅仅是其中的内容。
If you're looking for a nice simple solution that uses interactions instead of mucking about with user controls, code behind, input bindings, custom attached properties, etc.
And you want something that works at the ListBoxItem level, i.e. not ListBox level as per the (incorrectly) accepted solution.
Then here's a snippet for a simple 'button like' click action..
Note, background="Transparent" is required to ensure the entire Grid is clickable and not just the contents inside.