Link to home
Start Free TrialLog in
Avatar of GRChandrashekar
GRChandrashekarFlag for India

asked on

C# trigger button click on function key press

Hi, I am Developing C# winform Application. I need to use function keys to trigger button click event. for that I have written code as below.

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (keyData == (Keys.Control | Keys.F))
        {
            button1.PerformClick();
        }
        return base.ProcessCmdKey(ref msg, keyData);
    }

Open in new window


My Problem is I do not want to write this code in every form, instead write a class file for this and call the same from the form.

Request help on
1. How to convert to class file.
2. How to call from the form.
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

You could use IMessageFilter() to trap the Function keys and Form.ActiveForm to grab the current form.  If you make all the forms implement a custom Interface then you can send the keystroke to a special method that decides what to do with it.

Realistically, though, that amount of code is trivial, and you're going to need code in every form anyways to receive any kind of notification for function keys!...regardless of where/how they are trapped.
Avatar of GRChandrashekar

ASKER

Any idea how to use IMessageFilter()  in a class file
Do you want button1 on the current form to be run?...or will each form have different actions/buttons?
Each form button name will be same like

btnsave
btnclose
btndelete
btnclear
Try this out then.  I've given the code for simply F1 corresponding to clicking on btnSave.  It correctly clicked btnSave on the active form (either Form1 or Form2 in this example):
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
            MyFilter mf = new MyFilter();
            mf.F1 += new MyFilter.dlgF1(mf_F1);
            Application.AddMessageFilter(mf);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Form2 f2 = new Form2();
            f2.Show();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Form1.btnSave");
        }

        private void mf_F1()
        {
            Form frm = Form.ActiveForm;
            if (frm != null)
            {
                Control[] matches = frm.Controls.Find("btnSave", true);
                if (matches.Length > 0 && matches[0] is Button)
                {
                    Button btn = (Button)matches[0];
                    btn.PerformClick();
                }
            }
        }

    }

Open in new window

    public class MyFilter : IMessageFilter
    {

        public delegate void dlgF1();
        public event dlgF1 F1;

        private const int WM_KEYUP = 0x101;

        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == WM_KEYUP)
            {
                Keys key = (Keys)(int)m.WParam & Keys.KeyCode;
                if (key == Keys.F1)
                {
                    if (F1 != null)
                    {
                        F1();
                        return true;
                    }
                }
            }
            return false; 
        }

    }

Open in new window

    public partial class Form2 : Form
    {

        public Form2()
        {
            InitializeComponent();
        }

        private void btnsave_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Form2.btnSave");
        }

    }

Open in new window

Well, without using Form1 and Form2, if I need to implement this directly in Form 2 having save button how can I do this
Not sure I follow.  If Form2 is active, then the btnSave button on Form2 will be clicked.  Whatever is coded for that button will run.

You don't need to make any changes on Form2 for this to happen.

You don't need Form1 either.  Basically whatever form is the "startup form" needs to register the MessageFilter with this code:
            MyFilter mf = new MyFilter();
            mf.F1 += new MyFilter.dlgF1(mf_F1);
            Application.AddMessageFilter(mf);

Open in new window


Then, in that same form, it needs to handle the F1 event:
        private void mf_F1()
        {
            Form frm = Form.ActiveForm;
            if (frm != null)
            {
                Control[] matches = frm.Controls.Find("btnSave", true);
                if (matches.Length > 0 && matches[0] is Button)
                {
                    Button btn = (Button)matches[0];
                    btn.PerformClick();
                }
            }
        }

Open in new window

Sorry need little more detail.

My solution start up form is LoginForm.

My form which has btnsave is titleform.

So you mean i need to the following in login form

  MyFilter mf = new MyFilter();
            mf.F1 += new MyFilter.dlgF1(mf_F1);
            Application.AddMessageFilter(mf);

Open in new window


and then in titleform I add this ?
private void mf_F1()
        {
            Form frm = Form.ActiveForm;
            if (frm != null)
            {
                Control[] matches = frm.Controls.Find("btnSave", true);
                if (matches.Length > 0 && matches[0] is Button)
                {
                    Button btn = (Button)matches[0];
                    btn.PerformClick();
                }
            }
        }

Open in new window

The two need to be in the same form together.

That form should be one that stays "alive" for the entire app.  If you close the login form then it might make more sense to put that code into the title form (assuming you don't need those shortcuts while the login form is open).
Sorry for late response. Since my MDI form always remains open i have put the code as follows

     private void MdiParentLoad(object sender, EventArgs e)
        {
            IMessageFilter mf = new MyFilter();
            mf.F1 += new MyFilter.dlgF1(MfF1);
            Application.AddMessageFilter(mf);
        }
        private static void MfF1()
        {
            var frm = ActiveForm;
            if (frm != null)
            {
                var matches = frm.Controls.Find("btnSave", true);
                if (matches.Length > 0 && matches[0] is Button)
                {
                    var btn = (Button)matches[0];
                    btn.PerformClick();
                }
            }
        }

Open in new window


The very first line IMessageFilter mf = new MyFilter(); shows error for MyFilter. Error:Cannot resolve symbol "MyFilter"
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
buttons we're clicking only in the MdiChildren?
WOW ! Works like a charm. Great is that person who invented this site and greatest are experts like you :)

Before I close this question I last help is needed.

If I have add another one like F2 for btndelete how can I do that? after I learn this i can go on adding further :)

Hope you wont mind answering this last question of mine
Follow the pattern in MyFilter and declare another event for "F2":
        public delegate void dlgF1();
        public event dlgF1 F1;

        public delegate void dlgF2();
        public event dlgF2 F2;

Open in new window


Then add a check for F2 in PreFilterMessage():
        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == WM_KEYUP)
            {
                Keys key = (Keys)(int)m.WParam & Keys.KeyCode;
                if (key == Keys.F1)
                {
                    if (F1 != null)
                    {
                        F1();
                        return true;
                    }
                }
                else if (key == Keys.F2)
                {
                    if (F2 != null)
                    {
                        F2();
                        return true;
                    }
                }
            }
            return false;
        }

Open in new window


Finally, in the MdiParent form, you need to wire up that new event and handle it accordingly:
        private void MdiParentLoad(object sender, EventArgs e)
        {
            MyFilter mf = new MyFilter();
            mf.F1 += new MyFilter.dlgF1(MfF1);
            mf.F2 += new MyFilter.dlgF1(MfF2);
            Application.AddMessageFilter(mf);
        }

        private void MfF1()
        {
            var frm = this.ActiveMdiChild;
            if (frm != null)
            {
                var matches = frm.Controls.Find("btnSave", true);
                if (matches.Length > 0 && matches[0] is Button)
                {
                    var btn = (Button)matches[0];
                    btn.PerformClick();
                }
            }
        }

        private void MfF2()
        {
            var frm = this.ActiveMdiChild;
            if (frm != null)
            {
                var matches = frm.Controls.Find("btndelete", true);
                if (matches.Length > 0 && matches[0] is Button)
                {
                    var btn = (Button)matches[0];
                    btn.PerformClick();
                }
            }
        }

Open in new window


*Note that the controls names are case-sensitive so if your button is named "btnDelete" and you search for "btndelete" then it won't work.
**You could make a function that receives the control name and returns the matching control.  That would make the code shorter and reduce the repetition.
You mean make function for this line var matches = frm.Controls.Find("btnSave", true);
This whole snippet can be its own function as it is repeated and the only difference is the string used for the button name:
            var frm = this.ActiveMdiChild;
            if (frm != null)
            {
                var matches = frm.Controls.Find("btndelete", true);
                if (matches.Length > 0 && matches[0] is Button)
                {
                    var btn = (Button)matches[0];
                    btn.PerformClick();
                }
            }

Open in new window

Wish I could award even more points :) excellent guidance.