I have a Control which is first children of the Parent but nothing is unique in Parent and in the Control. I can find unique properties for Scond children so I need to find the previous Sibling of Second children
This is possible by using the SearchConfiguration property on the control.
What you do is you search for the label and next you instantiate a WinEdit control and pass it in the label search control. Next you set on the WinEdit instance the SearchConfiguration.Add(SearchConfiguration.NextSibling)
this will now search for the next sibling of the text label.
in a code example this looks like follows:
var app = ApplicationUnderTest.Launch(#"yourapplication.exe");
var mainWindow = new WinWindow(app);
mainWindow.WindowTitles.Add("Form1");
WinText textLabel = new WinText(mainWindow);
textLabel.SearchProperties.Add(WinControl.PropertyNames.Name, "Some Text Label");
WinEdit siblingEditControl = new WinEdit(textLabel);
siblingEditControl.SearchConfigurations.Add(SearchConfiguration.NextSibling);
siblingEditControl.Text = "setting the text";
There are no direct methods for getting siblings. One way of finding the siblings of a control is to find its parent and then find all the children of that parent. Next search through the children to find the current control and then take the previous one. The method might be based on the following. It uses the Name fields of the control for the comparison, this may be incorrect for the general case and I suggest other values be tried.
public UITestControl GetPreviousSibling(UITestControl uitc)
{
UITestControlCollection siblings = uitc.GetParent().GetChildren();
// Note that 'sibings[0]' has no predecessor
for (int ii=1; ii<siblings.Count)
{
if (uitc.Name == siblings[ii].Name)
{
return siblings[ii - 1];
}
}
return null;
}
The definition of "sibling" is not clear. This MSDN blog gives some details of siblings.
Related
I am using coded ui test with my windows store app.
My control hierarchy is :
UIPearsonPOCCommonViewFlipViewItem (XAMLFlipViewItem - > UIWebViewPane (XAMLWebViewPane) - > Rest of the content.
For rest of the child controls, there is no specific automation id or unique names and they look like html control for e.g. refer the image appended.
I want to iterate over the children of UIWebViewPane and reach to child DIV which is having the innerText.
I am relatively very new to coded ui test. I am unable to iterate over the children of UIWebViewPane(XAMLWebViewPane)
If the child control's inner text is unique, you could always search on that using the parent control in the definition. Ex:
public HtmlControl child()
{
HtmlControl parent = new HtmlControl(browser);
parent.SearchProperties["id"] = "[my id]";
HtmlControl child = new HtmlControl(parent);
child.SearchProperties["innerText"] = "[the inner text]";
return child;
}
If you truly want to iterate through, then you'll have to crawl the structure using the .GetParent() and .GetChildren() methods of the UITestControl class.
public HtmlControl child()
{
//First, we create an empty HtmlControl to return.
HtmlControl result = new HtmlControl()
//Specify the parent and get a collection of the children (this only goes one level,
// so if you have to go deeper, you'll have to nest your foreach loops and get
// children of the children, etc.
HtmlControl parent = new HtmlControl(browser);
parent.SearchProperties["id"] = "[my id]";
UITestControlCollection children = parent.GetChildren();
foreach (UITestControl child in children)
{
// If the child has the text you're looking for, then assign it to the result
// object and break the loop.
if (child.GetProperty("InnerText").ToString().Equals(searchTerm))
{
result = (HtmlControl)child;
break;
}
}
return result;
}
Personally, I'd try the first option. Your best bet, though, is to ask (politely) for the developer to add some unique and static tags to the HTML.
What we have
We have some complex winforms control. To store its state we use some custom serialized class. Lets say we've serialized it to xml. Now we could save this xml as a file in User directory or to include it in some another file....
But...
The question is,
if user creates several such controls across his winform application (at design time), what unique identifier is better to use in order to know which of the saved configs belongs to which of these controls?
So this identifier should:
Stay the same across application launches
Automatic given (or already given, like we can assume that Control.Name is always there)
Unique across application
I think one could imagine several ways of doing it and I believe there are might be some default ways of doing it.
What is better to use? Why?
This small extension method does the work:
public static class FormGetUniqueNameExtention
{
public static string GetFullName(this Control control)
{
if(control.Parent == null) return control.Name;
return control.Parent.GetFullName() + "." + control.Name;
}
}
It returns something like 'Form1._flowLayoutPanel.label1'
Usage:
Control aaa;
Dictionary<string, ControlConfigs> configs;
...
configs[aaa.GetFullName()] = uniqueAaaConfig;
I've been using a compound indentifier made of a full tree of control hierarchy. Assuming that your form name is Form1, then you have a groupbox Groupbox1 and a textbox TextBox1, the compound identifier would be Form1/Groupbox1/TextBox1.
If you'd like to follow this, here are the details:
http://netpl.blogspot.com/2007/07/context-help-made-easy-revisited.html
This is the method I've ended up creating to define a unique name that includes the full name of the form (with it's namespace) then each parent control above the control in question. So it could end up being something like:
MyCompany.Inventory.SomeForm1.SomeUserControl1.SomeGroupBox1.someTextBox1
static string GetUniqueName(Control c)
{
StringBuilder UniqueName = new StringBuilder();
UniqueName.Append(c.Name);
Form OwnerForm = c.FindForm();
//Start with the controls immediate parent;
Control Parent = c.Parent;
while (Parent != null)
{
if (Parent != OwnerForm)
{
//Insert the parent control name to the beginning of the unique name
UniqueName.Insert(0, Parent.Name + ".");
}
else
{
//Insert the form name along with it's namespace to the beginning of the unique name
UniqueName.Insert(0, OwnerForm.GetType() + ".");
}
//Advance to the next parent level.
Parent = Parent.Parent;
}
return UniqueName.ToString();
}
in my Win Forms app I create an array of dynamic custom controls inside a loop. These, lets call them 'boxes', are like my basic pieces of information. I also create string arrays in other parts of the code that contain the information of this 'boxes', so that for example string[3] is a variable of box[3] and so does stringa[3], stringb[3], stringc[3]... all the arrays with the same index are related to the box with that index. Hope I make myself clear.
Only 2 of this strings are shown in 2 labels inside each custom control 'box' in the array, but the others are there because I want to make something so that when the user clicks one of these controls the other strings can be shown in another control. Sort of something like "More Information...". All the 'boxes' in the array need to have the same event handler because I create +100.
To put it more into context, each custom control 'box' in the array shows the Symbol and the Price of a stock and I want that when the user clicks on each stock more quote information is shown on another special control which is like a placeholder for "More info".
I am thinking of 2 ways to do it:
If I could "detect" the index of the clicked control (which is the same in the strings related to it), I could just set this to an int j and all I have to do is show all the strings a,b,c... with index j. Unfortunately I cannot find a way to do this, maybe it is not even possible.
The other way I have thought is to create some properties for my custom control which "store" this variables, and in my app instead of assigning strings I would set properties for each control, which I could later retrieve when the control is clicked. I haven't tryed this because I don't know exactly how to do it.
What do you think? Do you know how can I achieve this or do you have a different idea that will work? Please help! Thanks in advance.
It's kind of a broad implementation question since there are countless ways you could implement something like this.
If you are creating two collections, one with the buttons and one with the information, you potentially could just assign each of the buttons 'Tag' properties to point to the corresponding info and assign a generic OnClick event handler that displays the info.. something like:
infoControl.text = ((InfoClass)((Button)Sender.Tag)).pieceOfInformation;
But again there are many ways to do this, and the choice comes down to how you store your information.
For your first method, you could have a property of your custom control that is the index.
public class Box : Control
{
// ...existing code
private int index;
public int Index
{
get
{
return index;
}
set
{
index = value;
}
}
}
OR
For your second method, you could have a property of your custom control that is the additional info string.
public class Box : Control
{
// ...existing code
private string extraInfo;
public string ExtraInfo
{
get
{
return extraInfo;
}
set
{
extraInfo = value;
}
}
}
In either case, you could then access the proper information right in your click handler for the "box".
i don't know about the first way - got to noodle around more, but in the second way you can extended your custom or built-in control: for example:
public class ExtendedLabel: Label
{
public string[] MoreInfo { get; set; }
}
and initialize it
public TestForm()
{
InitializeComponent();
ExtendedLabel label = new ExtendedLabel();
label.MoreInfo = new string[] { "test" };
this.Controls.Add(label);
label.AutoSize = true;
label.Location = new System.Drawing.Point(120, 87);
label.Name = "label1";
label.Size = new System.Drawing.Size(35, 13);
label.TabIndex = 0;
label.Text = label.MoreInfo[0];
}
And later in your event handler you can use the inside information
Example code:
var div = new HtmlGenericControl("div");
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
Assert.IsNotNull(lit);
This code fails the assert, because lit is null. Debugging shows that div.Controls definitely contains a literal with ID of "litSomeLit." My questions are "Why?" and "Is there any way to get a control of a specific ID without doing a recursive search of div.Controls[] by hand one element at a time?"
The reason I'm doing things this way is that my actual application is not so straightforward- a method I'm writing is given a complex control with several subcontrols in a number of possible configurations. I need to access a specific control several layers down (eg, the control with ID "txtSpecificControl" might be at StartingControl.Controls[0].Controls[2].Controls[1].Controls[3]). Normally I could just do FindControl("txtSpecificControl"), but that does not seem to work when the controls were just dynamically created (as in the above example code).
Near as I can tell, there is no way to do what I'm trying to accomplish without adding the control to the page. If I had to guess, I'd say that FindControl uses the UniqueID property of the control, which generally contains the IDs of all the controls above the current one (eg OuterControlID$LowerControlId$TargetControlID). That would only get generated when the control actually gets added to the page.
Anyway, here's an implementation of recursive depth-first-search FindControl that'll work when the control is not attached to the page yet:
public static Control FindControl(Control parent, string id)
{
foreach (Control control in parent.Controls)
{
if (control.ID == id)
{
return control;
}
var childResult = FindControl(control, id);
if (childResult != null)
{
return childResult;
}
}
return null;
}
Change your code to
var div = new HtmlGenericControl("div");
Page.Controls.Add(div);
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
As far as i know FindControl only works when the control is in the visual tree of the page.
When you confirmed that the control was in the Controls collection, did you do that by inspecting the collection directly? FindControl() may not work in this context.
When you debug the test, is the var lit null? If so, you may have to access the member by item index instead of using the FindControl() method.
I'm trying to fix this ugly code.
RadGrid gv = (RadGrid) (((Control) e.CommandSource).Parent.Parent.Parent.Parent.Parent);
I often need to find the first grid that is the parent of the parent of... etc of a object that just raised an event.
The above tends to break when the layout changes and the number of .Parents increase or decreases.
I don't necessarily have a control Id, so I can't use FindControl().
Is there a better way to find the 1st parent grid?
Control parent = Parent;
while (!(parent is RadGrid))
{
parent = parent.Parent;
}
If you really have to find the grid, then you might something like this:
Control ct = (Control)e.CommandSource;
while (!(ct is RadGrid)) ct = ct.Parent;
RadGrid gv = (RadGrid)ct;
But maybe you can explain why you need a reference to the grid? Maybe there is another/better solution for your problem.
I'm not familiar with the API that you are using, but can you do something like:
Control root = ((Control)e.CommandSource);
while(root.Parent != null)
{
// must start with the parent
root = root.Parent;
if (root is RadGrid)
{
// stop at the first grid parent
break;
}
}
// might throw exception if there was no parent that was a RadGrid
RadGrid gv = (RadGrid)root;
If you have control of the Parent's code, you could use simple recursion to do it, I'd think. Something like:
public Control GetAncestor(Control c)
{
Control parent;
if (parent = c.Parent) != null)
return GetAncestor(parent);
else
return c;
}
I make no claims as to how well that will work, but it should get the idea across. Navigate up the parent chain as far as it goes until there is no parent, then return that object back up the recursion chain. It's brute force, but it will find the first parent no matter how high it is.