Finding the name of a Control with the IsMouseOver property

Ok, I got sidetracked. I had intended to map out the layout of the controls that are common between most programs within Smart Office, but I wanted to have a bit of a play around with something a little more interesting that would get me back in to the Smart Office jscript headspace…

Wouldn’t it be nice to just move your mouse pointer over the top of a control and be able to determine the controls name? I thought it would be 🙂

The controls under the WPF framework tend to have a property called “IsMouseOver” (http://msdn.microsoft.com/en-us/library/system.windows.uielement.ismouseover.aspx), it’s a pretty handy property for quickly checking to see if…well, err, if the mouse is currently over the control.

Now as I am sure many of you are aware, controls are layered over top of each other, this is particularly so under WPF. Infact with WPF you basically have some foundation type controls and you can use that to build up some pretty complex and kewl controls, it also means that there is a huge level of customisation available without too much drama!

Why rabbit on about this? Well, the code that I am providing in this little talk will display all of the layers of controls. It also means that when we actually find the control we want, we may display some other control names that we weren’t really interested in – as an example, a Button often has a Border around it.

Yes, you can go to the effort of filtering all that out, and infact the first cut of the code only showed the relevant name, but I am far more interested in knowing what is going on with everything under the hood – background (no pun intended) information. It also means that we can do a little investigation on some of the other controls that are lower down on the presentation layer than the panel itself.

So, I can over over the “Start” or “Run Program” bar and find out the name of that control.



When you are doing this sort of thing it may be helpful to also display the object type aswell to give you a better clue as to which of these entries is infact that TextBox into which we enter commands.

To help keep the noise down a little, I have a couple of options within the code – suppression of controls without names and the ability to only search up the tree to the PART_ContentPanel

var gbFindOnlyMainControl = false; // set this to true if we don't want to go all the way up the tree 
var gbSupressNullControlNames = true; // supress the controls that don't have a name 

 
 

But if we take a look at OIS300, we want to find out the name of the status fields when the “Sorting Order” is set to “All Orders”.


I make sure that the Show Name button currently has focus. I then hover over one of the fields and hit the spacebar.


The highlighted sections indicate the field name for the Lowest Status. Typically the control name you want to reference will be below the “GridMain” object.

 

A few things about this code. I’ve added a button on to the screen, this isn’t exactly friendly to those of you who live in a mouse driven world. Basically you need to have the “Show Name” control as the control with the focus before you hover your mouse over the desired control. You can do this by hitting the tab key until it gets the little bars on the outside of the control as per below:


Then it is simply a case of moving your mouse pointer over the desired control and pressing the space bar once. Wait a couple of seconds and a MessageBox will appear exposing the VisualTree structure in all its naked glory.

A keyboard shortcut could be added, but I’m too lazy at the moment to do that. 🙂

Anyways, code is below, have fun.

 
//
// A script to show how to determine the structure of the controls
// in Smart Office and discover the Name of a specific control
//
// V001 20110304 * Completed
// 
import System;
import System.Text;
import System.Windows;
import System.Windows.Controls;
import System.Windows.Input;
import System.Windows.Media;
import System.Windows.Media.Media3D;

import MForms;
import Mango.UI.Core;
import Mango.UI.Core.Util;
import Mango.UI.Services;
import Mango.Services;

import System.Reflection;

package MForms.JScript
{
    class IsMouseOver
    {
        var gbtnShowName : Button;      // this is the button that we will be adding
        var content;
        var objTopControl;              // the topmost object we could find in the Visual Tree
        var gstrControls : String;      // and this is the string that will store the objects the mouse is over.
        var gbFindOnlyMainControl = false;  // set this to true if we don't want to go all the way up the tree
        var gbSupressNullControlNames = true;   // supress the controls that don't have a name

        public function Init(element: Object, args: Object, controller : Object, debug : Object)
        {
            content = controller.RenderEngine.Content;

            // create and add the button to the screen
            gbtnShowName = new Button();
            gbtnShowName.Content = "Show Name";
            Grid.SetColumnSpan(gbtnShowName, 15);
            Grid.SetColumn(gbtnShowName, 1);
            Grid.SetRow(gbtnShowName, 22);
         
            content.Children.Add(gbtnShowName);

            // now we find the topmost control, we will go down through the controls
            // searching for objects that we are hovering over.
            objTopControl = goUp(content);

            // monitor the click and unloaded events
            gbtnShowName.add_Click(OnShowNameClicked);
            gbtnShowName.add_Unloaded(OnShowNameUnloaded);
        }
      
        function OnShowNameClicked(sender : Object, e : RoutedEventArgs)
        {
            // clear the string that has the controls we are hovering over
            gstrControls = "";

            // now we go out and actually search down through the controls
            // adding any control which has the IsMouseOver set to true
            findControlInner(objTopControl,0,null);

            // check to see if we found anything
            if(String.IsNullOrEmpty(gstrControls) == false)
            {
                // show the message
                MessageBox.Show(gstrControls);
            }
        }
      
        function OnShowNameUnloaded(sender : Object, e : RoutedEventArgs)
        {
            if(null != gbtnShowName)
            {
                gbtnShowName.remove_Click(OnShowNameClicked);
                gbtnShowName.remove_Unloaded(OnShowNameUnloaded);
            }
        }
      
        // this function goes down the tree looking for an object which has the IsMouseOver property set to true
        private function findControlInner(parent : Object, depth : int, astrControlName : String)
        {
            var objResult = null;

            try
            {
                if(null != parent)
                {
                    // get the type of our object, we do this
                    // so we can check if the object inherits
                    // from a DependencyObject
                    var parentobjType : Type = parent.GetType();
                    if(parentobjType.IsSubclassOf(DependencyObject) == true)
                    {
                        for(var i=0; i < VisualTreeHelper.GetChildrenCount(parent);i++)
                        {
                            // retrieve the child object
                            var current : Object = VisualTreeHelper.GetChild(parent,i);
                            if(null != current)
                            {
                                // here we shall deterine the type of the new object
                                var objType = current.GetType();

                                if(null != objType)
                                {
                                    // we're looking for the Name and the
                                    // IsMouseOver properties
                                    var objPropertyName = objType.GetProperty("Name");
                                    var objPropertyIsMouseOver = objType.GetProperty("IsMouseOver");

                                    // do these properties exist on the object that we are examining?
                                    if((null != objPropertyName) && (null != objPropertyIsMouseOver))
                                    {
                                        // retrieve the Name value for the current object
                                        var strName = objPropertyName.GetValue(current);

                                        // retrieve the IsMouseOver value for the current object
                                        var strIsMouseOver = objPropertyIsMouseOver.GetValue(current);
                                        if(null != strName)
                                        {
                                            // if the IsMouseOver value = true then we should add it to our list
                                            if(0 == String.Compare(strIsMouseOver, "true", true))
                                            {
                                                var bSupressName = false;
                                                if(true == gbSupressNullControlNames)
                                                {
                                                    if(false == String.IsNullOrEmpty(strName))
                                                    {
                                                        bSupressName = false;
                                                    }
                                                    else
                                                    {
                                                        bSupressName = true;
                                                    }
                                                }
                                                if(false == bSupressName)
                                                {
                                                    gstrControls = gstrControls + "We are over: '" + strName + "'" + Environment.NewLine;
                                                }

                                            }
                                        }
                                    }
                                }
                                
                                // does the current object have any children?
                                if(VisualTreeHelper.GetChildrenCount(current) >= 1)
                                {
                                    // recurse down
                                    findControlInner(current, depth+1, astrControlName);
                                }
                            }
                        }
                    }
                }
            }
            catch(ex)
            {

            }

            return(objResult);
        }

        // go up the VisualTree
        private function goUp(aContent : Object)
        {
            var parent : Object = aContent;
            var lastParent : Object = aContent;
            var result : Object = null;

            // here we will loop UP the VisualTree seeking the very top
            while(null != (parent = VisualTreeHelper.GetParent(parent)))
            {
                lastParent = parent;

                // if gbFindOnlyMainControl is set to true then we aren't
                // interested in getting all controls, only the lower 
                // controls on the panel
                if(true == gbFindOnlyMainControl)
                {
                    var objType = lastParent.GetType();
                    var objPropertyName = objType.GetProperty("Name");
                    var strName : String = objPropertyName.GetValue(lastParent)
	 
                    if(null != strName)
                    {
                        if(String.Compare(strName,"PART_ContentPanel") == 0)
                        {
                            result = parent;
	 
                            break;
                        }
                    }
                }

            }
            
            if(false == gbFindOnlyMainControl)
            {
                if(null == parent)
                {
                    result = lastParent;
                }
            }
            return(result);
        }      
    }
}

 
 

Happy Coding!

This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

4 Responses to Finding the name of a Control with the IsMouseOver property

  1. Paul Grooby says:

    Scott – I’m still waiting till we get our hands on this with our upgrade.. I can see the logic of having a whole bunch of library functions like above. The non-programmers/techy’s would argue on the merits of interogating the work but exposing the foundation classes/methodology does make a lot of sense. I can see a situation where you’ve basically re-written a screen and added other data calls/webservices without the need for mashup/SDK. [Assume because its .Net and WPF then you could also draw data from non-m3 databases and potentially use that as validation where you’re customising the screens ?[ The situation I see for us is the ability to drag down a Colour for a tin of paint in some sort of browse box when adding that to the line text of a Customer Order line[ — Great stuff , Cheers Paul

    • potatoit says:

      Hi Paul,

      I get the impression that there is a fair amount of activity in the Lawson NZ space at the moment, hope you don’t have to wait too much longer 🙂

      Having the background on how the GUI objects interact is really needed to exploit the functionality IMO. Being able to retrieve the field names easily is imperative, as it is those field names which we require to manipulate the controls. It would be really nice if there was a shortcut that you could press which would give you the names to all of the controls 🙂

      With the rewritten screen, what I have really is moved the existing controls around the screen and changed the updating logic. I haven’t quite got to the removing the modification altogether, yet… 🙂

      It is also important to note that jscript, webservices, mashups, landscape and the SDK all have their limitations and purposes, some overlapping.

      Certainly with jscript and the .Net framework you can extract data from external sources – databases, spreadsheets, text files, whatever you want and validate existing data against them. All comes down to the performance impact and the complexity of the code (the Journal Importing scripts for example is difficult to manage as pure jscript due to its size).
      The other thing to consider is that you can create and compile your own assemblies within C# or your preferred development language and then call those functions from within your jscript. 🙂

      • Al says:

        Hi Scott,

        A belated response. Field Help (F1) in general provides the field name of a control. I’ve found that sometimes this is not 100% right, but in those cases I’ve been able to look at the list of fields in the mforms://jscript editor and find the field name.

        Cheers, Al.

      • potatoit says:

        Hi Al,

        you’re 100% right, when I initially wrote the script I hadn’t *cough* noticed.

        However, the saving grace for me is that the JScript Editor doesn’t display all of the fields, only those from the the RenderEngine.Content and below IIRC. So fields like the Sorting Order aren’t included nor are the fields that are in the header of a ListView.

        Cheers,
        Scott

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s