I use the following code to log every click in our WinForms application. In essence it looks up a control from its HWND and then prints the types and names of the control and all of its parents. Something like MainForm"myWindow">TabPanel"mainTab">Button"save"
internal class ClickLogger : IMessageFilter
{
    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_LBUTTONDBLCLK = 0x0203;
    private const int WM_RBUTTONDOWN = 0x0204;        
    private const int MaxRecurseDepth = 30;
    private readonly ILogger _log;
    public ClickLogger(ILogger logger)
    {
        _log = logger;
    }
    [DebuggerStepThrough]
    public bool PreFilterMessage(ref Message message)
    {
        if (message.Msg == WM_LBUTTONDOWN
            || message.Msg == WM_RBUTTONDOWN
            || message.Msg == WM_LBUTTONDBLCLK)
        {
            string path = "Unknown";
            Control ctl = Control.FromHandle(message.HWnd);
            if (ctl != null)
            {
                path = PathFromControl(ctl, MaxRecurseDepth).ToString();
            }
            string logEntry = string.Format("{0} Click on {1}",
                WndMsgToClickName(message.Msg), path);
            if (_log.IsInfoEnabled)
            {
                _log.Info(logEntry);
            }
        }
        return false;
    }
    private StringBuilder PathFromControl(Control control, int maxDepth)
    {
        if(maxDepth == 0)
        {
            _log.Warn("Max recursion {0} reached whilst resolving path of control", MaxRecurseDepth);
            return new StringBuilder("ERR");
        }
        string name = control.GetType().Name;
        if (control.Name.IsNotBlank())
        {
            name = name + "\"" + control.Name + "\"";
        }
        if (control.Parent != null && control.Parent != control)
        {
            return PathFromControl(control.Parent, maxDepth - 1).Append(">").Append(name);
        }
        return new StringBuilder(name);
    }
    public void Initialize()
    {
        Application.AddMessageFilter(this);
    }
    private static string WndMsgToClickName(int msgId)
    {
        switch (msgId)
        {
            case WM_LBUTTONDOWN:
                return "Left";
            case WM_LBUTTONDBLCLK:
                return "Double";
            case WM_RBUTTONDOWN:
                return "Right";
            default:
                return "0x" + Convert.ToString(msgId, 16);
        }
    }
}
Recently we've started to mix WPF and WinForms and the above click logger simply prints "Unknown" for any click on a WPF control.
Is there a way I can perform a similar trick for WPF controls? A method that would work across technologies would be great.