Get all <td> title of a table column with coded ui - c#

I need to check a filter function on a table.
This filter is only on the first cell of each row and I'm trying to figure out how to get all those values...
I tried with something like
public bool CheckSearchResults(HtmlControl GridTable, string FilterTxt)
{
List<string> Elements = new List<string>();
foreach (HtmlCell cell in GridTable.GetChildren())
{
Elements.Add(cell.FilterProperties["title"]);
}
List<string> Results = Elements.FindAll(l => l.Contains(FilterTxt));
return Results.Count == Elements.Count;
}
but I get stuck at the foreach loop...
maybe there's a simply way with linq, but i don't know it so much
edit:
all the cells i need have the same custom html tag.
with this code i should get them all, but i don't know how to iterate
HtmlDocument Document = this.UIPageWindow.UIPageDocument;
HtmlControl GridTable = this.UIPageWindow.UIPageDocument.UIPageGridTable;
HtmlCell Cells = new HtmlCell(GridTable);
Cells.FilterProperties["custom_control"] = "firstCellOfRow";
also because there's no GetEnumerator function or query models for HtmlCell objects, which are part of Microsoft.VisualStudio.TestTools.UITesting.HtmlControl library -.-
edit2:
i found this article and i tried this
public bool CheckSearchResults(string FilterTxt)
{
HtmlDocument Document = this.UIPageWindow.UIPageDocument;
HtmlControl GridTable = this.UIPageWindow.UIPageDocument.UIPageGridTable;
HtmlRow rows = new HtmlRow(GridTable);
rows.SearchProperties[HtmlRow.PropertyNames.Class] = "ui-widget-content jqgrow ui-row-ltr";
HtmlControl cells = new HtmlControl(rows);
cells.SearchProperties["custom_control"] = "firstCellOfRow";
UITestControlCollection collection = cells.FindMatchingControls();
List<string> Elements = new List<string>();
foreach (UITestControl elem in collection)
{
HtmlCell cell = (HtmlCell)elem;
Elements.Add(cell.GetProperty("Title").ToString());
}
List<string> Results = Elements.FindAll(l => l.Contains(FilterTxt));
return Results.Count == Elements.Count;
}
but i get an empty collection...

Try Cell.Title or Cell.GetProperty("Title"). SearchProperties and FilterProperties are only there for searching for a UI element. They either come from the UIMap or from code if you fill them out with hand. Otherwise your code should should work.
Or you can use a LINQ query (?) like:
var FilteredElements =
from Cell in UIMap...GridTable.GetChildren()
where Cell.GetProperty("Title").ToString().Contains(FilterTxt)
select Cell;
You could also try to record a cell, add it to the UIMap, set its search or filter properties to match your filtering, then call UIMap...Cell.FindMatchingControls() and it should return all matching cells.
The problem now is that you are limiting your search for one row of the table. HtmlControl cells = new HtmlControl(rows); here the constructor parameter sets a search limit container and not the direct parent of the control. It should be the GridTable if you want to search all cells in the table. Best solution would be to use the recorder to get a cell control then modify its search and filter properties in the UIMap to match all cells you are looking for. Tho in my opinion you should stick with a hand coded filtering. Something like:
foreach(var row in GridTable.GetChildren())
{
foreach(var cell in row.GetChildren())
{
//filter cell here
}
}
Check with AccExplorer or the recorder if the hierarchy is right. You should also use debug to be sure if the loops are getting the right controls and see the properties of the cells so you will know if the filter function is right.

I resolved scraping pages html by myself
static public List<string> GetTdTitles(string htmlCode, string TdSearchPattern)
{
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(htmlCode);
HtmlNodeCollection collection = doc.DocumentNode.SelectNodes("//td[#" + TdSearchPattern + "]");
List<string> Results = new List<string>();
foreach (HtmlNode node in collection)
{
Results.Add(node.InnerText);
}
return Results;
}
I'm freakin' hating those stupid coded ui test -.-
btw, thanks for the help

Related

Selenium Webdriver can't find table elements

I'm new with selenium, I can not figure out how to find some elements from an HTML page.
I reproduced the page here :
http://www.ladiana.it/test/test.htm
I need to click on this button :
Selection
and after the table appears, to read and write their values :
values
I'm using c# and I tried all possible way, I also tried to list all the elements with the Id
: "targetDemandReductionMW"
IList IWebElement> all = driver2.FindElements(By.Id("targetDemandReductionMW"));
I get a list of 52 void elements.
Here some other code , it doesn't generate any error but didn't work:
IWebElement element_xpath = driver2.FindElement(By.ClassName("tab_buttonSel"));
element_xpath.Click();
IWebElement table = driver2.FindElement(By.Id("drr1OfferAM"));
var rows = table.FindElements(By.TagName("tr"));
int conteggio = 0;
foreach (var row in rows)
{
var rowTds = row.FindElements(By.TagName("td"));
foreach (var td in rowTds)
{
IWebElement a = td.FindElement(By.XPath("//*[#id='targetDemandReductionMW']"));
Console.WriteLine(conteggio++.ToString()+" "+a.GetAttribute("href")+" "+ a.Text);
}
}
Any help?
Thanks
For the button you need to click, you can use:
//div[#id='tabContainer']/div[2]/following::tr/td[text()='DRR1 Offer']
For the xpath for the table records you can use:
//table[#id='drr1OfferPMHead']//td[#id='targetDemandReductionMW']
This returns the 13 rows that you are looking for. You can then do FindElements on this and get a list.
List<String> search = new List<string>();
IReadOnlyList<IWebElement> cells = driver.FindElements(By.Xpath("//table[#id='drr1OfferPMHead']//td[#id='targetDemandReductionMW']"));
foreach (IWebElement cell in cells)
{
search.Add(cell.Text);
}
The cell (in the html) has cellclick event , but if i try with :
foreach (IWebElement cell in cells)
{
search.Add(cell.Text);
if (cell.Text=="300.0")
{
cell.click();
}
}
it come out this error : "OpenQA.Selenium.ElementNotInteractableException: 'element not interactable"

Selenium C# Tables - reading each values and adding them and Returning as List<string>

I can able to read the table values BUT Im unable to add the values inside foreach loop and returning them as List<string>.
Please share your approach and Will be great helpful
IWebElement tableElement = driver.FindElement(By.XPath("//div[#id='table']"));
IList<IWebElement> tableRow = tableElement.FindElements(By.TagName("tr"));
IList<IWebElement> rowTD;
foreach (IWebElement row in tableRow)
{
rowTD = row.FindElements(By.TagName("td"));
//add the values in list??
}
return list
After this how can I add the td text values into an List<string>? I want this function to return the list.
I'm guessing that you are trying to return all of the table data elements as a list of strings.
If you want to stick with a similar approach of only getting the text of the td elements, then I would do something like this:
IWebElement tableElement = driver.FindElement(By.XPath("//div[#id='table']"));
IList<IWebElement> tableDataElements = tableElement.FindElements(By.TagName("td"));
var reults = new List<string>();
foreach (IWebElement tableDataElement in tableDataElements)
{
var tableData = tableDataElement.Text;
reults.add(tableData);
}
return reults;
You can combine Michiel's answer with LINQ to do this even more concisely--without a foreach loop at all:
First, add
using System.Linq;
Then, you can replace your entire code block with:
return driver.FindElement(By.XPath("//div[#id='table']"))
.FindElements(By.TagName("td"))
.Select(e => e.Text)
.ToList();

Setting checkbox selections in Grid View from comma separated list from SQL Server

I was wondering if anyone had an example of pulling a csv list of options from the database and checking the rows based on the text value pulled. So say I have a grid view with options showing various programming languages. I would like to match those languages to the ones that have been saved in the Database.
O ASP.NET
O C#
O VB.NET
...
So, if my returned list of languages is just ASP.NET and VB.NET, how do I get the checkboxes of those grid view rows to be checked? I could use View State but I am thinking more a data-based set of information by stepping through the recordset and checking the items based on the returned dataset.
Surprised I was able to figure this one out but suddenly the answer was staring me in the face.
protected void SetLangs()
{
List<string> sellangs = new List<string>();
string langs = hfPrgLangs.Value;
string langtrim = langs.Replace(" ", "");
sellangs = langtrim.Split(',').ToList<string>();
foreach (DataListItem dl in dlLanguages.Items)
{
Label lblLangName = (dl.FindControl("lblLangName") as Label);
CheckBox isChk = (dl.FindControl("cbLang") as CheckBox);
for (int i = 0; i < sellangs.Count; i++)
{
if (sellangs[i].ToString() == lblLangName.Text.ToString())
{
isChk.Checked = true;
}
}
}
}

Find item in list by property and know the item name

I have struggled with figuring out how to find an item in a list by checking for a property and then isolating and naming this item. I need to do this to set a value that corresponds with that specific string. It wouldn't work if I just found that there is one, or several items in the list that match the property. My code is massive, but here's a snippet of it:
List<string> diag_right = new List<string>();
diag_right.Add(b3); //Add the string b3
diag_right.Add(b5); //Add the string b5
diag_right.Add(b7); //Add the string b7
if (diag_right.Exists(a => a.Equals("")))
{
//Find the item (string name, b3, b5, etc.) that was found as matching the blank property
}
Is it possible to do this? I know I can do this by checking to see if each string matches this property individually, but is there a faster way to do this?
Many ways this can be achieved, I prefer simple approach, loop collection using index and modify matching field
for(int index=0; index< diag_right.Count(); index++)
{
if(diag_right[index] == "b3")
{
// update
diag_right[index] = "b8";
}
}
Linq approach
This approach I just used Linq to fetch all indexes for a matching string.
var indexes = diag_right.Where(e=>e.Equals("searchstring")).Select((c,i) => i).ToList();
foreach(int index in indexes) // loop through each line.
{
diag_right[index] = "newvalue"; // set new value.
}
Working Demo

Filling a Listview/Gridview dynamically with XML file

good afternoon guys and girls! i am trying to learn c#(WPF) for about 2 weeks now and i'm encountering some problems which google didnt help me to solve so far :/
Lets say i have a random XML file:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<ADRESSE>
<NAME1>John</NAME1>
<NAME2>Doe</NAME2>
<STRASSE1>Heystreet</STRASSE1>
<STRASSE2>9</STRASSE2>
<LAND>AUT</LAND>
<PLZ>1050</PLZ>
<ORT>Vienna</ORT>
</ADRESSE>
</XML>
Pretend this XML has like 100 entries.
Now i'll have a simple Listview called "lv1" in my XAML and a button.
int counter = 0;
GridView gv1 = new GridView();
XDocument rndfile = XDocument.Load(#"C:\Users\...\random.xml");
foreach (XElement xele in rndfile.Descendants("ADRESSE")) //#1
{
GridViewColumn gvc = new GridViewColumn();
gvc.DisplayMemberBinding = new Binding("Feld"+counter);
gvc.Header = xele.Name.LocalName; // #2
gv1.Columns.Add(gvc);
string feldx = string.Format(#"Feld{0}", counter);
// MessageBox.Show(feldx+"||"+"Feld"+counter); //was for me to test if names are identical
lv1.Items.Add(new { feldx = xele.Element("Childelement of ADRESSE").Value }); //#3+4
counter++;
}
lv1.View = gv1;
1 and 3 are my actual problems, whereby 1 and 2 are the same thing i guess.
So basically what i want to do is press the Button, load the XML and create for each Child of ADRESSE a column with the name of the current Child and directly fill it with the XML content.
The problems i am encountering now: #1 the foreach loop only runs for each entry called ADRESSE instead of each child element of it and i just cant figure out how to get the childs of it without breaking any syntax (tried it with Elements() but he doesnt like that in the loop then).. So for now the XML above would only create ONE row instead of 7, because theres only one ADRESSE entry in the file.
For the second problem i want to name the Columns with the Childname of the XML, but due to the problem at #1 it wont work as intended. (or is this thought generally wrong?)
And the third problem is the dynamical filling of the columns. As far as i could see it lv1.Items.Add({...}) does not accept the feldx from above, but thinks it is a own name and doesn't fill it correctly then, because there are no columnbindings named feldx. For #4 i need something like feldx = xele.Element(#"{0}", ChildName).Valuefor the correct content of the column then
I really tried to look and solve this on my own, but all stuff i found on XML or gridviews here or at mycsharp/msdn either was only with static (hardcoded) XAML entrys and bindings or just XML files where you know what entrys there are (so again practically hardcoded). So i just hope my request just isn't too barefaced and someone could enlighten me a little
Edit #1 :
var rootele = rndfile.Root.Element("ADRESSE").Elements(); //<-- worked like a charm. My problem was that i tried to fiddle this into the foreach loop itself, which wasn't accepted somehow - so many thanks har07
int counter = 0;
foreach (XElement xele in rootele)
{
GridViewColumn gvc = new GridViewColumn();
gvc.DisplayMemberBinding = new Binding("Feld"+counter);
gvc.Header = xele.Name.LocalName;
gv1.Columns.Add(gvc);
lv1.Items.Add(new { feld_x_ = xele.Element("Childelement of ADRESSE").Value }); // <-- for this i am trying to find a solution and post it again, or maybe someone else knows how to add a changing Binding Name into the .Add()
counter++;
}
lv1.View = gv1;
Your foreach loop is obviously tell to loop through each <ADRESSE> node, you can fix it like one of the following :
//approach 1 :
var chldNodes = rndfile.Descendants("ADRESSE").First().Elements();
//or approach 2 :
var chldNodes = rndfile.Root.Element("ADRESSE").Elements();
//then loop through childNodes
foreach (XElement xele in chldNodes)
3 & 4 looks simply wrong. That way, even if it worked, you'll ended with multiple rows, one for each column with corresponding row's column filled with value from XML and the rest columns are empty. Fix 1 & 2 then you can focused on 3 & 4, edit the question with your closest try -or open new question- if you can't make it work at the end.
EDIT :
Quick searching on google suggests that ListView with dynamic column seems not a trivial task (some related threads: 1, 2)
You may want to try this way (crafted based on link no. 2 above) :
....
var columnValues = new List<string>();
foreach (XElement xele in rootele)
{
GridViewColumn gvc = new GridViewColumn();
gvc.DisplayMemberBinding = new Binding(String.Format("[{0}]", counter));
gvc.Header = xele.Name.LocalName;
gv1.Columns.Add(gvc);
columnValues.Add((string)xele);
counter++;
}
lv1.Items.Add(columnValues);
....
Your XML file is incorrect.
<NAME2>Doe</NAME3>
should be
<NAME2>Doe</NAME2>
I'll just write the partial Solution to this now, which will require some more hardcoding than i wanted, but better than nothing - maybe someone else needs it (Many Thanks again to har07)
GridView gv1 = new GridView();
XDocument rndfile = XDocument.Load(#"C:\Users\...\rndfile.xml");
var rootele = rndfile.Descendants("ADRESSE").First().Elements();
int counter = 0;
//Create empty Listview depending on Sub-entrys of ADRESSE
foreach (XElement xele in rootele)
{
GridViewColumn gvc = new GridViewColumn();
gvc.DisplayMemberBinding = new Binding("Feld"+counter);//gets a Binding name Feld# for each child
gvc.Header = xele.Name.LocalName; //Name my Columns after the <tags> of the childs
gv1.Columns.Add(gvc); //add the current Column to my Gridview
counter++;
} //Listview created
//Fill my list for every single Parent-element in my XML file
foreach (XElement xe in rndfile.Descendants("ADRESSE"))
{
lv1.Items.Add(new
{
Feld0 = xe.Element("NAME1").Value,
Feld1 = xe.Element("NAME2").Value,
Feld2 = xe.Element("STRASSE1").Value,
Feld3 = xe.Element("STRASSE2").Value,
Feld4 = xe.Element("LAND").Value,
Feld5 = xe.Element("PLZ").Value,
Feld6 = xe.Element("ORT").Value
});
}//List filled
This way it doesn't matter in which order i have the ChildElements in the subsequent ADRESSE tags AFTER my first (Since the first Parent is needed to create the columns correctly). So the second shuffled Entry of ADRESSE below :
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<ADRESSE>
<NAME1>John</NAME1>
<NAME2>Doe</NAME2>
<STRASSE1>Heystreet</STRASSE1>
<STRASSE2>9</STRASSE2>
<LAND>AUT</LAND>
<PLZ>1050</PLZ>
<ORT>Vienna</ORT>
</ADRESSE>
<ADRESSE>
<LAND>AUT</LAND>
<PLZ>1060</PLZ>
<ORT>Vienna</ORT>
<NAME1>Jane</NAME1>
<STRASSE1>Wherestreet</STRASSE1>
<STRASSE2>11</STRASSE2>
<NAME2>Doe</NAME2>
</ADRESSE>
</XML>
Will still be filled in the correct Columns. Only Problems left now are the dependance of the Bindings (or namings of the Columns) on the First Entry and that i have to rename every X in all FeldX = xe.Element("XXX").Value if i use another kind of XML with different entries.
Still: if anyone knows a solution close to (i know that's completely broken but) something like ->
lv1.Items.Add(new{ string.Format(#"Feld{0}", counter) = xe.Element(#"{0}", xele.Name.LocalName).Value}
to put into a loop i'd be really grateful!

Categories