简体   繁体   中英

Binding to a mouse event in WPF MVVM

I try to use System.Windows.Interactivity to bind mouse events of elements on the screen to some command logic.

I have a simple Canvas with three circles. A command is implemented that decreases the radius of the circles. This works fine when bound to the command property of a Button .

Unfortunately, when I try to bind this command to the PreviewMouseDown event of the Canvas , it doesn't work anymore. What am I missing?

Here is the MainWindow.xaml:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:test="clr-namespace:Test"
    Title="MainWindow" Height="550" Width="525">
<Window.Resources>
    <test:ViewModel x:Key="viewobj"/>
</Window.Resources>
<Grid>
    <ItemsControl ItemsSource="{Binding CircleItems, Source={StaticResource viewobj}}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Background="Black" ClipToBounds="True" HorizontalAlignment="Left" Height="400" Margin="50,20,0,0" VerticalAlignment="Top" Width="400">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="PreviewMouseDown" >
                            <i:InvokeCommandAction Command="{Binding StartCommand}" />
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding X}"/>
                <Setter Property="Canvas.Top" Value="{Binding Y}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Ellipse Width="{Binding Radius}" Height="{Binding Radius}" Fill="Red"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <Button Content="Button" Command="{Binding StartCommand, Source={StaticResource viewobj}}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="189,474,0,0"/>
</Grid>
</Window>

The MainWindow.xaml.cs is empty except for initialization in accordance to MVVM principles:

using System.Windows;

namespace Test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

This is the ViewModel.cs:

using System.Collections.ObjectModel;
using System.ComponentModel;
using Test.Model;

namespace Test
{
    public class ViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<CircleItem> CircleItems { get; set; }

        private ButtonCommand _StartCommand;
        public ButtonCommand StartCommand
        {
            get { return _StartCommand; }
        }

        public ViewModel()
        {
            _StartCommand = new ButtonCommand(UpdateMap, () => {return true;});
            CircleItems = new ObservableCollection<CircleItem>();
            CircleItems.Add(new CircleItem(20, 20, 40));
            CircleItems.Add(new CircleItem(60, 60, 50));
            CircleItems.Add(new CircleItem(120, 100, 30));
        }

        public void UpdateMap()
        {
            CircleItem.UpdateMap(CircleItems);
        }

        internal void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
            { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

The CircleItem.cs class:

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace Test.Model
{
    public class CircleItem : INotifyPropertyChanged
    {
        private double _x;
        public double X
        {
            get { return _x; }
            set
            {
                if (_x != value)
                {
                    _x = value;
                    RaisePropertyChanged("X");
                }
            }
        }

        private double _y;
        public double Y
        {
            get { return _y; }
            set
            {
                if (_y != value)
                {
                    _y = value;
                    RaisePropertyChanged("Y");
                }
            }
        }

        private double _radius;
        public double Radius
        {
            get { return _radius; }
            set
            {
                if (_radius != value)
                {
                    _radius = value;
                    RaisePropertyChanged("Radius");
                }
            }
        }

        public CircleItem(double x, double y, double radius)
        {
            this.X = x;
            this.Y = y;
            this.Radius = radius;
        }

        public static void UpdateMap(ObservableCollection<CircleItem> coll)
        {
            foreach (var item in coll)
            {
                item.Radius -= 1;
            }
        }

        internal void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
            { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

And a simple RelayCommand.cs class:

using System;
using System.Windows.Input;

namespace Test
{
    public class ButtonCommand : ICommand
    {
        private Action WhattoExecute;
        private Func<bool> WhentoExecute;
        public ButtonCommand(Action What, Func<bool> When)
        {
            WhattoExecute = What;
            WhentoExecute = When;
        }
        public bool CanExecute(object parameter)
        {
            return WhentoExecute();
        }
        public void Execute(object parameter)
        {
            WhattoExecute();
        }

        public event EventHandler CanExecuteChanged;
    }
}

Please note that the NuGet Package "System.Windows.Interactivity v4.0 for WPF" has to be installed for this example to work.

You forgot to set the Source of the Binding :

<Canvas Background="Black" ClipToBounds="True" HorizontalAlignment="Left" Height="400" Margin="50,20,0,0" VerticalAlignment="Top" Width="400">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PreviewMouseDown">
            <i:InvokeCommandAction Command="{Binding StartCommand, Source={StaticResource viewobj}}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Canvas>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM