0

I am struggling to figure out why my User Control events are not executing. I have a dynamic UserControl, "MainMenu", within a dynamic UserControl, "MainControl".

In MainMenu I have the following:

public partial class MainMenu : UserControl
{
    public MainMenu()
    {
        InitializeComponent();

        ///
        ///Event Subscriptions
        /// 
        this.LostFocus += this.MainMenu_LostFocus;
    }

    public void MainMenu_LostFocus(object sender, EventArgs e)
    {
        this.Visible = false;
    }
}

In MainControl:

public partial class MainControl : UserControl
{
    private Custom_UI.MainMenu mainMenu = new Custom_UI.MainMenu();
    public MainControl()
    {
        InitializeComponent();
        mainMenu.Visible = false;
        mainMenu.BringToFront();            
        this.Controls.Add(mainMenu);
        mainMenu.BringToFront();
    }
    private void menuButton1_Click(object sender, EventArgs e)
    {
        if (mainMenu.Visible)
        {
            mainMenu.Visible = false;                
        }
        else
        {
            mainMenu.Visible = true;
            this.Focus();
        }
    }
}

And finally the main Form:

public partial class Form1 : Form
{
    MainControl mainControl = new MainControl() {
        Dock = DockStyle.Fill
    };
    public Form1()
    {
        InitializeComponent();
        this.Controls.Add(mainControl);
    }
}

So basically, the method MainMenu_LostFocus is not being invoked when I click elsewhere on the form. I have also tried using this.MouseLeave instead of this.LostFocus.

Hopefully this I was clear enough and thanks in advance.

Peter
  • 33
  • 1
  • 4

2 Answers2

0

using focus/focus lost etc was always tricky. what i could suggest is to modify your code a bit.

first of all your MainMenu needs to get focus to lose it

second of all to lose it another control needs to get the focus - and this needs to be handled by you (just clicking on MainControl won't work, you need to force it)

try this:

public partial class MainControl : UserControl
{
    private Custom_UI.MainMenu mainMenu = new Custom_UI.MainMenu();
    public MainControl()
    {
        InitializeComponent();
        mainMenu.Visible = false;
        mainMenu.BringToFront();            
        this.Controls.Add(mainMenu);
        mainMenu.BringToFront();
        this.Click += me_Click;
    }

    private void me_Click(object sender, EventArgs e)
    {
        this.Focus(); //this will cause main control to get control (if main menu is focused it'll lose focus and handle it's focus lost and set visible to false in result
    }

    private void menuButton1_Click(object sender, EventArgs e)
    {
        if (mainMenu.Visible)
        {
            mainMenu.Visible = false;   // this should work anyway
        }
        else
        {
            mainMenu.Visible = true;
            mainMenu.Focus(); //when showing mainMenu set focus to it
        }
    }
}
Jarek Kożdoń
  • 336
  • 2
  • 13
  • That makes sense. I am going to guess that "me_Click" will only be invoked for that control and none of its children. Do you think there is a simple way to invoke it regardless of the control I click on or will i have to add that method to all other controls and their child controls? – Peter Apr 25 '18 at 23:02
  • yes. All controls that do not get focus on click by default (label, panel won't get focus; text box or button will) would need similar action. that is quite annoying of course. you might try to handle all controls added to the main control (control added event) but this is quite filthy solution. i might have an idea though (gimme 15 minutes ;) ) – Jarek Kożdoń Apr 25 '18 at 23:22
  • It seems that if ".Visible = true" is set before "Focus()" it will invoke "MainMenu_LostFocus". – Peter Apr 25 '18 at 23:34
  • i've added new answer using a new "trick" thanks to that one we can avoid using that focus stuff and base on mouse left control and native button done message – Jarek Kożdoń Apr 25 '18 at 23:43
0

OK... lest try another, improved approach. the reason is the fact we don't want to be forced to handle click on each and every control added to MainControl and force it to receive focus. it's just clumsy.

We'd also like to encapsulate the whole code in a single control ( MainMenu ) - not "must have", but surely "nice to have"

this is my new idea:

 public partial class MainMenu : UserControl
    {

        //this class will be checking native windows messages, the one we're interested in is "MouseDown" with 0x0201 code 
        //(i won't be describing the whole stuff what is going inside: have that checked if you wish in documentation :)

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public class TestMessageFilter : IMessageFilter
        {
            const int WM_MOUSEMOVE = 0x200;
            const int WM_LBUTTONDOWN = 0x0201;

            public event EventHandler MouseMoved;
            public event EventHandler MouseDown;

            public bool PreFilterMessage(ref Message m)
            {
                // Blocks all the messages relating to the left mouse button.
                if (m.Msg == WM_LBUTTONDOWN)
                {
                    MouseDown?.Invoke(this, new EventArgs());
                }
                return false;
            }
        }


        public MainMenu()
        {
            InitializeComponent();

            //use our class that checks Windows Messages, subscribe it's event
            var tmf = new TestMessageFilter();
            tmf.MouseDown += UserControl1_MouseDown;
            //and add our class to Application's messages filter.
            Application.AddMessageFilter(tmf);

            //use "ordinary" events to detect mouse enter/mouse leave over control
            this.MouseLeave += UserControl1_MouseLeave;
            this.MouseEnter += UserControl1_MouseEnter;
        }

        private void UserControl1_MouseDown(object sender, EventArgs e)
        {
            //if mouse is not over control make it's visible false
            if (mouseLeft)
            {
                this.Visible = false;
            }
        }

        //variable to save mouse state (if that is over the control, or left)
        private bool mouseLeft;

        private void UserControl1_MouseEnter(object sender, EventArgs e)
        {
            mouseLeft = false;
        }


        private void UserControl1_MouseLeave(object sender, EventArgs e)
        {
            mouseLeft = true;
        }

    }

hope this solution will be better and more usable

Jarek Kożdoń
  • 336
  • 2
  • 13
  • I got it to work, thank you. The only thing I hand to do was add the events ".MouseLeave" and ".MouseEnter" to all of MainMenu's children, otherwise it would set "visible" to false if I clicked any of them. I added a recursive method to get all of child controls, return an array, then loop through them all to add the event handlers. – Peter Apr 26 '18 at 00:34
  • right haven't foresee that, glad i could help with the idea! – Jarek Kożdoń Apr 26 '18 at 08:58