29

I have one UserControl in which I am using a Canvas, and in that Canvas one Rectangle. I want to create a click event for that user control (Canvas and Rectangle) which I then want to use in the main window.

The question is: I want to create a new click event for the UserControl. How to do it? Kindly show little example or the code.

Sabuncu
  • 5,095
  • 5
  • 55
  • 89
Kiranaditya
  • 449
  • 1
  • 5
  • 15

3 Answers3

70

A brief example on how to expose an event from the UserControl that the main window can register:

In your UserControl:

1 . Add the following declaration:

public event EventHandler UserControlClicked;

2 . In your UserControl_Clicked event raise the event like this:

 private void UserControl_MouseDown(object sender, MouseButtonEventArgs e)
 {
        if (UserControlClicked != null)
        {
            UserControlClicked(this, EventArgs.Empty);
        }
  }

In your MainWindow:

Your usercontrol will now have a UserControlClicked event which you can register to:

<local:UserControl1 x:Name="UC" UserControlClicked="UC_OnUserControlClicked" />
Sabuncu
  • 5,095
  • 5
  • 55
  • 89
Blachshma
  • 17,097
  • 4
  • 58
  • 72
  • Would you need to unsubscribe this registration? – Werner Sep 03 '18 at 09:25
  • 1
    For sake of completeness, the `UserControl_MouseDown` event also needed to tied to the actual `MouseDown` event in XAML obviously for this to work. – zar Jun 29 '20 at 21:38
  • I can't get this to work. It tells me UserControlClicked is not accessible or not recognized. – Rafael Ventura Sep 24 '20 at 13:39
  • @zar the other possibility is to override the current OnMouseDown handler `protected override void OnMouseDown(MouseButtonEventArgs e)` and invoking `base.OnMouseDown(e)` inside. – Mike Apr 14 '21 at 10:47
0

i find this easier for passing value to handler:

public event Action<string> onUserCodeFetched;

private void btnEnterClicked(object sender, RoutedEventArgs e)
        {
            onUserCodeFetched(PersonellCode.Text);
            PersonellCode.Text = "";
        }
DAkbariSE
  • 151
  • 1
  • 13
  • 1
    I used to use `Action`s instead of `EventHandler`s, then I reconsidered that practice after reading this https://stackoverflow.com/a/1431368/740639 – Walter Stabosz Apr 14 '20 at 17:50
0

It's an old question, but I also found useful to bypass the event using a property-like custom event as in here. You can use a general EventHandler or you can be more specific (RoutedEventHandler, MouseButtonEventHandler, etc) to avoid later casts. For instance, to bypass a click event, you could write in your custom control class:

        public readonly object objectLock = new object();
        public event RoutedEventHandler CustomClick
        {
            add{  lock (objectLock) { myClickableInnerControl.Click += value;}}
            remove {lock (objectLock) { myClickableInnerControl.Click -= value; }}
        }

and for a PreviewMouseDown event, it would be something like:

        public readonly object objectLock = new object();
        public event MouseButtonEventHandler CustomPreviewMouseDown
        {
            add{  lock (objectLock) { myInnerControl.PreviewMouseDown += value;}}
            remove {lock (objectLock) { myInnerControl.PreviewMouseDown -= value; }}
        }

Where myInnerControl would be your canvas in this case.

Then you can initialize the event from xaml as

    <local:myClickableControl x:Name="ClickCtrl1" CustomClick="ClickCtrl1_CustomClick" />

or

    <local:myCustomControl x:Name="CustomCtrl1" CustomPreviewMouseDown="CustomCtrl1_CustomPreviewMouseDown" />

And from code behind:

        ClickCtrl1.CustomClick+=ClickCtrl1_CustomClick;

or

        CustomCtrl1.CustomPreviewMouseDown+=CustomCtrl1_CustomPreviewMouseDown;

You can also subscribe your callback to several inner controls events (being careful when they overlap as some events like previewMouseDown are not only fired by the front control but also by the controls underneath).

        public readonly object objectLock = new object();
        public event MouseButtonEventHandler CustomPreviewMouseDown
        {
            add{  lock (objectLock) 
                   { 
                       myInnerControl1.PreviewMouseDown += value;
                       myInnerControl2.PreviewMouseDown += value;
                   }}
            remove {lock (objectLock) 
                    { 
                        myInnerControl1.PreviewMouseDown -= value; 
                        myInnerControl2.PreviewMouseDown -= value; 
                    }}
        }

(If the inner controls partially overlap, you can use the corresponding eventArgs Handled property in your callback method to avoid repetitions)

Finally, you can add a layer of control to the event fired by your inner control:

        bool MousePreviewDownUsed = false;
        event MouseButtonEventHandler _myPreviewMouseDownEvent = null;
        public  event MouseButtonEventHandler CustomPreviewMouseDown
        {
            add
            {
                lock (objectLock)
                {
                    _myPreviewMouseDownEvent += value;

                    if (value != null && !MousePreviewDownUsed)
                    {
                        myInnerControl.PreviewMouseDown += myInnerControl_PreviewMouseDown;
                        MousePreviewDownUsed = true;
                    }
                }
            }
            remove
            {
                lock (objectLock)
                {
                   if (_myPreviewMouseDownEvent != null)
                    {
                        _myPreviewMouseDownEvent -= value;
                        if ((_myPreviewMouseDownEvent == null || 
                             _myPreviewMouseDownEvent.GetInvocationList().Length == 0) 
                              && MousePreviewDownUsed)
                        {
                            _myPreviewMouseDownEvent = null;
                            myInnerControl.PreviewMouseDown -= myInnerControl_PreviewMouseDown;                           
                            MousePreviewDownUsed = false;
                        }
                    }
                }
            }
        }


 
        private void myInnerControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            // Do some previous operations or control whether the event must be broadcasted

            _myPreviewMouseDownEvent?.Invoke(sender, e);
        }

This event is initialized from xaml or code behind the same way as before.

Amo Robb
  • 810
  • 5
  • 11