Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'System.StackOverflowException' occurred in PresentationCore.dll

I have a WPF UserControl in which I try to implement the custom MouseClick (because there is no MouseClick event on a WPF (User)Control) event.

I got the following:

alt text

Some code:

/// <summary>
/// Occurs when users left clicks the MyControl.
/// </summary>
public event MouseButtonEventHandler MouseClick { add { AddHandler(MouseClickEvent, value); } remove { RemoveHandler(MouseClickEvent, value); } }

    protected virtual void OnMouseClick(MouseButtonEventArgs e)
    {
        base.RaiseEvent(e);
        //this.RaiseEvent(new RoutedEventArgs(MouseClickEvent, this));
    }

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonUp(e);
        if (!this.movedAfterMouseDown)
        {
            OnMouseClick(e);
        }
        this.gotLeftButtonDown = false;
        this.movedAfterMouseDown = false;
    }

So, where is the problem?

UPDATE 1

protected virtual void OnMouseClick(MouseButtonEventArgs e)
{
    //base.RaiseEvent(e);
    MouseButtonEventArgs args = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, e.ChangedButton);
    this.RaiseEvent(args);
}

Value cannot be null. Parameter name: routedEvent

alt text

UPDATE 2

An other custom event I implemented successfully (work without problems) - SelectedChanged:

static void OnIsSelectedChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
    var s = (MyControl)source;

    s.RaiseEvent(new RoutedEventArgs(SelectedChangedEvent, s));
}

UPDATE 3

System.Windows.Controls.Control's OnPreviewMouseDoubleClick implementation:

protected virtual void OnPreviewMouseDoubleClick(MouseButtonEventArgs e)
{
    base.RaiseEvent(e);
}

Update 5 (for people in the tank)

class Foo : FrameworkElement
{
    event EasterCameEvent; // I named it MouseClick

    public DoSomething()
    {
        EasterCameArgs args= ...

        if (Date.Now = EasterDate)
            OnEasterCame(args)
    }

    protected virtual void OnEasterCame(EasterCameArgs e)
    {
        base.RaiseEvent(e);
    }
}
like image 332
serhio Avatar asked Nov 21 '10 00:11

serhio


3 Answers

protected virtual void OnMouseClick(MouseButtonEventArgs e)
{
    MouseButtonEventArgs args = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, e.ChangedButton);

    // Don't forget this
    args.RoutedEvent = MouseClickEvent;

    base.RaiseEvent(args);
}
like image 105
moldovanu Avatar answered Nov 06 '22 02:11

moldovanu


You need to remove the base.RaiseEvent(e) from ALL parts of your custom user control. That is the cause of the stack overflow.

If you're inheriting from UserControl, the click events are already implemented for you. You do NOT need to re-implement. You MAY need to handle them though, but most probably you do not.

protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
    // handle the event if you need to do something with the data.
    // this is not over-riding the event, this is attaching a custom handler to the event
    this.gotLeftButtonDown = false;
    this.movedAfterMouseDown = false;
}

This does not over-ride the event. This is a handler for when the event is raised! Users of your control will write handlers just like this one. Don't reimplement it. Don't even handle it unless you need to do something with the data. These events are written for you already.

Edit:

While my answer was wrong in that it didn't fix the issue, it should still be helpful to understand WHY the stackoverflow exception was happening.

protected virtual void OnMouseClick(MouseButtonEventArgs e)
{
    base.RaiseEvent(e);
    /* this will raise the OnMouseLeftButtonUp event if the MouseButtonEventArgs
       designates that it came from the MouseLeftButtonUp event. 
       That will then call the OnMouseLeftButtonUp because e.RoutedEvent equals
       MouseLeftButtonUpEvent.

       The accepted answer does the following, to stop OnMouseLeftButtonUp being
       called again, and the whole process repeating itself in an infinite loop.

       args.RoutedEvent = MouseClickEvent;
       base.RaiseEvent(e); // No longer fires MouseLeftButtonUp, breaking cycle.

       // Changes the event to be fired, avoiding the cycle of
       // OnMouseLeftButtonUp -> OnMouseClick -> OnMouseLeftButtonUp etc
    */
}

protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
    base.OnMouseLeftButtonUp(e);
    if (!this.movedAfterMouseDown)
    {
        OnMouseClick(e); // This will call OnMouseClick handler
    }
    this.gotLeftButtonDown = false;
    this.movedAfterMouseDown = false;
}

The control flow was:

  1. User generates MouseLeftButtonUpEvent
  2. OnMouseClick is called within OnMouseLeftButtonUp handler
  3. OnMouseClick raises MouseLeftButtonUp event
  4. Goto 2.

The accepted answer changes to this:

  1. User generates MouseLeftButtonUpEvent
  2. OnMouseClick is called within OnMouseLeftButtonUp handler
  3. OnMouseClick changes the routed event to MouseClickEvent
  4. OnMouseClick raises MouseClickEvent
  5. Control resumes

This is what I was trying to explain in comments on other answers. If I wasn't clear enough I apologise for that. I believe that serhio and I were out of sync in that I was trying to explain the cause of the stackvoverflow, when he was looking for a fix to the code. Correct me if I'm wrong.

like image 28
Josh Smeaton Avatar answered Nov 06 '22 04:11

Josh Smeaton


I think it's in this line:

base.RaiseEvent(e);

If you are handling the mouse click you don't want to raise the event again as that will simply call your handler again, which will raise the event....

like image 2
ChrisF Avatar answered Nov 06 '22 02:11

ChrisF