Can't find some nodes using HTMLAgilityPack - html-agility-pack

I want to get all div from a page :
I use two method :
Method 1 : (from parent)
int ahc = documentz0.DocumentNode.SelectNodes("//div[#class='facilitiesChecklist ']/div").Count;
foreach (HtmlNode link in documentz0.DocumentNode.SelectNodes("//div[#class='facilitiesChecklist ']/div"))
{
}
or Method 2 : (with full search)
int ahc = documentz0.DocumentNode.SelectNodes("//div[contains(#class,'facilitiesChecklistSection')]").Count;
foreach (HtmlNode link in documentz0.DocumentNode.SelectNodes("//div[contains(#class,'facilitiesChecklistSection')]"))
{
}
With two method ,there is 17 div in page with this property, but it select just 14 of them .
Closing tag has not problem.

Related

How to create separate DetailTable on each row in a RadGrid?

I have a telerik radgrid where columns and detail tables are declared like:
<telerik:RadGrid>
<Columns>
<telerik:GridBoundColumn/>
<telerik:GridBoundColumn/>
</Columns>
<DetailTables>
<telerik:GridTableView
<Columns>
<telerik:GridBoundColumn/>
<telerik:GridBoundColumn/>
</Columns>
</telerik:GridTableView
</DetailTables>
</telerik:RadGrid>
Which gives a nested grid like this:
Now, what I want is to be able to specify a detail table (those sub tables) per row, programmatically.
(I cannot be sure that the columns for the nested table that comes up when I expand the line fgvbvb will be the same as the columns when expanding the line xcxcv).
I have tried, without luck in the OnDataBound handler of the radgrid (in which I omitted <DetailTables>) to access the data structure for nested tables like this:
protected void OnRadGridDataBound(object sender, EventArgs e)
{
foreach (GridDataItem item in grdActivitiesToCopy.MasterTableView.Items)
{
var dg = item.ChildItem.NestedTableViews[0];
}
}
This will overindex the array NestedTableViews because it is empty. Also, item.ChildItem.NestedTableViews has no setter.
How do I populate each row with a detail table one by one manually?
According to Telerik:
RadGrid does not support mixing declarative grid columns with grid
columns added dynamically at runtime. You should either create all the
columns in the grid programmatically, or else define them all in the
ASPX file. When creating Detail tables, it should be created in the
PageInit event.
Creating a Hierarchical Grid Programmatically:
You should follow these basic steps in order to create hierarchical
RadGrid programmatically in the code-behind (having a data source
control for data content generation):
Create the grid dynamically in the Page_Init handler of the page by
calling its constructor.
Specify the preferred settings for your grid instance through its
properties.
Create columns for the grid dynamically. Keep in mind that you have to
first set their properties and then add them to the
MasterTableView/GridTableView collection (discussed in the first
paragraph of this same topic). Thus, their ViewState will be properly
persisted (as LoadViewState is raised after the Init event of the
page).
Set the proper ParentTableRelations for the GridTableViews (along with
their MasterKeyField and DetailKeyField attributes) and DataKeyNames
for the MasterTableView/GridTableViews in the code-behind of the page.
Assign data sources (through the DataSourceID attribute) for each
table in the grid hierarchy.If you do not want to use declarative
relations, generate the data in the NeedDataSource/DetailTableDataBind
handlers of the grid. On DetailTableDataBind you can determine which
data source should be related to the currently bound GridTableView by
checking its Name/DataSourceID property. Here, the Name property must
have a unique value for each detail table (this value has to be
defined previously by the developer) and the DataSourceID is the ID of
the DataSource control responsible for the corresponding detail table
content generation.
Code Sample:
RadGrid RadGrid1 = new RadGrid();
RadGrid1.DataSourceID = "SqlDataSource1";
RadGrid1.MasterTableView.DataKeyNames = new string[] { "CustomerID" };
RadGrid1.Skin = "Default";
RadGrid1.Width = Unit.Percentage(100);
RadGrid1.PageSize = 15;
RadGrid1.AllowPaging = true;
RadGrid1.AutoGenerateColumns = false;
//Add columns
GridBoundColumn boundColumn;
boundColumn = new GridBoundColumn();
boundColumn.DataField = "CustomerID";
boundColumn.HeaderText = "CustomerID";
RadGrid1.MasterTableView.Columns.Add(boundColumn);
boundColumn = new GridBoundColumn();
boundColumn.DataField = "ContactName";
boundColumn.HeaderText = "Contact Name";
RadGrid1.MasterTableView.Columns.Add(boundColumn);
//Detail table - Orders (II in hierarchy level)
GridTableView tableViewOrders = new GridTableView(RadGrid1);
tableViewOrders.DataSourceID = "SqlDataSource2";
tableViewOrders.DataKeyNames = new string[] { "OrderID" };
GridRelationFields relationFields = new GridRelationFields();
relationFields.MasterKeyField = "CustomerID";
relationFields.DetailKeyField = "CustomerID";
tableViewOrders.ParentTableRelation.Add(relationFields);
RadGrid1.MasterTableView.DetailTables.Add(tableViewOrders);
Please refer to this help article for more details:
http://docs.telerik.com/devtools/aspnet-ajax/controls/grid/defining-structure/creating-a-radgrid-programmatically#creating-a-hierarchical-grid-programmatically
First of all , because of the life cicle of a asp page. You can't access to a event on a detail table.
If you need to access detail tables , items etc ..
You need to add an method to the PreRender in the MasterTableView like this:
<MasterTableView DataSourceID="myDataSource"
AllowMultiColumnSorting="True"
DataKeyNames="Key1,Key2,KeyN"
HierarchyDefaultExpanded="True"
OnPreRender="Unnamed_PreRender" >
The method will recursively iterate through the grid.
The way you do it can change depending on your HieararchyLoadMode.
So this is my way to do it, easiest way exist if you are on Client or Serverbind mode.
Traversing and load mode by the telerik doc .
I'm pretty sure you don't want to :
"populate each row with a detail table one by one manually"
You want to have Multiple table at a Sub Level in your grid and display the rigth one programmatically.
And this is can be done in two easy step:
1/. Create every Detail table in your apsx page.
Please refer to this documentation for more information :
Several tables at a level
2/. Handle the display:
protected void Unnamed_PreRender(object sender, EventArgs e)
{
if (!IsPostBack) myControler(MASTERGRID.MasterTableView);
}
private void myControler(GridTableView gridTableView)
{
GridItem[] nestedViewItems = gridTableView.GetItems(GridItemType.NestedView);
foreach (GridNestedViewItem nestedViewItem in nestedViewItems)
{
foreach (GridTableView nestedView in nestedViewItem.NestedTableViews)
{
if (nestedView.Name == "mytable12" && nestedView.Items.Count == 0)
{ HideExpandColumn(nestedView, nestedView.ParentItem["ExpandColumn"]); }
else if (nestedView.Name == "mytable23")
{
if (nestedView.Items.Count == 0)//
HideExpandColumn(nestedView, nestedView.ParentItem["ExpandColumn"]);
else
{ }
}
if (nestedView.HasDetailTables)
{ myControler(nestedView); }
}
}
}
private void HideExpandColumn(GridTableView _GNVI, TableCell _cell)
{
if (_cell.Controls.Count > 0)
{
_cell.Controls[0].Visible = false;
_cell.Text = " ";
}
_GNVI.Visible = false;
}
You can hide a detail table using :
HideExpandColumn(nestedView, nestedView.ParentItem["ExpandColumn"]);
Or you can hide the parent of the detail table you tested using the detail table that is in param of the controler :
HideExpandColumn(gridTableView, nestedView.ParentItem["ExpandColumn"]);
HideExpandColumn will hide the expand control that stay sometimes even if you hide th detail table.
Bonus: If you need to access to a control in a detail table.
You can use this:
public static class ControlExtensions
{
public static Control FindIt(this Control control, string id)
{
if (control == null) return null;
Control ctrl = control.FindControl(id);
if (ctrl == null)
{
foreach (Control child in control.Controls)
{
ctrl = FindIt(child, id);
if (ctrl != null) break;
}
}
return ctrl;
}
}
Calling it in your controler like this :
else if (nestedView.Name == "DetailPV")
{
if (nestedView.Items.Count == 0)
HideExpandColumn(gridTableView, nestedView.ParentItem["ExpandColumn"]);
else
{
RadLabel ctrl = (RadLabel)this.FindIt("RadLabel11");
ctrl.Text += "<b>" + nestedView.Items.Count.ToString() + "</b>";
}

How to get a select in HTML Agility Pack

I'm trying to get the select element for a particular webpage, but I have trouble doing this.
Here's my code so far.
I'm trying to get the select element in a web page, containing the name "postalDistrictList", and none of my code works.
I also tried htmlweb.DocumentNode.SelectNodes("//select") but this returns null.
Does anyone have any idea how I can do this?
static void Main(string[] args)
{
HtmlNode.ElementsFlags.Remove("option");
HtmlWeb htmlweb = new HtmlWeb();
HtmlDocument html = htmlweb.Load("https://www.ura.gov.sg/realEstateWeb/realEstate/pageflow/transaction/submitSearch.do");
// HtmlNode bodyNode = html.DocumentNode.SelectNodes("//select");
HtmlNode bodyNode = html.DocumentNode.SelectSingleNode("/html/body");
HtmlNode selectNode = html.GetElementbyId("postalDistrictList");
HtmlNodeCollection selectNodes = html.DocumentNode.SelectNodes("//select[#name='postalDistrictList']");
// HtmlNode selectNode = html.DocumentNode.SelectSingleNode("//select[#name='postalDistrictList']");
HtmlNode node = selectNode;
// foreach (HtmlNode node in selectNodes)
{
Console.Out.Write(node.Attributes["options"].Value);
Console.Out.WriteLine();
}
}
Try the XPath //./select[#name='postalDistrictList'], i.e.
HtmlNodeCollection selectNodes = html.DocumentNode.SelectNodes("//./select[#name='postalDistrictList']");
should help you get the a collection of the select elements you are looking for.

Removing childnodes using HAP

When i'm trying to remove a childnode from my xpath i'm getting a weird error:-
System.ArgumentOutOfRangeException was unhandled
Message=Node "" was not found in the collection
I know there an issue with HAP childremoving but idk if they have fix it with the new release or not. My question is it my code that is wrong or is it HAP? In either way is there any way to get around that and remove those childnode?
Here is my code:-
List<MediNetScheme> medinetScheme = new List<MediNetScheme>();
HtmlDocument htdoc = new HtmlDocument();
htdoc.LoadHtml(results);
foreach (HtmlNode table in htdoc.DocumentNode.SelectNodes("//table[#class='list-medium']/tbody[1]/tr[#class]"))
{
string itemValue = string.Empty;
HtmlNode ansvarig =table.SelectSingleNode("//table[#class='list-medium']/tbody[1]/tr[#class]/td[4]");
table.RemoveChild(ansvarig, true);
itemValue = table.InnerText;
medinetScheme.Add(new MediNetScheme(){Datum=itemValue.Remove(15),Sections=itemValue.Remove(0,15)});
}
MediNetScheme.ItemsSource = medinetScheme;
Edit:-
My HTML document has a table with several rows that have this xpath :- "//table[#class='list-medium']/tbody1/tr[#class]". Each row in this table have 5 columns td1...td[5]. In my first foreach loop i'm using selectnodes to get the HTMLcode of each row in the table. What i want to do is to get only the innertext from the first 3 td in each row, which means i need to get rid of td[4] and td[5] from each row. When i used your edited code, i was able to get rid of td[4] and td[5] in the first row but not other rows that follows the first row.
Here is a pic of my HTML:-
the better way to remove a node from their parent in HtmlAgilityPack is this:
nodeToRemove.ParentNode.RemoveChild(nodeToRemove);
In your code you can use like this:
List<MediNetScheme> medinetScheme = new List<MediNetScheme>();
HtmlDocument htdoc = new HtmlDocument();
htdoc.LoadHtml(results);
foreach (HtmlNode table in htdoc.DocumentNode.SelectNodes("//table[#class='list-medium']/tbody[1]/tr[#class]"))
{
string itemValue = string.Empty;
HtmlNode ansvarig =table.SelectSingleNode("//table[#class='list-medium']/tbody[1]/tr[#class]/td[4]");
ansvarig.ParentNode.RemoveChild(ansvarig);
itemValue = table.InnerText;
medinetScheme.Add(new MediNetScheme(){Datum=itemValue.Remove(15),Sections=itemValue.Remove(0,15)});
}
MediNetScheme.ItemsSource = medinetScheme;
I hope this will be useful for you :)
EDITED:
Do you want to get the InnerText of the three first td's in each row.
I'm checking your code and i think that xpath inside the foreach is wrong.
I would change the xpath for a classic counted loop with linq like this:
foreach (HtmlNode trNodes in htdoc.DocumentNode.SelectNodes("//table[#class='list-medium']/tbody[1]/tr[#class]"))
{
string itemValue = string.Empty;
int position = 1;
foreach (var td in tr.DescendantNodes("td"))
{
itemValue = td .InnerText;
medinetScheme.Add(new MediNetScheme(){Datum=itemValue.Remove(15),Sections=itemValue.Remove(0,15)});
position++;
if (position == 3)
break;
}
After few hours of testing different codes and ways to achive what i wanted, i figured it out.
But i have to thank vfportero for his answer and flag it as answer too.
The answer to the edited verion of my question is simply this code ;)
List<MediNetScheme> medinetScheme = new List<MediNetScheme>();
HtmlDocument htdoc = new HtmlDocument();
htdoc.LoadHtml(results);
foreach (HtmlNode table in htdoc.DocumentNode.SelectNodes("//table[#class='list-medium']/tbody[1]/tr[#class]"))
{
table.ChildNodes.RemoveAt(3);
string itemValue = table.InnerText;
medinetScheme.Add(new MediNetScheme(){Datum=itemValue.Remove(15),Sections=itemValue.Remove(0,15)});
}
MediNetScheme.ItemsSource = medinetScheme;
You can see that i omit RemoveChild method coz it was not doing what i wanted (plz read the edit of my question), and instead i used .ChildNodes.RemoveAt(int //the placeof the child you want to remove).
Hope this will help some other ppl facing the same problem.
Yours

asp.net mvc 3 EF HTML MultiSelect - How to retrieve multiple values using LINQ

I have an Advanced Search form, It contains, input boxes, dropdown lists, etc. And now I want to change a dropdown list to a multiselect list.
I can set the view, like this:
#Html.ListBoxFor(model => model.IdState, null, new { size = "7" })
But I don't know how to change the controller from:
public ActionResult Index(FormData model)
{
IQueryable<mytable> results = from s in db.mytable.Include("State").where(s=> (model.IdState != null ? s.IdState == model.IdState : true)) select s;
return View(result);
}
To a multiple IdState Result???
Earlier the IdState was:
int IdState;
Now is:
IEnumerable<int> IdState;
Thanks for your time!
I still can't make it work. In SQL will be:
SELECT * FROM MyTable WHERE IdState IN (1,2,5,7,10)
Any Idea? The http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx Don't show similar situation
Workaround
I found on this discussion: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/095745fe-dcf0-4142-b684-b7e4a1ab59f0/ this code snippet:
IQueryable<Foo> foos = context.Foo.Where("it.Id in {1, 2, 3, ...}");
I changed to
string sState = "";
foreach (int a in model.IdState ) sState += (a.ToString() + ",");
sState = sState .Substring(0, sState .Length - 1);
IQueryable<mytable> results = from s in db.mytable.Where("it.IdState in {" + sState + "}");
And It works great!!!
I hope can find the final solution
Instead of
s.IdState == model.IdState
try using
model.IdState.Contains(s.IdState)

Traverse upwards through nested ajax accordion web controls; Type checking and FindControl

I have nested ajax accordion web controls (nested 3 deep or sometimes 4 deep) and I am trying to find a nested accordion's parent accordion. I want to do this when either of the deep-most accordions becomes databound.
protected void grandChildAccordion_ItemDataBound(object sender, AjaxControlToolkit.AccordionItemEventArgs e)
{
if (e.AccordionItem.ItemType == AjaxControlToolkit.AccordionItemType.Header)
{
// find parent accordion
}
}
Sometimes the parent accordion will be at e.AccordionItem.Parent.Parent.Parent level or e.AccordionItem.Parent.Parent.Parent.Parent level.
So, how do I check if e.AccordionItem.Parent equals AjaxControlToolkit.AccordionContentPanel "type"?
The (not-so-elegant) Solution: I have nested accordions, the deep-most accordion levels contains lists of documents, and each accordion header has a label showing a count of documents nested within it. Starting from the deep-most accordion level where I get a count of documents, I traverse up the nested accordions and find each accordion header, allowing me to find their doc count label and add a value to it.. hope this helps someone:
// Save doc count. It will be displayed in doc count label within current accordion header, and the value will then be added to the doc count of each parent accordion's header.
int curDocCount = qcDocsBO.FetchQCDocumentCountByProjectID(SessionHandler.ProjectID, qcDocsBO.LSDItemID);
lblDocCount.Text = curDocCount.ToString();
// A deep-most accordion has just been DataBound. Start traversing upwards through nested accordions, adding the doc count to each parent accordion header's doc count label.
AjaxControlToolkit.Accordion curAccordion = sender as AjaxControlToolkit.Accordion;
AjaxControlToolkit.AccordionPane parentAccCP = curAccordion.Parent.Parent.Parent as AjaxControlToolkit.AccordionPane;
if (parentAccCP == null)
parentAccCP = curAccordion.Parent.Parent.Parent.Parent as AjaxControlToolkit.AccordionPane;
while (parentAccCP != null)
{
Label parentDocCountLabel = (Label)parentAccCP.FindControl("lblDocCount");
if (parentDocCountLabel.Text == "")
parentDocCountLabel.Text = "0";
parentDocCountLabel.Text = (Convert.ToInt32(parentDocCountLabel.Text) + curDocCount).ToString();
AjaxControlToolkit.AccordionPane nextParentAccCP = parentAccCP.Parent.Parent.Parent as AjaxControlToolkit.AccordionPane;
if (nextParentAccCP == null)
nextParentAccCP = parentAccCP.Parent.Parent.Parent.Parent as AjaxControlToolkit.AccordionPane;
if (nextParentAccCP == null)
nextParentAccCP = parentAccCP.Parent.Parent.Parent.Parent.Parent as AjaxControlToolkit.AccordionPane;
if (nextParentAccCP == null)
nextParentAccCP = parentAccCP.Parent.Parent.Parent.Parent.Parent.Parent as AjaxControlToolkit.AccordionPane;
if (nextParentAccCP != null)
parentAccCP = nextParentAccCP;
else
parentAccCP = null;
}
You can always do type checks using the is keyword or safe cast using the as keyword.
Consider the following:
if(e.AccordionItem.Parent is AjaxControlToolkit.AccordionContentPanel) {
...
}
or this
var accordion = e.AccordionItem.Parent as AjaxControlToolkit.AccordionContentPanel;
if(accordion != null) {
...
}
accordion is null if the safe cast wasn't successful.
Using either or both of these techniques, you can iterate upwards in the hierarchy and stop when you encounter the type you are looking for.

Resources