How can I fix UI Control Map controls from losing their mappings? - c#

I am creating a Coded UI Test on my machine (Windows 8.1) by creating all the mappings and manually writing the tests. (I do not use Action Recordings) When they run at night on my Nightly machine (Server 2012R2) the action to select my Group Lookup window actually selects my Customer Code lookup window during the test run.
Remapping the controls on my machine does not fix this, and it is not possible to remap them on the Server2012R2 machine as it is only meant to run the tests not to run Visual Studio.
The available search properties on the control are not helpful enough to differentiate the controls. Instead I tried modifying the Friendly Name property on the control and used the following code:
public void ClickControl(WinControl mycontrol, string resultId)
{
UITestControlCollection controls = mycontrol.FindMatchingControls();
foreach (UITestControl allControls in controls)
{
if (allControls.FriendlyName == resultId)
{
Mouse.Click(allControls);
}
}
}
But when I run the code it seems to not use the modified value of the Friendly Name when the program is running.
Is there a way to ensure I am selecting the correct control before I send the Mouse.Click event?

I found a work around by creating the following method. It's not as clean as I would like and I have to use the UIMap tool to find the ControlName, but the code is making my tests pass when I call it.
public static UITestControl FindControl(WinControl myControl, string controlName)
{
try
{
UITestControlCollection controls = myControl.FindMatchingControls();
foreach (UITestControl currentControl in controls)
{
if (currentControl.ControlType == ControlType.Button)
{
WinButton mycont = (WinButton)currentControl;
if (mycont.ControlName == controlName)
{
return mycont;
}
}
if (currentControl.ControlType == ControlType.Edit)
{
WinEdit mycont = (WinEdit)currentControl;
if (mycont.ControlName == controlName)
{
return mycont;
}
}
}
return myControl;
}
catch
{
return myControl;
}
}

Related

Method with generic and multiple parameters

I want to enable/disable controls in a Windows Forms application according to the user privileges.
Initially I thought of writing a method in each form class that would check the user credentials and then enable/disable its controls. But then I realized I could (maybe) create a static class method which would take the form as a parameter and do the job.
So I started writing it, presuming that sometimes I would like to enable the controls of just one or two panels, instead of the whole form. So, I need the parameters to be:
a varying number of panels and/or
a form class.
My difficulties with this task is that I'm getting an error trying to make the panels argument varying, and I have no idea how to set a parameter that could take any form class. All my form classes obviously inherits from Form generic class, but I don't know how to apply this.
Here's what I got:
public static void Enable(TableLayoutPanel[] containers = null)
{
if (MyOF.isEnabled)
{
return;
}
else
{
try
{
foreach (TableLayoutPanel table in containers)
{
foreach (Control control in table.Controls)
{
control.Enabled = false;
}
}
}
catch (NullReferenceException)
{
}
}
}
If we remember that the Form class derives from Control (indirectly, by deriving from ContainerControl which derives from ScrollableControl, which derives from Control), and the Enabled property belongs to the Control class, we can write a method that will enable any control's children (including the Form or TableLayoutPanel controls), since the Controls collection also belongs to the Control class:
public static void EnableChildren(Control control, bool enabled = true)
{
foreach (Control child in control.Controls)
{
child.Enabled = enabled;
}
}
And then if we also want to be able to use this with a collection of controls (as in your example), we can write an overload that takes a collection:
public static void EnableChildren(IEnumerable<Control> controls = null,
bool enabled = true)
{
if (controls == null) return;
foreach (var control in controls)
{
EnableChildren(control, enabled);
}
}
Now we can use this with a Form or a collection of TableLayoutPanel controls (or any control that has controls in it's Controls collection).
Examples of usage:
var myForm = new Form1();
EnableChildren(this); // 'this' is the current form
EnableChildren(myForm); // a separate instance of a form control
EnableChildren(tableLayoutPanel1, false); // A single TableLayoutPanel control
var tableLayoutPanels = new [] {tableLayoutPanel1, tableLayoutPanel2, tableLayoutPanel3};
EnableChildren(tableLayoutPanels); // An array of tableLayoutPanel controls
One of the simple ways I can think about what you are trying to do, is this. Let me get away for a sec here. I worked on projects where all form controls were built from Metadata. And meta came with licensing info. So, when control was placed where it should, it also was disabled or set read-only based on Metadata but the whole feature would be hidden if licensing info was restricting access to it. Coming back to your approach, this is not a bad approach and I see that this is can be done. And it can be done in 2 ways, (quickly from my head).
Use user controls as surface for the components you want to enable/disable. Create an interface
public interface IDisableableControl // make your fine name, no methods needed - marker interface
. . . . .
public class MyFineUserControl : UserControl, IDisableableControl
And in your static method that you're going to write pass the form, and find all controls that implement this interface and work them the way you want.
2.
Similarly, you can use property Tag, which is available on each control. With that, you can actually set your complex security object that can come from DB-stored metadata and then you evaluate this object stored in Tag to apply your configuration
Your method needs to be recursive
internal static void SetAllControls(Control parent)
{
// Do something with control, for example parent.Enabled = false
if (parent is IDisableableControl)
{
// here you use your logic, evaluate your parent you're dialing with and
// enable/disable correspondingly
parent.Enabled = false;
return;
}
foreach(var c in parent.Controls)
SetAllControls(c);
}
In real life, your TOP parent will be a form and will not need to be disabled, but it's certain children will. In fact, most of the time, once you found a UserControl which implements IDisableableControl that should be end of line, means, you don't need to go into children controls as they all sit on this parent and all will be disabled
I manage to accomplish what I was trying to do with the code below, which is pretty much a blend of all the helpful answers I got:
public static void EnableContainer(params Control[] containers)
{
if(containers.Count() == 0) { return; }
if (MyOF.isEnabled)
{
return;
}
else
{
try
{
foreach (var container in containers)
{
foreach (Control control in container.Controls)
{
control.Enabled = false;
}
}
}
catch (NullReferenceException)
{
}
}
}
public static void EnableForm<form>(form f) where form : Form
{
if (MyOF.isEnabled)
{
return;
}
else
{
foreach(Control control in f.Controls)
{
control.Enabled = false;
}
}
}
The community is welcome to suggest improvements as I am far from being a professional programmer. Thanks everyone once again.

Reset part of application settings

So, I have Form called Preferences with TabControl in it. This TabControl contains several TabPages(General, Advanced, Misc, ...) with few comboboxes, checkboxes and labels. Each of this control inside TabPage is assigned Application Settings Property Binding (aka they show saved user settings, user can change them etc...).
I know that there is a method to reset all settings (Properties.Settings.Default.Reset();), but is there a way how to reset only settings inside one TabPage?
My solution is to iterate thru controls in TabPage, check if it is combobox, label etc and then reset it´s value to default, but is there a "oneliner" solution to this ?
Use the following solution to get the original value of a single setting:
(The example assumes you want to get the ORIGINAL value of a setting named 'Username')
var defaultUsername = Properties.Settings.Default.GetType()
.GetProperty(nameof(Properties.Settings.Default.Username))
.GetCustomAttribute<System.Configuration.DefaultSettingValueAttribute>()
.Value;
Important - this solution will always return a string value. make sure to parse it properly, or use this extension method I wrote:
public static T GetDefaultValue<T>(this ApplicationSettingsBase settings, string settingKey)
{
return (T)Convert.ChangeType(settings.GetType()
.GetProperty(settingsKey)
.GetCustomAttribute<DefaultSettingValueAttribute>()
.Value, typeof(T));
}
Usage:
var defaultNumber = Properties.Settings.Default.GetDefaultValue<int>(nameof(Properties.Settings.Default.Number));
The ApplicationSettings doesn't have built-in support to reset just some properties. But to solve the problem, you can use either of these options:
Create a method which resets all bound controls of a TabPage
Using Multiple Settings Files with Designer Support
Option 1 - Create a method which resets all bound controls of a TabPage
You can create a method which look at controls of the tab page and check if it's bound to application settings, find the property in settings and reset its value to the default value. Then you can reset settings of a TebPage width one line of code: ResetSettings(tabPage1);.
Here is the method:
private void ResetSettings(TabPage tabPage)
{
foreach (Control c in tabPage.Controls)
{
foreach (Binding b in c.DataBindings)
{
if (b.DataSource is ApplicationSettingsBase)
{
var settings = (ApplicationSettingsBase)b.DataSource;
var key = b.BindingMemberInfo.BindingField;
var property = settings.Properties[key];
var defaultValue = property.DefaultValue;
var type = property.PropertyType;
var value = TypeDescriptor.GetConverter(type).ConvertFrom(defaultValue);
settings[key] = value;
//You can also save settings
settings.Save();
}
}
}
}
Option 2 - Using Multiple Settings Files with Designer Support
If the reason of using a single settings file is because of designer support, you should know you can have designer support also with multiple settings files. Then you can use different settings files and reset each settings group separately. You can simply encapsulate them in a single class using such code:
public static class MySettings
{
public static Sample.General General
{
get { return Sample.General.Default; }
}
public static Sample.Advanced Advanced
{
get { return Sample.Advanced.Default; }
}
public static void ResetAll()
{
General.Reset();
Advanced.Reset();
}
public static void SaveAll()
{
General.Save();
Advanced.Save();
}
}
To reset a setting group it's enough to call MySettings.General.Reset();
To reset all settings, you can call MySettings.ResetAll();
Note for design-time support
To have designer support for binding properties to settings, create multiple settings files in root of your project. Don't put settings files in folders. The setting picker, only shows Settings.settings file which is in Properties folder and those files which are in root of project. This way you can see different settings files and settings properties in a tree-view like this:
TabPage page = aTabControl.SelectedTab;
var controls = page.Controls;
foreach (var control in controls)
{
//do stuff
}
Try this:
private void button2_Click(object sender, EventArgs e)
{
TabPage page = tabControl1.SelectedTab;
var controls = page.Controls;
foreach (var control in controls)
{
if(control is TextBox)
{
//do stuff
}
if(control is ComboBox )
{
ComboBox comboBox = (ComboBox)control;
if (comboBox.Items.Count > 0)
comboBox.SelectedIndex = 0;
comboBox.Text = string.Empty;
}
}
}

Testing label visibility in Coded UI test?

I am writing a coded ui test for my project, and i'd like to test if a label is visible or not.
However, the auto-generated code uses WinText, which does not contain any visibility option.
Is there a way to check visibility of the ui element behind WinText's or get the actual label instead ?
The easiest and most straightforward way is to call WinText's TryGetClickablePoint. If it returns false that means the control is either invisible, offscreen or blocked by an other control.
Point p = new Point();
bool isVisible = UIMap.UIForm1Window.UILabel1Window.UILabel1Text.TryGetClickablePoint(out p);
If you want to fiddle with the "ui element behind WinText's" then you have to get its native element and cast it to the appropriate class (IAccessible if winform) and get the visibility state from there. Be careful tho: you'll probably have to get the label's window's native element and not the label's.
IAccessible acc = (UIMap.UIForm1Window.UILabel1Window.NativeElement as object[])[id] as IAccessible; //id is most probably 0, check with AccExplorer
bool isVisible = (((AccessibleStates)acc.accState & AccessibleStates.Invisible) == 0) && (((AccessibleStates)acc.accState & AccessibleStates.Offscreen) == 0);
I'm using the following extension method for the UITestControl class, and so far it has been pretty bullet proof:
using Microsoft.VisualStudio.TestTools.UITesting;
namespace CodedUIExtensions
{
public static class UITestControlExtensions
{
public static bool IsElementVisible(this UITestControl control)
{
// Assume the control is invisible
bool visible = false;
System.Drawing.Point p;
try
{
// If the control is offscreen, bring it into the viewport
control.EnsureClickable();
// Now check the coordinates of the clickable point
visible = control.TryGetClickablePoint(out p)
&& (p.X > 0 || p.Y > 0);
}
catch (Exception ex)
{
// Boom goes the dynamite! Control is not visible.
// Log to stdout for debugging.
Console.WriteLine(ex);
}
return visible;
}
}
}
My requirements were:
Ensure the control is inside the viewport so a control that is visible, but offscreen doesn't trigger a false negative
Then assert the control is visible
Do not throw exceptions
If an exception occurs, do something with it for debugging purposes
And to use it:
using CodedUIExtensions;
[CodedUITest]
class MyTest
{
[TestMethod]
public void SomeVisibilityTest()
{
UITestControl control = // ...
HtmlDiv div = // ...
Assert.IsTrue(control.IsElementVisible(), "Not visible");
Asset.IsFalse(div.IsElementVisible(), "Visible");
}
}
As long as the Coded UI control class inherits from UITestControl and you have using CodedUIExtensions in your C# file, every control will have an IsElementVisible() method. This should work for web pages as well as GUI applications, such as WinForms.
You can try this:
Control.WaitForControlPropertyNotEqual(UITestControl.PropertyNames.State, ControlStates.Invisible);
This one worked for me (and also the method with .TryGetClickablePoint() ):
while (myControl.State != ControlStates.Offscreen) { }

Copying a TabItem with an MVVM structure

This is an attempt to expand on this question. In my WPF program I've been cloning tabItems by using an XamlWriter in a function called TrycloneElement. I originally found this function here, but the function can also be viewed in the link to my previous question.
Now that I am beginning to worry about functionality inside my program, I found that the TrycloneElement function does not replicate any code-behind functionality assigned to the tabItem that it is cloning.
Because of High Core's link and comment on my earlier question I decided to start implementing functionality on my tabItems through Data Binding with my ViewModel.
Here is a sample of a command that I've implemented:
public viewModel()
{
allowReversing = new Command(allowReversing_Operations);
}
public Command AllowReversing
{
get { return allowReversing; }
}
private Command allowReversing;
private void allowReversing_Operations()
{
//Query for Window1
var mainWindow = Application.Current.Windows
.Cast<Window1>()
.FirstOrDefault(window => window is Window1) as Window1;
if (mainWindow.checkBox1.IsChecked == true) //Checked
{
mainWindow.checkBox9.IsEnabled = true;
mainWindow.groupBox7.IsEnabled = true;
}
else //UnChecked
{
mainWindow.checkBox9.IsEnabled = false;
mainWindow.checkBox9.IsChecked = false;
mainWindow.groupBox7.IsEnabled = false;
}
}
*NOTE: I know that I cheated and interacted directly with my View in the above code, but I wasn't sure how else to run those commands. If it is a problem, or there is another way, please show me how I can run those same commands without interacting with the View like I did.
Now to the question:
After changing my code and adding the commands to my ViewModel, the TrycloneElement function no longer works. At run time during the tab clone I receive an XamlParseException on line, object x = XamlReader.Load(xmlReader); that reads:
I'm fine with ditching the function if there is a better way and I don't need it anymore. But ultimately, how do I take a tabItem's design and functionality and clone it? (Please keep in mind that I really am trying to correct my structure)
Thank you for your help.
Revision of Leo's answer
This is the current version of Leo's answer that I have compiling. (There were some syntax errors)
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues) })
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd
where dpd != null
select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly)
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
Here is my example of a properly-implemented dynamic TabControl in WPF.
The main idea is that each Tab Item is a separate widget that contains its own logic and data, which is handled by the ViewModel, while the UI does what the UI must do: show data, not contain data.
The bottom line is that all data and functionality is managed at the ViewModel / Model levels, and since the TabControl is bound to an ObservableCollection, you simply add another element to that Collection whenever you need to add a new Tab.
This removes the need for "cloning" the UI or do any other weird manipulations with it.
1.) To fix that XamlParseException, make sure you have a public constructor like an empty one, you probably defined a constructor and when you tried to serialize that object and deserialize it can't. You have to explicitly add the default constructor.
2.) I don't like the word clone, but I'd say, when they want to copy. I'll manually create a new tab item control then do reflection on it.
I have this code that I made
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] {new PropertyFilterAttribute(PropertyFilterOptions.SetValues)})
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd where dpd != null select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly))
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
So it would be like
var newTabItem = new TabItem();
newTabItem.CopyPropertiesFrom(masterTab);

Alternative to OverLibWrapper for displaying control tooltips in ASP.NET

I looked into a nice way to display tooltips dynamically and I found OverLibWrapper, which was exactly what I needed.
I have all the tooltip data stored in a custom configuration section, the tooltips are bound to their respective controls during Page_Load.
I did a quick test and worked fine. The problem came up when I realized that OverLibWrapper didn't work on masterpages. Our website has uses quite a few masterpages, so taking them out isn't an option.
I was wondering if there's anything like OverLibWrapper that I could use.
EDIT:
What I'm looking for is a control to display good-looking tooltips on mouseover preferably instantly like overlib (nothing fancy because I'm just displaying raw text) in a dynamic way, because the tooltip property in ASP.NET is not very pretty and takes a while to appear. For example let's say I have a collection of Messages:
class Message
{
string ctrlid, msgtodisplay;
}
And when the page is loaded:
TooltipManager manager;
foreach(var m in messages)
{
Tooltip tltp=new Tooltip;
m.ControlID=m.ctrlid;
m.Message=m.msgtodisplay;
manager.AddTooltip(tltp);
}
So basically something that offers the functionality of Tooltip and TooltipManager.
Take a look at this:
NotesTooltip
I think this will do what you need.
Have you thought about just writing your own? Sometimes I find the things out there by other people are never quite fit for my needs.
Well I finally solved my problem:
I've used this function to find any control (works with masterpages):
public static Control FindControlRecursive(Control root, string id)
{
if (id == string.Empty)
return null;
if (root.ID == id)
return root;
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
return t;
}
return null;
}
And this method:
public static void load(Page page, string pageFileName)
{
foreach (ConfiguracionElem elem in Configuracion.GetConfig(pageFileName).Tooltips)
{
WebControl ctrl = (WebControl)FindControlRecursive(page, elem.controlid);
if (ctrl == null)
throw new ControlNotFoundException("There's no control'"+elem.controlid+"'")
else
{
ctrl.Attributes.Add("onmouseover","return overlib('"+elem.message+"');");
ctrl.Attributes.Add("onmouseout","return nd();");
}
}
}
I added the Overlib library manually to a script folder, then I iterated through my Custom Configuration Section (where my tooltip data is stored) to add the javascript attributes dynamically.

Categories