Question Details

No question body available.

Tags

c# wpf mouseevent

Answers (2)

January 19, 2026 Score: 2 Rep: 120,894 Quality: Medium Completeness: 100%

It looks like you have encountered some sort of bug with WPF. The fact that the problem is reproducible if you call MessageBox.Show() from inside a PreviewMouseDown event but not if you call, say, Console.WriteLine() argues for this being a bug rather than an intentional limitation.

You could consider opening an issue, but rather than fighting the framework I would suggest an alternative approach, which is to bind your ToggleButton to some bool IsChecked property in a view model, then display your message box from within the setter. In comments this was noted to work by XAMlMAX but in practice this requires a bit of setup because, due to separation-of-concerns, the view model should not know anything about the GUI view itself. So as suggested by Philip Stuyck in their answer to Confirmation Window In MVVM design pattern you will need to inject a service into your view model to display the message box.

To do this, first define the following service in, say, ServiceDefinitions.cs (replace PreviewMouseDown with the overall namespace of your app), typically in some lower-level project referenced by the startup project:

namespace PreviewMouseDown.ServiceDefinitions;

public interface IDialogService { bool MessageBoxGetYesNo(string messageBoxText, string caption); void MessageBoxShow(string messageBoxText); }

Then in a separate file ServiceImplementations.cs (typically not in the service definition project but maybe in the startup project) implement them as follows:

using System.Windows;
using PreviewMouseDown.ServiceDefinitions;

namespace PreviewMouseDown.ServiceImplementations;

public class DialogService(Window window) : IDialogService { public bool MessageBoxGetYesNo(string messageBoxText, string caption) => MessageBox.Show(window, messageBoxText, caption, MessageBoxButton.YesNo) == MessageBoxResult.Yes; public void MessageBoxShow(string messageBoxText) => MessageBox.Show(window, messageBoxText); }

OK, having done that, you can create a view model with two bool properties, one that shows a message box and one that does not. This would typically not be in the startup project but must have visibility on the project containing IDialogService because you will inject your concrete dialog service when the view model is constructed:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using PreviewMouseDown.ServiceDefinitions;

namespace PreviewMouseDown.ViewModels;

public class MyViewModel(IDialogService dialogService) : ViewModelBase(dialogService) { public bool IsCheckedWithConfirmation { get => field; set { if (value && !field) { if (!DialogService.MessageBoxGetYesNo("Do you really want to check this button?", "Confirmation")) return; } else if (!value && field) { if (!DialogService.MessageBoxGetYesNo("Do you really want to uncheck this button?", "Confirmation")) return; } SetField(ref field, value); } }

public bool IsCheckedWithoutConfirmation { get => field; set => SetField(ref field, value); } }

public abstract class ViewModelBase(IDialogService dialogService) : INotifyPropertyChanged { protected IDialogService DialogService => dialogService;

public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } }

(Note - I modified the simple message box to a confirmation box for testing purposes.)

OK, now that all that is done, modify your MainWindow.xaml to bind to your view model properties IsCheckedWithConfirmation and IsCheckedWithoutConfirmation like so:

With PreviewMouseDown

No PreviewMouseDown

And bind your view model in MainWindow.xaml.cs like so:

using System.Windows;
using PreviewMouseDown.ViewModels;
using PreviewMouseDown.ServiceImplementations;

namespace PreviewMouseDown;

public partial class MainWindow : Window { public MainWindow() { InitializeComponent();

this.DataContext = new MyViewModel(new DialogService(this)); } }

And now everything works! If I check or uncheck the "With PreviewMouseDown", a confirmation message box will get displayed asking the user to confirm their choice. When accepted, the ToggleButton state changes:

Screen shot showing

Notes:

  1. The fact that calling MessageBox.Show() from inside PreviewMouseDown mysteriously cancels the CheckedEvent looks to be a MSFT bug. However, I've noticed a couple of bugs with your code that are not MSFT problems:

    • The PreviewMouseDown fires for any type of mouse down event including a middle-button click, a right-button click, a click on any of the functional keys on my Logitech mouse and maybe touchpad or stylus events also. If you want to display a confirmation dialog if and only if the user is checking or unchecking the ToggleButton you will need to look at the event details inside the MouseButtonEventArgs to ensure that specific event corresponds to a CheckedEvent.

    • Your PreviewMouseDown code is not called at all in the event the user uses keyboard navigation to navigate to, and check, your toggle button. Running the original code from your question, I used Tab to navigate to the "With PreviewMouseDown" button, then Space to click it -- and the PreviewMouseDown event was not invoked. Instead the button was clicked unconditionally.

      I suppose you could disable keyboard navigation for your dialog box, but please don't -- it can cause accessibility problems for people who have trouble making manual mouse picks.

    By moving the dialog display into the IsChecked property of the view model, my code avoids both these problems.

  2. Tested using net10.0-windows

Update

If (as noted in subsequently in comments) you are binding directly to your model and so don't have view models from which you can display your MessageBox, I was able to reproduce the required behavior by doing everything manually in the Checked and Unchecked routed events.

Say your model looks like this:

public class MyModel
{
    public bool IsCheckedWithConfirmation { get; set; }
    public bool IsCheckedWithoutConfirmation { get; set; }
}

If I modify the XAML as follows:

With PreviewMouseDown

No PreviewMouseDown

And rewrite MainWindow.xaml.cs as follows:

using System.Windows;
using PreviewMouseDown.ViewModels;
using PreviewMouseDown.ServiceImplementations;
using System.Windows.Controls.Primitives;

namespace PreviewMouseDown;

public partial class MainWindow : Window { public MainWindow() { InitializeComponent();

// For testing purposes check that everything works properly when IsCheckedWithConfirmation is set to true initially. this.DataContext = new MyModel { IsCheckedWithConfirmation = true }; }

void ToggleButtonCheckedWithConfirmation(object sender, RoutedEventArgs e) { if (DataContext is MyModel model) { if (!model.IsCheckedWithConfirmation && MessageBox.Show("Do you really want to check this button?", "Confirmation", MessageBoxButton.YesNo) != MessageBoxResult.Yes) // Cancel Checked event ((ToggleButton)sender).IsChecked = false; else // Manually propagate to model model.IsCheckedWithConfirmation = true; } }

void ToggleButtonUncheckedWithConfirmation(object sender, RoutedEventArgs e) { if (DataContext is MyModel model) { if (model.IsCheckedWithConfirmation && MessageBox.Show("Do you really want to uncheck this button?", "Confirmation", MessageBoxButton.YesNo) != MessageBoxResult.Yes) // Cancel Unchecked event ((ToggleButton)sender).IsChecked = true; else // Manually propagate to model model.IsCheckedWithConfirmation = false; } } }

Then a confirmation dialog is displayed before checking and unchecking the "With PreviewMouseDown" toggle as required.

Notes:

  1. BindingMode.OneTime is used to populate the user interface from the model initially, after which it becomes the responsibility of the Checked and Unchecked handlers to keep the UI and model synchronized.

  2. Both mouse and keyboard navigation work correctly.

January 19, 2026 Score: 1 Rep: 8,805 Quality: Low Completeness: 60%

This is related to the interception of Mouse Event routing.

First, a tunneled "Preview" event occurs from the Window level to the target element. In the target element, tunneling stops, and a Bubbling Mouse Event is raised.

At the button level, the bubbling mouse event is intercepted and marked as "handled." After this, a Click event is raised in place of this event. Therefore, it is important that a bubbling event is raised in the target element after tunneling.

For a new window (not necessarily a MessageBox), a mouse click needs to be tracked, for example, to close the window. But the new window is opened "within the tunneled PreviewMouseDown event." This means that the Mouse Click for the new window must be processed, first tunneled, then bubble up. After that, the OS must "recollect" that the PreviewMouseDown event for the "old" window has not yet completed, continue it, and then raise the bubbling event. To implement this, it would be necessary to organize some kind of event queue (stack), monitor their nesting, etc. This is too complex and redundant for most scenarios. Such logic would be required in exceptional, unique cases. In practice, I, and I think almost everyone in the developer community, have never needed something like this.

Test examples for clarification:

        private void ToggleButtonOnPreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            new Window().ShowDialog();
        }

private void OutputText(object sender, System.Windows.Input.MouseButtonEventArgs e) { output.AppendText(((TextBlock) sender).Text + " | " + e.RoutedEvent.Name + " | " + Environment.NewLine); }

    

P.S. If necessary, if there is no other solution, you can raise the Click event after closing the model dialog of another Window.
But you'll have to use reflection to "extract" the protected method.

        private static readonly Action onClick = typeof(ButtonBase)
            .GetMethod("OnClick", BindingFlags.Instance | BindingFlags.NonPublic)!
            .CreateDelegate();

private void ToggleButtonOnPreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { new Window().ShowDialog(); ButtonBase button = (ButtonBase)sender; onClick(button); }

Or you can create a derived class from ToggleButton in which you make the OnClick method public.