Is there a way to programatically browse all controls on a screen?
-
Sunday, September 11, 2011 1:03 PM
Since Lightswitch is based on MVVM, screen control events are "less" important. Commands, on the other hand, are first-class citizens.
Nonetheless, once in a while there is a need for attaching an event to a specific control. This can be done via the this.FindControl ("MyControl") method. This is very powerful, but it's usage escapes compile time checking, because of the "magic strings" (e.g. "MyControl"). What can go wrong? Well, renaming controls without updating the MyControl method param!
I'm currently looking for a way to foresee "something" that can improve this issue. If finished I will be very happy to share it with the LightSwitch community. But first I have to overcome the following: How can I programmatically "browse" all conttrols on a specific screen? Is there, anyhow, an API for this?
paul van bladel
All Replies
-
Wednesday, September 21, 2011 5:55 PM
I'll answer myself: programmaticaly browsing all controls on a screen is NOT possible. (NOT TRUE !!!!)
Thanks to Diego we know better now :)
paul.
paul van bladel- Marked As Answer by Paul Van Bladel Wednesday, September 21, 2011 5:55 PM
- Unmarked As Answer by Paul Van Bladel Saturday, October 01, 2011 6:39 AM
- Edited by Paul Van Bladel Saturday, October 01, 2011 6:42 AM
-
Wednesday, September 21, 2011 8:05 PMModerator
What makes you say that it's not possible Paul?
If you mean looping through the controls on a screen programatically & examining/acting on them, it *is* possible.
You just need to start from the root control, which if I remember correct is RootVisual. It's RootSomething, if it's not RootVisual, I can't find the post right now where the name was figured out, but I remember it.
(plus ça change, plus c'est la même chose!)
If you found this post helpful, please "Vote as Helpful". If it actually answered your question, remember to "Mark as Answer".
This will help people find the answers that they're looking for more quickly.
-
Wednesday, September 21, 2011 11:23 PM
You are right that LightSwitch uses MVVM.
What Lightswitch allows is the ability to access everything on the screen. The properties and collections. This is what you want to manipulate. See:
A Random Walk Through The LightSwitch Data Model
-
Monday, September 26, 2011 7:42 PM
You are right that LightSwitch uses MVVM.
What Lightswitch allows is the ability to access everything on the screen. The properties and collections. This is what you want to manipulate. See:
A Random Walk Through The LightSwitch Data Model
Michael,
I guess you are referring to:
var x = this.Details.Screen.Details.Properties.All();
from your article.That's giving me the properties of the viewModel, but not of the screen (or view) itself.I need to be able to browse the controls on a screen, not the collections on the viewModel.
paul van bladel -
Monday, September 26, 2011 8:31 PM
You should be able to get to the actual screen using:
this.Details.Screen
-
Monday, September 26, 2011 8:46 PM
Sure, but how can I programmatically "browse" (iterate over) all conttrols on a specific screen?You should be able to get to the actual screen using:
this.Details.Screen
paul van bladel -
Monday, September 26, 2011 9:08 PM
You should be able to cast it to:
< strike >Microsoft.LightSwitch.Client.IVisualCollection< /strike > <- (No I am wrong)
(see: http://tejana.com/Home/Default.aspx?tabid=71)
However, I do not reccomend this. You have access to the Property the controls are bound to, you want to modify the property not the control. You modify the property and the control will automatically be updated. This is the principal of the MVVM design.
Make Them Ask: That's a LightSwitch App?
http://LightSwitchHelpWebsite.com
- Edited by ADefwebserverMVP Monday, September 26, 2011 10:13 PM
-
Tuesday, September 27, 2011 4:25 AM
You should be able to cast it to:
< strike >Microsoft.LightSwitch.Client.IVisualCollection< /strike > <- (No I am wrong)
(see: http://tejana.com/Home/Default.aspx?tabid=71)
However, I do not reccomend this. You have access to the Property the controls are bound to, you want to modify the property not the control. You modify the property and the control will automatically be updated. This is the principal of the MVVM design.
So, it seems not to be possilbe.
paul van bladel -
Friday, September 30, 2011 11:12 PM
Hello Paul, hello adef, hello Yann I'm back, good to see you again like beta days!
My answer is yes! You can programmatically browsing all controls on a screen.
The idea is to get a proxy for a control in the screen (I test with a TextBox or DataGrid) and get the DataContext, a Microsoft.LightSwitch.Presentation.IContentItem, and go back in the hierarchy of IContentItem to the screen root. Once I get the root item I can visit every control in the screen:
partial void MyScreen_Created() { var proxy = this.FindControl("KnowControl"); proxy.ControlAvailable += (s, e) => { var ctrl = e.Control as System.Windows.Controls.Control; var contentItem = ctrl.DataContext as Microsoft.LightSwitch.Presentation.IContentItem; //Find the Screen Root var parent = contentItem.EffectiveParentItem; while (parent.EffectiveParentItem != null) parent = parent.EffectiveParentItem; //Visit the control hierarchy VisitControl(parent); }; } public void VisitControl(Microsoft.LightSwitch.Presentation.IContentItem item) { var c = this.FindControl(item.Name); if (c != null) System.Diagnostics.Debug.WriteLine("Control: " + item.Name + ", DisplayName:" + c.DisplayName); else System.Diagnostics.Debug.WriteLine("Control not found: " + item.Name); foreach (var i in item.ChildItems) VisitControl(i); }
Hope this help! bye- Proposed As Answer by ADefwebserverMVP Friday, September 30, 2011 11:34 PM
- Marked As Answer by Paul Van Bladel Saturday, October 01, 2011 6:14 AM
-
Saturday, October 01, 2011 6:15 AM
Wow Diego... brillant !
thx
paul.
paul van bladel -
Saturday, October 01, 2011 7:24 AMModerator
Welcome back Diego!!!
This is a good method, but it's more complicated than it needs to be. I just wish I could find the post where I worked this out with Garth (back in the B2 forum).
Doesn't
var proxy = this.FindControl("RootLayout");
work?
(I said "RootVisual" before, but I think I just remembered it's "RootLayout")
(plus ça change, plus c'est la même chose!)
If you found this post helpful, please "Vote as Helpful". If it actually answered your question, remember to "Mark as Answer".
This will help people find the answers that they're looking for more quickly.
-
Sunday, October 02, 2011 12:25 PM
Hello Paul, hello adef, hello Yann I'm back, good to see you again like beta days!
My answer is yes! You can programmatically browsing all controls on a screen.
The idea is to get a proxy for a control in the screen (I test with a TextBox or DataGrid) and get the DataContext, a Microsoft.LightSwitch.Presentation.IContentItem, and go back in the hierarchy of IContentItem to the screen root. Once I get the root item I can visit every control in the screen:
partial void MyScreen_Created() { var proxy = this.FindControl("KnowControl"); proxy.ControlAvailable += (s, e) => { var ctrl = e.Control as System.Windows.Controls.Control; var contentItem = ctrl.DataContext as Microsoft.LightSwitch.Presentation.IContentItem; //Find the Screen Root var parent = contentItem.EffectiveParentItem; while (parent.EffectiveParentItem != null) parent = parent.EffectiveParentItem; //Visit the control hierarchy VisitControl(parent); }; } public void VisitControl(Microsoft.LightSwitch.Presentation.IContentItem item) { var c = this.FindControl(item.Name); if (c != null) System.Diagnostics.Debug.WriteLine("Control: " + item.Name + ", DisplayName:" + c.DisplayName); else System.Diagnostics.Debug.WriteLine("Control not found: " + item.Name); foreach (var i in item.ChildItems) VisitControl(i); }
Hope this help! byeHi Diego,
Is it correct that the code could be simplified by "assuming" that every screen starts with "RootContentItem" ?
It works in a few screen I tested, but do you think it's a reasonable assumption?
If yes, your code becomes:
public void BrowseControlsOnScreen(IScreenObject myScreen) { StringBuilder stringBuilder = new StringBuilder(); IContentItemProxy proxy = myScreen.FindControl("RootContentItem"); proxy.ControlAvailable += (s, e) => { var ctrl = e.Control as System.Windows.Controls.Control; IContentItem contentItem = ctrl.DataContext as Microsoft.LightSwitch.Presentation.IContentItem; VisitControl(myScreen, contentItem, stringBuilder); string result = stringBuilder.ToString(); this.ShowMessageBox(result); }; } public void VisitControl(IScreenObject screen, Microsoft.LightSwitch.Presentation.IContentItem item, StringBuilder stringBuilder) { var c = screen.FindControl(item.Name); if (c != null) { System.Diagnostics.Debug.WriteLine("Control: " + item.Name + ", DisplayName:" + c.DisplayName); stringBuilder.AppendLine("Control: " + item.Name + ", DisplayName:" + c.DisplayName); } else System.Diagnostics.Debug.WriteLine("Control not found: " + item.Name); foreach (var i in item.ChildItems) VisitControl(screen, i, stringBuilder); }
I have another question. Is there a way to "browse" all screens in your app. Not only the active screen (because that can be easily done by Application.ActiveScreens....) ? Any idea?
Thanks
paul
paul van bladel -
Sunday, October 02, 2011 5:00 PMHello Paul, about browsing all screens in the app, my first thought was to use reflection, get all the classes within the assembly and all the classes that implement the IScreenObject interface are Screens!
About your code fix it looks good, before posting my code, I tried getting a reference to the "ScreenLayout" control, but that control doesn't seem to trigger the ControlAvariable event, if "RootContentItem" triggers the ControlAvariable everything will be ok, Yann suggested something like this :)
Paul, you give me another idea about getting all the screens, and all the controls withing the screens, all the lightswitch magic lives in the ApplicationDefinition.lsml, if we are able to parse that xml file we could get all the app information, tomorrow I'll make some test and I'll tell you the the results, actually my phone doesn't run visual studio :p
Bye- Edited by Diego Mesa Tabares Sunday, October 02, 2011 5:05 PM
-
Monday, October 03, 2011 4:05 PM
Hello, parsing the ApplicationDefinition.lsml file you can get the screens and controls information and more! Here is another version of getting all the screens and visiting all the controls in the screen:
public static void ListAllScreensAndControls() { XDocument doc = XDocument.Load("ApplicationDefinition.lsml"); //Get all the screens var screens = doc.Root.Elements("{http://schemas.microsoft.com/LightSwitch/2010/xaml/model}Screen"); foreach (var s in screens) { System.Diagnostics.Debug.WriteLine("Screen: " + s.Attribute("Name").Value); //Get al the controls in the screen var items = s.Descendants("{http://schemas.microsoft.com/LightSwitch/2010/xaml/model}ContentItem"); foreach(var i in items) System.Diagnostics.Debug.WriteLine("\tName: " + i.Attribute("Name").Value + ", " + i.Attribute("Kind").Value); } } public static void VisitControls(IScreenObject screen) { XDocument doc = XDocument.Load("ApplicationDefinition.lsml"); //Get the screen var screenNode = doc.Root.Elements("{http://schemas.microsoft.com/LightSwitch/2010/xaml/model}Screen").Where(s => s.Attribute("Name").Value == screen.GetType().Name).FirstOrDefault(); if (screenNode == null) throw new Exception("Screen not found"); //Get the controls in the screen foreach (var i in screenNode.Descendants("{http://schemas.microsoft.com/LightSwitch/2010/xaml/model}ContentItem")) { var itemName = i.Attribute("Name").Value; var kindName = i.Attribute("Kind").Value; try { var c = screen.FindControl(itemName); System.Diagnostics.Debug.WriteLine("Control: " + itemName + ", DisplayName:" + c.DisplayName); } catch(Exception ex) { //Summary controls could have subcontrols System.Diagnostics.Debug.WriteLine("Control not found: " + itemName + ", Kind: " + kindName); } } }Hope this help, bye!
-
Monday, October 03, 2011 6:42 PM
Very inventive Diego !
paul van bladel- Edited by Paul Van Bladel Monday, October 03, 2011 6:43 PM
-
Tuesday, October 04, 2011 7:36 PM
If something is not available through runtime objects and you really need to introspect model then the right way is to use model API instead of parsing lsml file.
The API is available under Microsoft.LightSwitch.Model namespace e.g. you may write something like this to get all screen definitions.
public partial class Application
{
partial void Application_Initialize()
{
IApplicationDefinition appDef = this.Details.GetModel();
foreach (var screen in appDef.GlobalItems.OfType<IScreenDefinition>())
{
// write your code here for each screen definition
}
}
}
- Proposed As Answer by ADefwebserverMVP Tuesday, October 04, 2011 8:13 PM
- Marked As Answer by Paul Van Bladel Tuesday, October 04, 2011 9:22 PM
-
Tuesday, October 04, 2011 7:46 PM
If something is not available through runtime objects and you really need to introspect model then the right way is to use model API instead of parsing lsml file.
The API is available under Microsoft.LightSwitch.Model namespace e.g. you may write something like this to get all screen definitions.
public partial class Application
{
partial void Application_Initialize()
{
IApplicationDefinition appDef = this.Details.GetModel();
foreach (var screen in appDef.GlobalItems.OfType<IScreenDefinition>())
{
// write your code here for each screen definition
}
}
}
I'm impressed!
Lightswitch = simple/transparant on the outside, but rich on the inside. That's clear.
thx
paul.
paul van bladel -
Tuesday, October 04, 2011 8:14 PM
If something is not available through runtime objects and you really need to introspect model then the right way is to use model API instead of parsing lsml file.
The API is available under Microsoft.LightSwitch.Model namespace e.g. you may write something like this to get all screen definitions.
public partial class Application
{
partial void Application_Initialize()
{
IApplicationDefinition appDef = this.Details.GetModel();
foreach (var screen in appDef.GlobalItems.OfType<IScreenDefinition>())
{
// write your code here for each screen definition
}
}
}
Thank you for posting this.It opens up a lot of things.
-
Tuesday, October 04, 2011 8:48 PMThank you Bilal, lightswitch a new world to explore!
-
Tuesday, October 04, 2011 9:20 PM
Allow me to refer back to my initial reason why I was in search for a robust way to browse all controls on all screens:
"...Nonetheless, once in a while there is a need for attaching an event to a specific control. This can be done via the this.FindControl ("MyControl") method. This is very powerful, but it's usage escapes compile time checking, because of the "magic strings" (e.g. "MyControl"). What can go wrong? Well, renaming controls without updating the MyControl method param!
I'm currently looking for a way to foresee "something" that can improve this issue. If finished I will be very happy to share it with the LightSwitch community. But first I have to overcome the following: How can I programmatically "browse" all conttrols on a specific screen? Is there, anyhow, an API for thiis?
-------
With the help of Diego and Bilal, this is now a piece of cake. It should be possible to include a resource file in the app, and refer in the this.FindControl method to a particular entry in the resource file. Next, in the app initialize I can iterate over the resource file and check if the controls are really present in the model. I could surround this "check" with an #IFDEBUG, so there is no impact on production code. I will never be faced with a non-working app because of accidentally renamed controls.
But, more important, I would suspect (under the assumption we can access and set the display value prop) that the approach of Bilal, opens the door for MULTI LANGUAGE APPS !!!!
paul van bladel

