WPF: How to bind a command to the ListBoxItem using MVVM?

后端 未结 4 668
栀梦
栀梦 2020-12-01 04:53

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

相关标签:
4条回答
  • 2020-12-01 05:13

    This is made tricky because of the DoubleClick event. There are a few ways to do this:

    1. Handle the double-click event in code behind, and then manually invoke a command/method on your ViewModel
    2. Use an attached behavior to route the DoubleClick event to your Command
    3. Use a Blend Behavior to map the DoubleClick event to your command

    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:

    <Window .... Name="This">
    

    Now, in the DataTemplate for your ListBox items, use something like this:

    <ListBox ...>
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Hyperlink 
            Command="{Binding ElementName=This, Path=DataContext.OpenEntryCmd}"
            Text="{Binding Path=Name}" 
            />
    

    The ElementName binding lets you resolve the OpenEntryCmd from the context of your ViewModel, rather than the specific data item.

    0 讨论(0)
  • 2020-12-01 05:15

    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 your ListBoxItems don't fill the entire container.

    Basically, just create a DataTemplate for your ListBox that is composed of a TextBlock and bind the width of the TextBlock to the width of the ListBox, set the margins and padding to 0, and disable horizontal scrolling (because the TextBlock will bleed beyond the visible bounds of the ScrollView 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 the ListBoxItem, which I can live with.

    Here is an example:

    <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>
    
    0 讨论(0)
  • 2020-12-01 05:23

    Unfortunately, only ButtonBase derived controls have the possibility for binding ICommand objects to their Command properties (for the Click event).

    However, you can use an API provided by Blend to map an event (like in your case MouseDoubleClick on the ListBox) to an ICommand object.

    <ListBox>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <i:InvokeCommandAction Command="{Binding YourCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListBox>
    

    You'll have to define: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" and have a reference to System.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 :)

    0 讨论(0)
  • 2020-12-01 05:29

    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:

    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>
    

    Usage:

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

    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.

    0 讨论(0)
提交回复
热议问题