I'm attempting to find a document on my window that has a rich text box with text in it using TestStack White, and extract that text.
I've tried to use the UIItem Label & TextBox, but White doesn't seem to be able to find the object during my test. The object can be found using the generic UIItem, but I want to be able to access the text it holds.
I'm implementing it like:
public [Unsure] MyRichTextBox
{
get { return Window.Get<[Unsure]>(SearchCriteria.ByClassName("RichTextBox")); }
}
and I'd like to be able to say:
Assert.That(MyRichTextBox.Text.Equals(x));
But it can't find what I'm looking for if I tag it as a Label or a TextBox, and I don't have access to .Text if I declare it a UIItem.
You want to use the type of TextBox. Then you can use BulkText to access the text in the RichEditBox.
First the Window:
TestStack.White.UIItems.WindowItems.Window _mainWindow;
app = TestStack.White.Application.Launch(startInfo);
_mainWindow = app.GetWindow("MyDialog");
Then Find the richEditBox:
public string _richeditdocument_ID = "rtbDocument";
private TextBox _richeditdocument_ = null;
public TextBox RichEditDocument
{
get
{
if (null == _richeditdocument_)
_richeditdocument_ = _mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId(_richeditdocument_ID));
return _richeditdocument_;
}
}
Then use the following to access the text:
string data = RichEditDocument.BulkText;
Here are the code comments for using the Text Method in White:
// Summary:
// Enters the text in the textbox. The text would be cleared first. This is
// not as good performing as the BulkText method. This does raise all keyboard
// events - that means that your string will consist of letters that match the
// letters of your string but in current input language.
public virtual string Text { get; set; }
Here are the comments for using BulkText:
// Summary:
// Sets the text in the textbox. The text would be cleared first. This is a
// better performing than the Text method. This doesn't raise all keyboard events.
// The string will be set exactly as it is in your code.
Related
I am using below method to validate data within textbox in groupbox
so for improvement of user Feedback for example
message text Please enter First Name
where First Name is the label for textbox, so I used Textbox.Tag to store the name of textbox to achieve it since there is no link between the Textbox and it is Label
I search and found that I can use the Tag property to store anything but I wan to be sure of using it by the I told you about
Is there any problem with that ?
public int ValidateData()
{
foreach (Control cont in GB_PatientInfo.Controls)
{
if (cont is TextBox)
{
if (string.IsNullOrWhiteSpace(cont.Text.Trim()))
{
MessageBox.Show("enter data " + cont.Tag, "Message", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, MessageBoxOptions.RtlReading);
cont.BackColor = Color.Red;
cont.Focus();
return -1;
}
}
}
return 1;
}
Thanks
As you have read, you can store any type that derives from object (i.e. everything) in the Control.Tag property so storing the name of a label is fine.
The use of the Tag property does not influence your application. You can store whatever you want in there with no problem.
As it's already mentioned in other answers, it's OK to use Tag property to store any kind of additional information about the control, including a display name.
But have you ever noticed how ToolTip lets you to set ToolTip for your control at design-time?
A ToolTip, ErrorProvider or HelpProvider are examples of extender provider components. They add some properties to the controls at design-time. You also can create such component for DisplayName by implementing IExtenderProvider.
Example
The following code shows you how easily you can create a component called DisplayNameExtender. When you drop an instance of this component on the form, then a new property will be added to design-time of controls, you can set the value to the property
at design-time: DisplayName on diaplayNameExtender1.
Then at run-time, whenever you want to get the value of DisplayName for a control, it's enough to find it this way:
var displayName = displayNameExtender1.GetDisplayName(control);
Here is the code for DisplayNameExtender component:
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
[ProvideProperty("DisplayName", typeof(Control))]
public class DisplayNameExtender : Component, IExtenderProvider
{
private Hashtable displayNameValues = new Hashtable();
public bool CanExtend(object extendee)
{
return (extendee is Control && !(extendee is Form));
}
public string GetDisplayName(Control control)
{
if (displayNameValues.ContainsKey(control))
return (string)displayNameValues[control];
return null;
}
public void SetDisplayName(Control control, string value)
{
if (string.IsNullOrEmpty(value))
displayNameValues.Remove(control);
else
displayNameValues[control] = value;
}
}
I am trying to get text/labels from application controls with Automation in C#.
So far I am able to obtain AutomationElement tree of application (for example Notepad) with this function:
private void WalkControlElements(AutomationElement rootElement, TreeNode treeNode)
{
AutomationElement elementNode = TreeWalker.ContentViewWalker.GetFirstChild(rootElement);;
while (elementNode != null)
{
TreeNode childTreeNode = treeNode.Nodes.Add(elementNode.Current.ControlType.LocalizedControlType);
// here I want to get text from 'elementNode'
WalkControlElements(elementNode, childTreeNode);
elementNode = TreeWalker.ControlViewWalker.GetNextSibling(elementNode);
}
}
I tried to follow this article http://msdn.microsoft.com/en-us/library/ms788751(v=vs.110).aspx but it only can get text attributes as font name, font weight and so on.
Could anybody point me to the right procedure how to get element text with Automation?
That sample is showing you how to get text attributes, i.e. information about the display of the text in the UI, not the actual displayed text. Getting all the actual displayed text for a general application is more difficult that it might first appear.
It is made difficult by the fact that there are several ways get text and there is inconsistent support by applications and controls. There are two patterns that are of some use, ValuePattern and TextPattern. By convention the Name property contains text displayed to the user however adherence to this is inconsistent. Below is a helper method that I've used in UI automation for testing. It basically goes through those patterns checking the control for support and falls back to the Name.
public static class AutomationExtensions
{
public static string GetText(this AutomationElement element)
{
object patternObj;
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out patternObj))
{
var valuePattern = (ValuePattern)patternObj;
return valuePattern.Current.Value;
}
else if (element.TryGetCurrentPattern(TextPattern.Pattern, out patternObj))
{
var textPattern = (TextPattern)patternObj;
return textPattern.DocumentRange.GetText(-1).TrimEnd('\r'); // often there is an extra '\r' hanging off the end.
}
else
{
return element.Current.Name;
}
}
}
This takes care of getting the text out of simple controls like labels, textboxes (both vanilla textbox and richtextbox), and buttons. Controls like listboxes and comboboxes (esp. in WPF) can be tricker because their items can be virtualized so they may not exist in the automation tree until the user interacts with them. You may want to filter and call this method only on certain UI Automation control types like Edit, Text, and Document which you know contain text.
Mike Zboray answer works fine. In case you have access to pattern-Matching, here is the same (condensed) code :
public static class AutomationExtensions
{
public static string GetText(this AutomationElement element)
=> element.TryGetCurrentPattern(ValuePattern.Pattern, out object patternValue) ? ((ValuePattern)patternValue).Current.Value
: element.TryGetCurrentPattern(TextPattern.Pattern, out object patternText) ? ((TextPattern)patternText).DocumentRange.GetText(-1).TrimEnd('\r') // often there is an extra '\r' hanging off the end.
: element.Current.Name;
}
Here I'm talking about Windows Forms Application written in C#. Consider a simple model
class Labelled
{
private string label;
public string Label
{
get { return label; }
set
{
if (label != value)
{
string message = String.Format(
"Label changed from {0} to {1}",
label, value
);
MessageBox.Show(message);
label = value;
}
}
}
public Labelled(string label)
{
this.label = label;
}
}
class Model
{
public Labelled SingularLabelled { get; set; }
public List<Labelled> ListedLabelled { get; set; }
public Model()
{
SingularLabelled = new Labelled("Singular");
ListedLabelled = new List<Labelled>();
for (int i = 1; i <= 10; ++i)
ListedLabelled.Add(new Labelled("Listed " + i.ToString()));
}
}
We have a simple class Labelled with string property Label and class Model with member SingularLabelled of type Labelled and ListedLabelled which is a list of Labelled's.
Now I want to display the data to the user. Here is my setup:
The main window has a TextBox for displaying SingularLabelled.Label and a DataRepeater from the Visual Basic PowerPacks to display labels of the elements of ListedLabelled. The ItemTemplate of the DataRepeater consists of just a single TextBox.
Let's focus on one way binding, namely I want the underlying data to be updated when the User changes the contents of a text box. The Label property of the Labelled raises a notification in form of a message box, so I can get to know exactly when the data is being updated. Now the arrows represent bindings. Blue arrows stand for data source and the red ones for data members. An instance of Model is created and bound to the modelBindingSource in the constructor of the main window form.
And here we come to a very important thing. I want the data to be updated immediately in sync with what the User is typing, so I made sure that the data source update modes of the data bindings are set to OnPropertyChanged. The generated code that might be of interest here is
this.singularTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.modelBindingSource, "SingularLabelled.Label", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.listedTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.listedLabelledBindingSource, "Label", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
And this is working as expected when typing into the text box of SingularLabelled but the text boxes within DataRepeater trigger the update only when they loose focus. I want them to behave like the singular one. Ideal solution would be to do it using the designer. Does anyone know how to do this?
Above is a sample of the program working. Notice how SingularLabelled's label is updated every character put in and the members of ListedLabelled get the whole edited chunk updated after the corresponding text box looses focus.
We were able to overcome this limitation of DataRepeater by simulating the Tab key.
private void listedTextBox_TextChanged(object sender, EventArgs e)
{
//simulate tab key to force databinding
SendKeys.Send("{TAB}");
SendKeys.Send("+{TAB}");
}
I am in need of some guidance for the following design.
I have a tab control that contains various group boxes. Within the group box, there are specific controls that relates to that group box. For example:
Now whenever a change is made to any control in the group box, the value for the control needs to be tracked because at the end of the application run cycle, the control data will need to be saved to a file that contains that value. An example file is:
HOT_STANDBY_FEATURE_ENABLE [val from control here]
HEART_BEAT_DIGITAL_OUTPUT [val from control here]
....
A design that I have in mind has another that has just properties that the group box form sets whenever a ValueChanged event occurs on a control.
Example code:
class ConfigurationValues
{
public int HotStandbyValue { get; set; }
public int HeartBeatDigitalOutputValue { get; set; }
//...add all other controls here
}
The downside that I see to this is that there are 40 controls on that tab page, so I'd have to manually type each property. When the file needs to be generated at the end of the application run cycle, I have a method that gets the value of the control need.
Example:
private void GenerateFile()
{
string[] file =
"HOT_STANDBY_FEATURE_ENABLE " + ConfigurationTabSettings.HotStandbyValue;
}
Another design consideration I need to make is that whenever a user clicks "Open Configuration File", the values from the file from disk need to be loaded into the properties so the form can take that data on startup and populate the controls within the group boxes with their respective values.
Any suggestions on this design would be greatly appreciated. I know this is not the most efficent way to do this and am not the most experienced programmer, so any Google keywords I can search for would be great also.
You could xml serialise and xml deserialise your ConfigurationValues class rather than writing manual "generate file" and "read file" methods
http://support.microsoft.com/kb/815813
You'll need to bind the controls Text or Value properties to the properties in your ConfigurationValues class e.g.
ConfigurationValues cv = Repository.ReadConfigValues();
numPulseFilterDelay.DataBindings.Add("Value", cv, "PulseFilterDelay");
// Add the rest of your control bindings here
on the btnSave_Click() of your Form, end the current edit on the form and save the config values
void btnSave_Click(object sender, EventArgs e)
{
BindingContext[cv].EndCurrentEdit(); // Commits all values to the underlying data source
Repository.SaveConfigValues(cv);
}
In your repository class you'll need methods to Load() and Save() the data. You can put XmlSerialization code in here, or write your own format (depending on your requirements)
public class Repository
{
public static ConfigurationValues LoadConfigValues()
{
var cv = new ConfigurationValues();
string[] lines = File.ReadAllLines("values.cfg");
foreach (string cfg in lines)
{
string[] nameValue = cfg.Split(new char[] { ' ' } ); // To get label/value
if (nameValue[0] == "HOT_STANDBY_FEATURE_ENABLE")
cv.HotStandbyFeatureEnable = nameValue[1];
else if (nameValue[0] == "SOME_OTHER_PROPERTY")
cv.SomeOtherProperty = nameValue[2];
// Continue for all properties
}
return cv;
}
public static void SaveConfigValues(ConfigurationValues cv)
{
var builder = new StringBuilder();
builder.AppendFormat("HOST_STANDBY_FEATURE_ENABLE {0}\r\n", cv.HostStandbyFeatureEnable);
// Add the rest of your properties
File.WriteAllText("values.cfg", builder.ToString());
}
}
I've got a Windows Form User Control with a string property for setting the text of a textbox. This string can be multi-line.
I've noticed that on some controls with a text property, and instead of being forced to type in the single line property textbox, you get a little pop up where you can type multiple lines. (As a matter of fact, a Windows Forms Textbox control allows this on the Text property.)
How do I enable this functionality in the properties window for the property I have designed?
The following is not real code in my app, but an example of how such a property might be defined
public string Instructions
{
get
{
return TextBox1.Text;
}
set
{
TextBox1.Text = value;
}
}
You can use the EditorAttribute with a MultilineStringEditor:
[EditorAttribute(typeof(MultilineStringEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public string Instructions
{
get
{
return TextBox1.Text;
}
set
{
TextBox1.Text = value;
}
}
To avoid adding a reference to System.Design and thus requiring the Full framework, you can also write the attribute like this:
[EditorAttribute(
"System.ComponentModel.Design.MultilineStringEditor, System.Design",
"System.Drawing.Design.UITypeEditor")]
Although this is less of a problem now that they've stopped splitting the framework into a Client Profile and a Full one.