How to capture UIAutomation event - c#

I need to monitor changes in the google chrome url textbox. I have the code the finds the url and correctly reads it.
AutomationElement elementx = element.FindFirst(System.Windows.Automation.TreeScope.Descendants, conditions);
return ((ValuePattern)elementx.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
elementx is the url textbar and the url is returned
Now
I need to monitor the url textbar for text changes. I found some code on stackoverflow but it doesnt work . Handling ProgressBar's Value change with UIAutomation
I have used an app called accevent.exe and see that it is indeed possible to monitor for text changes as is evident per the screenshot.
So basically I need a way to monitor and report changes to the url text.
and here are some settings

ValuePattern chromeValuePattern;
AutomationPropertyChangedEventHandler propChangeHandler = null;
chromeValuePattern = elementx.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
propChangeHandler += new AutomationPropertyChangedEventHandler(OnPropertyChange);
Automation.AddAutomationPropertyChangedEventHandler(elementx,
System.Windows.Automation.TreeScope.Subtree, propChangeHandler,
AutomationProperty.LookupById(UIA_PropertyIds.UIA_ValueValuePropertyId));
private void OnPropertyChange(object src, AutomationPropertyChangedEventArgs e)
{
AutomationElement sourceElement = src as AutomationElement;
if (e.Property == AutomationProperty.LookupById(UIA_PropertyIds.UIA_ValueValuePropertyId))
{
Debug.WriteLine(e.NewValue);
}
else
{
}
}

Related

Webbrowser C# How to put a changing value in a textbox?

I cant seem to figure out how to put the value text into a texbox.Text on Webbrowser C# because with this website the value changes and there are duplicates of the input code and cant pinpoint Webbrowser to put the changing value username in a textbox.Text as seen in screenshot below.
I hope someone knows how to do this with the Webbrowser in C#
Thanks in advance.
Problem Screenshot
This is the website I am trying to get the username from: fakepersongenerator
You'd need to manually scan through the DOM and find the element based on it's xpath. Like this:
webBrowser.DocumentCompleted += (o, e) =>
{
var frame1 = webBrowser.Document.Body.GetElementsByClassName("frame-1");
if (frame1.Count > 0)
{
var rows = frame1[0].GetElementsByClassName("row no-margin");
if (rows.Count > 4)
{
var usernameForm = rows[4].GetElementsByClassName("form-control");
if (rows.Count > 0)
{
// Do something with value here
Console.WriteLine(usernameForm[0].GetAttribute("value"));
}
}
}
};
webBrowser.Navigate("http://www.fakepersongenerator.com/Index/generate");
Extension class to :
internal static class Utils
{
internal static List<HtmlElement> GetElementsByClassName(this HtmlElement doc, string className = "")
{
var list = new List<HtmlElement>();
foreach (HtmlElement e in doc.All)
if (e.GetAttribute("className") == className)
list.Add(e);
return list;
}
}
There isn't really a way in the default web browser to detect changes after a page load, so if you wanted to monitor for changes, you'd have to set up a BackgroundWorker or Timer to poll the browser and manually look for a value change.

Is it possible to activate a tab in another program using an IntPtr?

Thanks in advance.
Is it possible to activate a tab in another program using an IntPtr? If so, how?
SendKeys is not an option.
Perhaps what I need is a fishing lesson. I have exhausted Google and my lead developer.
I would appreciate an outright solution OR a recommendation to continue my Google efforts.
basic process is:
I drag a shortcut icon to the launcher
This opens the target application (Notepad++) and grabs IntPtr, etc.
I would like to programmatically select various items in Notepad++ such as Edit, menu items under Edit, or a doc tab.
The basic code I am running is:
the 'blob'
item 1: IntPtr of item
item 2: IntPtr of itemsChild
item 3: control text of item 1
item 4: is rectangle parameters of item 1
root contains similar info:
As others pointed out, the standard way of doing this is to use UI Automation. Notepad++ does support UI Automation (to some extent, as it's somehow automatically provided by the UI Automation Windows layers).
Here is a sample C# console app that demonstrates the following sceanrio (you need to reference UIAutomationClient.dll, UIAutomationProvider.dll and UIAutomationTypes.dll):
1) get the first running notepad++ process (you must start at least one)
2) open two files (note there may be already other opened tabs in notepad++)
3) selects all tabs in an infinite loop
class Program
{
static void Main(string[] args)
{
// this presumes notepad++ has been started somehow
Process process = Process.GetProcessesByName("notepad++").FirstOrDefault();
if (process == null)
{
Console.WriteLine("Cannot find any notepad++ process.");
return;
}
AutomateNpp(process.MainWindowHandle);
}
static void AutomateNpp(IntPtr handle)
{
// get main window handle
AutomationElement window = AutomationElement.FromHandle(handle);
// display the title
Console.WriteLine("Title: " + window.Current.Name);
// open two arbitrary files (change this!)
OpenFile(window, #"d:\my path\file1.txt");
OpenFile(window, #"d:\my path\file2.txt");
// selects all tabs in sequence for demo purposes
// note the user can interact with n++ (for example close tabs) while all this is working
while (true)
{
var tabs = GetTabsNames(window);
if (tabs.Count == 0)
{
Console.WriteLine("notepad++ process seems to have gone.");
return;
}
for (int i = 0; i < tabs.Count; i++)
{
Console.WriteLine("Selecting tab:" + tabs[i]);
SelectTab(window, tabs[i]);
Thread.Sleep(1000);
}
}
}
static IList<string> GetTabsNames(AutomationElement window)
{
List<string> list = new List<string>();
// get tab bar
var tab = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab));
if (tab != null)
{
foreach (var item in tab.FindAll(TreeScope.Children, PropertyCondition.TrueCondition).OfType<AutomationElement>())
{
list.Add(item.Current.Name);
}
}
return list;
}
static void SelectTab(AutomationElement window, string name)
{
// get tab bar
var tab = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab));
// get tab
var item = tab.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, name));
if (item == null)
{
Console.WriteLine("Tab item '" + name + "' has been closed.");
return;
}
// select it
((SelectionItemPattern)item.GetCurrentPattern(SelectionItemPattern.Pattern)).Select();
}
static void OpenFile(AutomationElement window, string filePath)
{
// get menu bar
var menu = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuBar));
// get the "file" menu
var fileMenu = menu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "File"));
// open it
SafeExpand(fileMenu);
// get the new File menu that appears (this is quite specific to n++)
var subFileMenu = fileMenu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Menu));
// get the "open" menu
var openMenu = subFileMenu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Open..."));
// click it
((InvokePattern)openMenu.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
// get the new Open dialog (from root)
var openDialog = WaitForDialog(window);
// get the combobox
var cb = openDialog.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ComboBox));
// fill the filename
((ValuePattern)cb.GetCurrentPattern(ValuePattern.Pattern)).SetValue(filePath);
// get the open button
var openButton = openDialog.FindFirst(TreeScope.Children, new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
new PropertyCondition(AutomationElement.NameProperty, "Open")));
// press it
((InvokePattern)openButton.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
}
static AutomationElement WaitForDialog(AutomationElement element)
{
// note: this should be improved for error checking (timeouts, etc.)
while(true)
{
var openDialog = element.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
if (openDialog != null)
return openDialog;
}
}
static void SafeExpand(AutomationElement element)
{
// for some reason, menus in np++ behave badly
while (true)
{
try
{
((ExpandCollapsePattern)element.GetCurrentPattern(ExpandCollapsePattern.Pattern)).Expand();
return;
}
catch
{
}
}
}
}
If you wonder how this has been made, then you must read about UI Automation. The mother of all tools is called Inspect: https://msdn.microsoft.com/library/windows/desktop/dd318521.aspx
Make sure you get version at least 7.2.0.0. Note there is another one called UISpy but inspect is better.
Note, unfortunately, notepad++ tab text content - because it's based on the custom scintilla editor control - does not properly supports automation (we can't read from it easily, I suppose we'd have to use scintilla Windows messages for this), but it could be added to it (hey, scintilla guys, if you read this ... :).
In addition to the answer from Garath, you might also want to investigate the Windows automation API's i.e. the technology used to implement coded UI tests for GUI applications. As part of regular functional testing, I routinely control an external application from a set of NUnit tests using these API's.
Tools like UIAVerify will give you an indication of what controls are available in the application and you can use the Invoke Pattern (and many others) to interact with the controls at run-time.
If you want a detailed example of how to use the automation API's, the open source TestStack White project is pretty handy.
It is almost not possible if SendKeys is not an option but read more
Now more important part of the question- why:
We have to look how win32 application works: it has a WndProc/WindowProc method which is resposible for processing "events" form the UI.
So every event in the windows application must go through above method. SendKeys method is a special of SendMessage (MSDN), so you can use SendMessage to control other exe than your.
Simple code could look like:
IntPtr hwnd = FindWindow("Notepad++", null);
SendMessageA(hwnd, WM_COMMAND, SOMETHING1, SOMETHING2);
There is already on StackOverflow example how to do that with chrome: C# - Sending messages to Google Chrome from C# application , but this is only a start. You will have to find out what exactly message you want to send.
In exactly situation which you described I will try to send WM_MOUSE and WM_KEYBORD events to Notepad++ events, but it is only an idea :)

Highlighting a particular control while capturing screenshot of a dialog in web page in c#

I have a requirement to capture the screen shot of the opened dialog with a particular html control highlighted ( whose static id is given ). currently I Implemented the code following manner :
public void Snapshot()
{
Image currentImage = null;
currentImage = GetOpenedDialogFrame().CaptureImage();
}
public UITestControl GetOpenedDialogFrame()
{
var dialogsFrames = new HtmlDiv(this.BrowserMainWindow.UiMobiControlDocument);
dialogsFrames.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.Class, "mcw-dialog", PropertyExpressionOperator.Contains));
var dialogs = dialogsFrames.FindMatchingControls();
if (dialogs.Count == 0)
{
return null;
}
return dialogs[dialogs.Count - 1];
}
Now I have to write the code to highlight the particular html control while taking a screenshot. The DrawHighlight() method of Microsoft.VisualStudio.TestTools.UITesting.dll does not take any parameter so how can I highlight a particular html control in the screenshot.
DrawHighlight() is a method of a UI Control. It could be used in this style:
public void Snapshot()
{
Image currentImage = null;
var control = GetOpenedDialogFrame();
// TODO: protect the code below against control==null.
control.DrawHighlight();
currentImage = control.CaptureImage();
}
Whilst that answers your question about DrawHighlight, I am not sure it will achieve what you want. Please see this question the Microsoft forums where they are trying to do a similar screen capture.
Why not simply user the playback settings:
Playback.PlaybackSettings.LoggerOverrideState = HtmlLoggerState.AllActionSnapshot;
This will produce the html log file with all the screenshots that your codedui test went threw.
After searching for the matching controls you can try to highlight each one of them.
something like:
foreach( var control in controls)
{
control.drawhighlight();
}
that way you'll be able to which controls are located by the playback(qtagent to be more precise). furthermore this will help you decide which instance to refer to. (run and wait to see which controls are highlighted, pick the one you need and hard code it to be part of the test).
so after the test run you'll end up with something like:
var dialogs = dialogsFrames.FindMatchingControls();
dialogs[desiredLocation].drawhighlight();
hope this helps.

Post to facebook group with C# code

I am working on a windows application where I embedded webbroswercontrol. I am trying to post sample message to a open facebook group. I am unable to change value of a textbox with c#. When ever I automate click it says textbox value is null. What would be the fix?
<input type="hidden" autocomplete="off" class="mentionsHidden"
name="xhpc_message" value="lklklkl">
HtmlElement textBox = this.FindControlByName("xhpc_message",
this.webBrowser.Document.All);
//Click Code
var elements = webBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement element in elements)
{
// If there's more than one button, you can check the
//element.InnerHTML to see if it's the one you want
if (element.InnerText.Contains("Post"))
{
if (textBox.InnerText.Trim() == "Write something...")
{
textBox.Focus();
textBox.GetAttribute("value").Equals("Test Message");
IHTMLElement nativeElement = element.DomElement as IHTMLElement;
nativeElement.click();
break;
}
}
}
1) I suggest you to ensure, that textbox is null, not the textBox.InnerText. Usually inner text for elements is null, so its better to check the "placeholder" attribute and update the code with:
// if (textBox.InnerText.Trim() == "Write something...")
if (textBox.GetAttribute("placeholder") == "Write something...")
2) This code doesn't set the value. It gets the value and compares to "Test message".
textBox.GetAttribute("value").Equals("Test Message");
Just use SetValue instead.
textBox.SetAttribute("value", "Test message");
3) Ensure, that all operations are made after page is loaded.
public SomeFormName()
{
...
webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
}
void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs args)
{
// put your code here
}
4) Not sure, how the FindControlByName is working, so check a simple LINQ query to ensure that textbox is found.
var textbox = webBrowser.Document.All.OfType<HtmlElement>()
.Where(item => item.Name == "xhpc_message")
.FirstOrDefault()
;
Use the code below after the Document complete event has been fired completely in a separate function, after commenting your code.The URL in the webbrowser should be holding the Group Page on which the post is to happen.
private void AfteDocumentLoads()
{
HtmlElementCollection textBox = webBrowser.Document.GetElementsByTagName("textarea").GetElementsByName("xhpc_message");
HtmlElementCollection button = webBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement element in textBox)
{
foreach (HtmlElement btnelement in button)
{
if (btnelement.InnerText == "Post")
{
element.Focus();
element.InnerText = txtPortalUserId.Text.ToString();
btnelement.InvokeMember("Click");
}
}
}
}
I was also stuck as it was not posting earlier because I was using WebBrowser class to get current WebBrowser. Result was that the text was inputted to the Group as a 'dim' Comment. Even if I clicked manually on FB page it would say "This status update appears to be blank. Please write something or attach a link or photo to update your status."
I used the page's webbrowser & it worked cos' it came in proper manner on the page. Also little bit of changes are there in the LOCs

Unpin from start menu using c# in win8

I am developing a c# desktop application.
We need to unpin our application tile based on number of conditions. this could happen anytime during the application life-cycle, and not only during installation.
I saw this question on how to unpin a tile in CPP. I tried to do that also in C# with no success.
any help?
Update:
I was able to write a C# code that sets the AppUserModel_StartPinOption to APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL but it didn't help :(
this is the code:
private static void InstallShortcut(string linkPath)
{
// Find the path to the current executable
// String exePath = Process.GetCurrentProcess().MainModule.FileName; //Path to the current exe file that is running. C:\\...
IShellLinkW newShortcut = (IShellLinkW)new CShellLink();
// Create a shortcut to the exe
ErrorHelper.VerifySucceeded(newShortcut.SetPath(targetPath));
ErrorHelper.VerifySucceeded(newShortcut.SetArguments(""));
// Open the shortcut property store, set the AppUserModelId property
IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut;
var APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL = new PropVariant(0);
var StartPinOption = new PropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 12);
ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(StartPinOption, APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL));
ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
// Commit the shortcut to disk
IPersistFile newShortcutSave = (IPersistFile)newShortcut;
ErrorHelper.VerifySucceeded(newShortcutSave.Save(linkPath, true));
}
I tried both: removing the tile and then recreating it, and changing the params of an existing tile, but nothing worked, the tile stays pinned to the start menu.
Are you talking about your main application tile or a secondary tile? If you are referring to a secondary tile, there is sample code for unpinning in this article. The meat of it is (and I'm modifying a bit for simplicity; see the article for the full code):
// Check to see if this restaurant exists as a secondary tile and then unpin it
string restaurantKey = this.m_ViewModel.Restaurant.Key;
Button button = sender as Button;
if (button != null)
{
if (Windows.UI.StartScreen.SecondaryTile.Exists(restaurantKey))
{
SecondaryTile secondaryTile = new SecondaryTile(restaurantKey);
bool isUnpinned = await secondaryTile.RequestDeleteForSelectionAsync(GetElementRect((FrameworkElement)sender), Windows.UI.Popups.Placement.Above);
if (!isUnpinned)
{
// Do error handling here
}
}
else
{
// If we ever get to this point, something went wrong or the user manually
// removed the tile from their Start screen.
Debug.WriteLine(restaurantKey + " is not currently pinned.");
}
}
According to the MSDN article: System.AppUserModel.StartPinOption (Windows), the No pin install option should be (1)
var APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL = new PropVariant(1);
I got it working by doing this.

Categories