Pass-Through Mouse Events to Parent Control

Pass-through mouse events to parent control

Yes. After a lot of searching, I found the article "Floating Controls, tooltip-style", which uses WndProc to change the message from WM_NCHITTEST to HTTRANSPARENT, making the Control transparent to mouse events.

To achieve that, create a control inherited from Label and simply add the following code.

protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 0x0084;
const int HTTRANSPARENT = (-1);

if (m.Msg == WM_NCHITTEST)
{
m.Result = (IntPtr)HTTRANSPARENT;
}
else
{
base.WndProc(ref m);
}
}

I have tested this in Visual Studio 2010 with .NET Framework 4 Client Profile.

C# Forms: Raise mouse event (pass from child to parent)

In this example I have handled the TextBoxes MouseDown event. From here, you can raise the MouseDown event of the UserControl that holds your TextBox.

public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}

private void textBox1_MouseDown(object sender, MouseEventArgs e)
{
OnMouseDown(e); // Goes through as a MouseDown Event from UserControl1
}
}

Depending on your requirements, this may not work for you as when the MouseDown of the UserControl is handled, it will come through as being originated by UserControl (The sender parameter will refer to UserControl1.

I also extracted the OnMouseDown implementation of the Control class to see if it could be used:

        // Extracted using Reflection
// This will not compile as Control.EventMouseDown is a private member
System.Windows.Forms.MouseEventHandler mouseEventHandler = (System.Windows.Forms.MouseEventHandler)this.Events[System.Windows.Forms.Control.EventMouseDown];
if (mouseEventHandler == null)
return;
mouseEventHandler(sender, e);

Unfortunately, the events are stored in a Private member and not readily accessible.

If you want to know and handle the MouseDown event differently if it originates from a TextBox, you will have to declare and raise a custom event.

Declare Custom Event

public event EventHandler<MouseEventArgs> TextBoxMouseDownEvent;

Raise Custom Event from TextBox_MouseDown

private void textBox1_MouseDown(object sender, MouseEventArgs e)
{
EventHandler<MouseEventArgs> handler = TextBoxMouseDownEvent;
if (handler != null)
{
handler(sender, e);
}
}

Pass through click event to control underneath

In order to make your control transparent for mouse click events, you can pass all the click events to the parent control appropriate method. So, first create an interface that will declare HandleMouseClick method.

interface IMouseClickable
{
void HandleMouseClick(object sender, MouseEventArgs e);
}

Apply this interface to the parent Control, so that you can pass this Control to GamePiece. It could be done via the constructor and you can immedietly register parent HandleMouseClick to the GamePiece.

...
public GamePiece(IMouseClickable parent)
{
MouseClick += parent.HandleMouseClick;
}
...

Pass-through mouse click on label to the parent element

Why not try this :

  label.Click -= label_Click;

As you know += crates an event , so , if i replace + with a -,the result is obvious right ?

UPDATE

I don't quiet get the term pass through click but if it is that u have a control beneath the label and u want to rather perform click on that,isn't it obvious to do something like this ?

  private void beneathControl_click()
{
allCodesInAnotherMethod();
}

private void label_click()
{
allCodesInAnotherMethod();
}

private void allCodesInAnotherMethod()
{
///all codes here
}

Parent Control Mouse Enter/Leave Events With Child Controls

After more research, I discovered the Application.AddMessageFilter method. Using this, I created a .NET version of a mouse hook:

class MouseMessageFilter : IMessageFilter, IDisposable
{
public MouseMessageFilter()
{
}

public void Dispose()
{
StopFiltering();
}

#region IMessageFilter Members

public bool PreFilterMessage(ref Message m)
{
// Call the appropriate event
return false;
}

#endregion

#region Events

public class CancelMouseEventArgs : MouseEventArgs
{...}

public delegate void CancelMouseEventHandler(object source, CancelMouseEventArgs e);
public event CancelMouseEventHandler MouseMove;
public event CancelMouseEventHandler MouseDown;
public event CancelMouseEventHandler MouseUp;

public void StartFiltering()
{
StopFiltering();
Application.AddMessageFilter(this);
}

public void StopFiltering()
{
Application.RemoveMessageFilter(this);
}
}

Then, I can handle the MouseMove event in my container control, check to see if the mouse is inside my parent control, and start the work. (I also had to track the last moused over parent control so I could stop the previously started parent.)

---- Edit ----

In my form class, I create and hookup the filter:

public class MyForm : Form
{
MouseMessageFilter msgFilter;

public MyForm()
{...
msgFilter = new MouseMessageFilter();
msgFilter.MouseDown += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseDown);
msgFilter.MouseMove += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseMove);
}

private void msgFilter_MouseMove(object source, MouseMessageFilter.CancelMouseEventArgs e)
{
if (CheckSomething(e.Control)
e.Cancel = true;
}
}

Make MouseWheel event pass from child to parent

All credit goes to Hans Passant (again), taken from the thread he suggested: https://stackoverflow.com/a/3562449/17034

Allowing the the containing panel to take focus worked fine. For the demo code above the class needs no changes, just use it for only the containing panel. I had to make some tweaks to my project to call focus when necessary, but it was far from rocket science.

Thanks again.

Pass click event of child control to the parent control

While you can interact with parent form directly from child, It's better to raise some events by child control and subscribe for the events in parent form.

Raise event from Child:

public event EventHandler CloseButtonClicked;
protected virtual void OnCloseButtonClicked(EventArgs e)
{
CloseButtonClicked.Invoke(this, e);
}
private void CloseButton_Click(object sender, EventArgs e)
{
//While you can call `this.ParentForm.Close()` but it's better to raise the event
//Then handle the event in the form and call this.Close()

OnCloseButtonClicked(e);
}

Note: To raise the XXXX event, that's enough to invoke the XXXX event delegate; the reason of creating the protected virtual OnXXXX is just to follow the pattern to let the derivers override the method and customize the behavior before/after raising the event.

Subscribe and use event in Parent:

//Subscribe for event using designer or in constructor or form load
this.userControl11.CloseButtonClicked += userControl11_CloseButtonClicked;

//Close the form when you received the notification
private void userControl11_CloseButtonClicked(object sender, EventArgs e)
{
this.Close();
}

To learn more about events, take a look at:

  • Handling and raising events
  • Standard .NET event pattern

Allow mouse events to pass through semitransparent Popup

Thanks to the comment made by @NETscape I tried to get a hold of the actual Window for the Popup. It works with the following code (after the Popup is initially shown I might add..)

// FromVisual can take any Visual inside the Popup..
HwndSource popupHwndSource = HwndSource.FromVisual(rectangle) as HwndSource;

Combining this with the answer to this question I created an attached behavior which adds the property IsPopupEventTransparent which can be set on any child in the Popup. At first, this solution caused a bug which forced the users to click twice to re-activate the Window after the Popup lost its event transparency. I solved this with a "Mouse.Capture-workaround".

elementInPopup.Dispatcher.BeginInvoke(new Action(() =>
{
Keyboard.Focus(elementInPopup);
Mouse.Capture(elementInPopup);
elementInPopup.ReleaseMouseCapture();
}));

Usable like this

<Popup AllowsTransparency="True"
...>
<Border inf:PopupBehavior.IsPopupEventTransparent="{Binding SomeProperty}"
BorderThickness="1"
BorderBrush="Black"
Background="White"
Margin="0 0 8 8">
<Border.Effect>
<DropShadowEffect ShadowDepth="2.25"
Color="Black"
Opacity="0.4"
Direction="315"
BlurRadius="4"/>
</Border.Effect>
<!--...-->
</Border>
</Popup>

PopupBehavior

public class PopupBehavior
{
public static readonly DependencyProperty IsPopupEventTransparentProperty =
DependencyProperty.RegisterAttached("IsPopupEventTransparent",
typeof(bool),
typeof(PopupBehavior),
new UIPropertyMetadata(false, OnIsPopupEventTransparentPropertyChanged));

public static bool GetIsPopupEventTransparent(DependencyObject obj)
{
return (bool)obj.GetValue(IsPopupEventTransparentProperty);
}
public static void SetIsPopupEventTransparent(DependencyObject obj, bool value)
{
obj.SetValue(IsPopupEventTransparentProperty, value);
}
private static void OnIsPopupEventTransparentPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = target as FrameworkElement;
if ((bool)e.NewValue == true)
{
FrameworkElement topParent = VisualTreeHelpers.GetTopParent(element) as FrameworkElement;
topParent.Opacity = 0.4;
HwndSource popupHwndSource = HwndSource.FromVisual(element) as HwndSource;
WindowHelper.SetWindowExTransparent(popupHwndSource.Handle);
}
else
{
FrameworkElement topParent = VisualTreeHelpers.GetTopParent(element) as FrameworkElement;
topParent.Opacity = 1.0;
HwndSource popupHwndSource = HwndSource.FromVisual(element) as HwndSource;
WindowHelper.UndoWindowExTransparent(popupHwndSource.Handle, element);
}
}
}

WindowHelper based on the answer to this this question

public static class WindowHelper
{
private static Dictionary<IntPtr, int> _extendedStyleHwndDictionary = new Dictionary<IntPtr, int>();

const int WS_EX_TRANSPARENT = 0x00000020;
const int GWL_EXSTYLE = (-20);

[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

public static void SetWindowExTransparent(IntPtr hwnd)
{
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
if (_extendedStyleHwndDictionary.Keys.Contains(hwnd) == false)
{
_extendedStyleHwndDictionary.Add(hwnd, extendedStyle);
}
}

public static void UndoWindowExTransparent(IntPtr hwnd, FrameworkElement elementInPopup)
{
if (_extendedStyleHwndDictionary.Keys.Contains(hwnd) == true)
{
int extendedStyle = _extendedStyleHwndDictionary[hwnd];
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle);
// Fix Focus problems that forces the users to click twice to
// re-activate the window after the Popup loses event transparency
elementInPopup.Dispatcher.BeginInvoke(new Action(() =>
{
Keyboard.Focus(elementInPopup);
Mouse.Capture(elementInPopup);
elementInPopup.ReleaseMouseCapture();
}));
}
}
}


Related Topics



Leave a reply



Submit