Search code examples
wpfbuttonmouseenter

WPF: MouseEnter doesn't work on several buttons when mouse is pressed



I have a list of toggle-buttons in wpf and I want the user to be able to toggle several buttons by dragging over them. To do this, I used the MouseEnter-Event for each button. This does work, when I press the mousebutton outside the buttons and start dragging. But when I press the mousebutton on a button and start dragging, the MouseEnter-Event is only fired for the first button, where I pressed the mousebutton (also none of the other events like mouseover or mousemove are fired).
Here's the code:

public void AddButton()
{
    ToggleButton btn = new ToggleButton();
    btn.MouseEnter += VisibilityButton_Enter;
    this.gridButtons.Children.Add(btn);
}

private void VisibilityButton_Enter(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed)
    {
        ToggleButton btn = sender as ToggleButton;
        btn.IsChecked = !btn.IsChecked;
    }
}

I found a solution to use "drag and drop" and the dragover-event, but I think there must be an easier solution?


Solution

  • As Kent mentioned, the ToggleButton captures the mouse. If we handle the PreviewMouseDown event ourselves we can prevent that. The rest is just keeping track of the mouse state so the we don't click twice during a single roll-over. Here is a behavior you can add to your button to enable roll-over clicking.

    First add this namespace:

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    

    and the corresponding reference to your project.

    Then the XAML looks like this (notice the RollOverBehavior):

    <Grid>
        <ItemsControl>
            <ItemsControl.ItemsSource>
                <PointCollection>
                    <Point/>
                    <Point/>
                    <Point/>
                    <Point/>
                    <Point/>
                </PointCollection>
            </ItemsControl.ItemsSource>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ToggleButton Width="25" Height="25">
                        <i:Interaction.Behaviors>
                            <local:RollOverBehavior/>
                        </i:Interaction.Behaviors>
                    </ToggleButton>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
    

    and here is the behavior itself:

    public class RollOverBehavior : Behavior<ToggleButton>
    {
        bool mouseOver;
        bool clicked;
    
        protected override void OnAttached()
        {
            AssociatedObject.PreviewMouseLeftButtonDown += (s, e) =>
            {
                AssociatedObject.IsChecked = !AssociatedObject.IsChecked;
                e.Handled = true;
            };
            AssociatedObject.MouseEnter += (s, e) =>
            {
                mouseOver = true;
                clicked = false;
            };
            AssociatedObject.MouseLeave += (s, e) =>
            {
                mouseOver = false;
            };
            AssociatedObject.MouseMove += (s, e) =>
            {
                if (mouseOver && !clicked && e.LeftButton == MouseButtonState.Pressed)
                {
                    AssociatedObject.IsChecked = !AssociatedObject.IsChecked;
                    clicked = true;
                }
            };
        }
    }