Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF:如何使用MVVM将命令绑定到ListBoxItem?_C#_Wpf_Mvvm_Binding_Commandbinding - Fatal编程技术网

C# WPF:如何使用MVVM将命令绑定到ListBoxItem?

C# WPF:如何使用MVVM将命令绑定到ListBoxItem?,c#,wpf,mvvm,binding,commandbinding,C#,Wpf,Mvvm,Binding,Commandbinding,我刚刚开始学习MVVM。我遵循这一点从零开始制作了这个应用程序(我强烈建议所有MVVM初学者使用它)。基本上,到目前为止,我已经创建了两个文本框,用户在其中添加数据,一个保存数据的按钮,随后用所有输入填充列表框 这里是我遇到的问题:我希望能够双击ListBoxItem并触发我创建并添加到ViewModel中的命令。我不知道如何完成XAML端,也就是说,我不知道如何将该命令绑定到ListBox(项) 这里是XAML: ... <ListBox Name="EntriesListBo

我刚刚开始学习MVVM。我遵循这一点从零开始制作了这个应用程序(我强烈建议所有MVVM初学者使用它)。基本上,到目前为止,我已经创建了两个文本框,用户在其中添加数据,一个保存数据的按钮,随后用所有输入填充列表框

这里是我遇到的问题:我希望能够双击ListBoxItem并触发我创建并添加到ViewModel中的命令。我不知道如何完成XAML端,也就是说,我不知道如何将该命令绑定到ListBox(项)

这里是XAML:

...
<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");
    }
}

请帮忙,我会很感激的

由于双击事件,这变得很棘手。有几种方法可以做到这一点:

  • 在代码隐藏中处理双击事件,然后在ViewModel上手动调用命令/方法
  • 使用附加的行为来路由
  • 使用混合行为来
  • 2和3可能更纯粹,但坦率地说,1更容易,不那么复杂,而且不是世界上最糟糕的事情。对于一次性案例,我可能会使用方法1

    现在,如果您将需求更改为在每个项目上使用(比如)超链接,则会更容易。首先在XAML中命名根元素-例如,对于窗口:

    <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>