C# WPF:如何使用MVVM将命令绑定到ListBoxItem?
我刚刚开始学习MVVM。我遵循这一点从零开始制作了这个应用程序(我强烈建议所有MVVM初学者使用它)。基本上,到目前为止,我已经创建了两个文本框,用户在其中添加数据,一个保存数据的按钮,随后用所有输入填充列表框 这里是我遇到的问题:我希望能够双击ListBoxItem并触发我创建并添加到ViewModel中的命令。我不知道如何完成XAML端,也就是说,我不知道如何将该命令绑定到ListBox(项) 这里是XAML:C# WPF:如何使用MVVM将命令绑定到ListBoxItem?,c#,wpf,mvvm,binding,commandbinding,C#,Wpf,Mvvm,Binding,Commandbinding,我刚刚开始学习MVVM。我遵循这一点从零开始制作了这个应用程序(我强烈建议所有MVVM初学者使用它)。基本上,到目前为止,我已经创建了两个文本框,用户在其中添加数据,一个保存数据的按钮,随后用所有输入填充列表框 这里是我遇到的问题:我希望能够双击ListBoxItem并触发我创建并添加到ViewModel中的命令。我不知道如何完成XAML端,也就是说,我不知道如何将该命令绑定到ListBox(项) 这里是XAML: ... <ListBox Name="EntriesListBo
...
<ListBox
Name="EntriesListBox"
Width="228"
Height="208"
Margin="138,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Entries}" />
...
最后,这里是OpenEntryCommand,我希望在用户双击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");
}
}
请帮忙,我会很感激的 由于双击事件,这变得很棘手。有几种方法可以做到这一点:
<Window .... Name="This">
现在,在ListBox项的DataTemplate中,使用如下内容:
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<Hyperlink
Command="{Binding ElementName=This, Path=DataContext.OpenEntryCmd}"
Text="{Binding Path=Name}"
/>
ElementName绑定允许您从ViewModel的上下文而不是特定的数据项解析OpenEntryCmd 不幸的是,只有
按钮库
派生控件才有可能将ICommand
对象绑定到其命令
属性(对于单击
事件)
但是,您可以使用Blend提供的API将事件映射到ICommand
对象(例如在您的示例中,ListBox
上的MouseDoubleClick
)
<ListBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding YourCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
您必须定义:xmlns:i=”http://schemas.microsoft.com/expression/2010/interactivity“
并有对System.Windows.Interactivity.dll
的引用
--编辑--
这是WPF4的一部分,但如果不使用WPF4,则可以使用Microsoft.Windows.Interactive。此dll来自Blend SDK,它不需要Blend,如下所示: 更新:我发现了一些应该对你有帮助的东西。检查其中包含如何执行此操作的演练,以及指向所需库的链接。MVVMLightToolkit是一个非常有趣的框架,用于将MVVM与Silverlight、WPF和WP7一起应用
希望这有帮助:)我发现最好的方法是为我的内容创建一个简单的用户控件包装器,其中包含命令和参数的依赖属性 我这样做的原因是按钮没有将单击事件冒泡到我的ListBox,这阻止了它选择ListBoxItem CommandControl.xaml.cs:
public partial class CommandControl : UserControl
{
public CommandControl()
{
MouseLeftButtonDown += OnMouseLeftButtonDown;
InitializeComponent();
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
if (Command != null)
{
if (Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
}
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand),
typeof(CommandControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object),
typeof(CommandControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
}
CommandControl.xaml:
<UserControl x:Class="WpfApp.UserControls.CommandControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="Transparent">
</UserControl>
用法:
<ListBoxItem>
<uc:CommandControl Command="{Binding LoadPageCommand}"
CommandParameter="{Binding HomePageViewModel}">
<TextBlock Text="Home" Margin="0,0,0,5" VerticalAlignment="Center"
Foreground="White" FontSize="24" />
</uc:CommandControl>
</ListBoxItem>
内容可以是任何内容,当单击控件时,它将执行命令
编辑:向UserControl添加了
Background=“Transparent”
,以便在控件的整个区域启用单击事件。这是一个小技巧,但它工作良好,允许您使用命令并避免代码隐藏。这还有一个额外的好处,即在空的ScrollView
区域中双击(或无论触发器是什么)时,如果ListBoxItems
没有填满整个容器,则不会触发命令
基本上,只需为由TextBlock
组成的ListBox
创建一个DataTemplate
,将TextBlock
的宽度绑定到ListBox
的宽度,将边距和填充设置为0,并禁用水平滚动(因为TextBlock
将溢出ScrollView
的可见边界,否则将触发水平滚动条)。我发现的唯一错误是,如果用户精确单击ListBoxItem
的边框,该命令将不会触发,我可以接受
以下是一个例子:
<ListBox
x:Name="listBox"
Width="400"
Height="150"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ItemsSource="{Binding ItemsSourceProperty}"
SelectedItem="{Binding SelectedItemProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Padding="0"
Margin="0"
Text="{Binding DisplayTextProperty}"
Width="{Binding ElementName=listBox, Path=Width}">
<TextBlock.InputBindings>
<MouseBinding
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectProjectCommand}"
Gesture="LeftDoubleClick" />
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
如果您正在寻找一个简单的解决方案,它使用交互而不是用户控件、代码隐藏、输入绑定、自定义附加属性等 您需要的是在ListBoxItem级别工作的东西,即根据(错误地)接受的解决方案,不是ListBox级别的东西 下面是一个简单的“按钮式”点击操作的片段
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<!-- insert your visuals here -->
<b:Interaction.Triggers>
<b:EventTrigger EventName="MouseUp">
<b:InvokeCommandAction Command="{Binding YourCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
注意,需要background=“Transparent”来确保整个网格是可点击的,而不仅仅是其中的内容。我最近还需要在双击
列表框项目时触发ICommand
就个人而言,我不喜欢DataTemplate
方法,因为它绑定到ListBoxItem
容器中的内容,而不是容器本身。我选择使用附加属性在容器上分配InputBinding
。这需要更多的时间,但效果很好
首先,我们需要创建一个附加的属性类。我创建的属性类对从FrameworkElement
派生的任何类都有一点通用性,以防我再次以不同的视觉方式遇到它
public class FrameworkElementAttachedProperties : DependencyObject
{
public static readonly DependencyProperty DoubleClickProperty = DependencyProperty.RegisterAttached("DoubleClick", typeof(InputBinding),
typeof(FrameworkElementAttachedProperties), new PropertyMetadata(null, OnDoubleClickChanged));
public static void SetDoubleClick(FrameworkElement element, InputBinding value)
{
element.SetValue(DoubleClickProperty, value);
}
public static InputBinding GetDoubleClick(FrameworkElement element)
{
return (InputBinding)element.GetValue(DoubleClickProperty);
}
private static void OnDoubleClickChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = obj as FrameworkElement;
/// Potentially throw an exception if an object is not a FrameworkElement (is null).
if(e.NewValue != null)
{
element.InputBindings.Add(e.NewValue as InputBinding);
}
if(e.OldValue != null)
{
element.InputBindings.Remove(e.OldValue as InputBinding);
}
}
}
最后一步是覆盖ListBoxItem的基本容器样式
public class FrameworkElementAttachedProperties : DependencyObject
{
public static readonly DependencyProperty DoubleClickProperty = DependencyProperty.RegisterAttached("DoubleClick", typeof(InputBinding),
typeof(FrameworkElementAttachedProperties), new PropertyMetadata(null, OnDoubleClickChanged));
public static void SetDoubleClick(FrameworkElement element, InputBinding value)
{
element.SetValue(DoubleClickProperty, value);
}
public static InputBinding GetDoubleClick(FrameworkElement element)
{
return (InputBinding)element.GetValue(DoubleClickProperty);
}
private static void OnDoubleClickChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = obj as FrameworkElement;
/// Potentially throw an exception if an object is not a FrameworkElement (is null).
if(e.NewValue != null)
{
element.InputBindings.Add(e.NewValue as InputBinding);
}
if(e.OldValue != null)
{
element.InputBindings.Remove(e.OldValue as InputBinding);
}
}
}
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"
BasedOn="{StaticResource ListBoxItem}">
<Setter Property="local:FrameworkElementAttachedProperties.DoubleClick">
<Setter.Value>
<MouseBinding Command="{Binding OnListBoxItemDoubleClickCommand}"
MouseAction="LeftDoubleClick"/>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>