How to build an ASP.NET TreeView From Scratch...? - c#

I am trying to build a nested/threaded comment system in ASP.NET. I don't know how the PHP guys do it. Its much harder than first imagined.
I am trying my damnedest get Hierarchical data to output to the user, but its not working.
I have a table with text, a itemID and a parentID.
I want to display the information in a tree view format, but asp.net's standard control just doesn't work...
Can anyone point me in the right direction of how to output this into a treeview. I have tried nesting repeaters, straight out html building from codebehind and the treeview control.
I still haven't found a solution... Can anyone help me out?

Quickly, by head (could not check in VS2k8 now), I would do it something like this using Linq to SQL
private void BuildTree()
{
List<Item> items = from item in dataContext.Items
select item;
List<Item> rootItems = items.FindAll(p => p.ParentID == null );
foreach ( Item item in rootItems )
{
TreeViewNode tvi = new TreeViewNode(item.text);
BuildChildNodes(tvi, items, item.ID);
YourTreeNodeName.Nodes.Add(tvi);
}
}
private void BuildChildNodes(TreeViewNode parentNode, List<Item> items, long parentID)
{
List<Item> children = items.FindAll ( p => p.ParentID = parentID );
foreach( Item item in children)
{
TreeViewNode tvi = new TreeViewNode(item.text);
parentNode.Nodes.Add(tvi);
BuildChildNodes(tvi, items, item.ID);
}
}

As I understand it, if you have a database table with hierarchical data, you have two options: create your own custom data source, or programmatically bind the treeview to the database table.
I don't have code for you, but you could use following these steps:
declare treeview control within asp.net page
populate a DataTable (via a SqlDataAdapter) with your heirarchical data (SELECT itemID, parentID FROM...)
use that same DataTable to create a DataView of top-level items (parentID will be null)
for each row of the DataView, recursively add treeview items by filtering another DataView where each DataViewRow's parentID is equal to the row you're looping through in step 4
pretty much all of this is done using TreeView.Nodes.Add(theNewNode), where theNewNode is an instance of the TreeNode object
I know this all sounds very confusing, but I've done it in the past. I found great info in Stephen Walther ASP.NET Unleashed book, which has entire sections devoted to accomplishing this.

Related

How to create and bind a GridView where the columns are determined by a database table?

I'd like to create a GridView (preferably telerik) where the columns are generated and bound based on rows in the database. The grid should look like this:
Here is an example of what the database setup is.
I have 3 database tables as follows:
Employees - A list of Employee Names
States- A list of States
EmployeeStates- join table, which has an EmployeeID, StateID, and boolean for checked or unchecked.
New rows can be added to both database tables so that the list of employees and list of cities will get longer.
How do I bound this to a GridView?
Don't know if you still have this issue, but we solved it a while back. It wasn't straightforward, but can be done. Our tables were for treatment sites (parts of the body) and treatment types, and we wanted to display a grid showing all the possible sites, along with the ones ticked for the patient in question.
The server-side query to get the data looked like this...
int systemID = 210;
var cross = TreatmentSites.Select(ts => new {
TreatmentSiteID = ts.ID,
TreatmentSiteName = ts.Description,
Checked = string
.Join("", TreatmentCheckTypes
.OrderBy(tct => tct.ID)
.Select(cs => SystemTreatmentSitesCheckeds
.Any(s => s.Active
&& s.SystemID == systemID
&& s.TreatmentSiteID == ts.ID
&& s.TreatmentCheckTypeID == cs.ID) ? "y" : "n"))
})
.ToArray();
This pulled out a list of sites, with a string of the form nnyyn for the types that were checked.
Nest job was to massage that into something that could be used by WPF. For that, we used the following code...
Dictionary<String, dynamic>[] data = new Dictionary<string, dynamic>[cross.Count()];
for (int crossN = 0; crossN < cross.Count(); crossN++) {
var d = new Dictionary<String, dynamic>();
d["TreatmentSiteID"] = cross[crossN].TreatmentSiteID;
d["TreatmentSiteName"] = cross[crossN].TreatmentSiteName;
for (int checktypeN = 0; checktypeN < treatmentCheckTypes.Count(); checktypeN++) {
d["C" + checktypeN] = cross[crossN].Checked[checktypeN] == 'y';
}
data[crossN] = d;
}
That gave us the data we needed. With that, the WPF view was able to add columns to the grid in code, then bind the data to the grid.
The above is the basic idea. More details of how it works can be found on my blog.
Hope that helps.
If you follow this link You should have enough examples to get some idea. Think points to the WPF telerik components.
You could set or bind the ItemsSource property of the RadGridView to an IEnumerable<YourType> where your YourType is a class that has the Employee, NJ, PA, RI and NY properties.
Or to a DataView of a DataTable.
You will have to write an SQL query that selects the appropriate data from the database and then create an instance of YourType for each returned record, or use the DataTable directly. There are plenty of example of how to communicate with a database available online: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldataadapter(v=vs.110).aspx

Does Linq OrderBy Not Sort Original Collection?

I am using Linq To Sql as my database layer for the first time and I have run into an issue. Basically, I have a form that allows users to create predefined jobs. A predefined job can have many predefined job items. For example, a predefined job might be something like an oil change. The predefined job items would be oil, labor, etc. In my database PredefinedJobs is a table and PredefinedJobItems is another table with a foreign key back to PredefinedJobs. I have a form for adding predefined jobs that has the Linq-to-Sql class backing the form as a class variable. There is a ListView on the form that displays all of the jobs items. A new feature has required me to track the position of an item in the ListView. For example, if my item ListView looks like below, note the order:
Qty Name Desc ItemOrder
4 Oil Penzoil 0
1 Labor 1
Because the items are added via a child form I do not want to provide access to the ListView. So, I created the method below in an attempt to both create the ItemOrder and sort the collection on the PredefinedJob Linq to Sql object. It does not appear that the OrderBy function on the List actually sorts the collection on the PredefinedJob. What would be the best way to maintain order on the Linq to Sql collection (i.e. PredefinedJob.fkJobItems)? Or, would it be better to just pass a reference to my ListView into the child form that adds the items to the jobs where I have access to the selectedIndex?
private SortAndOrderItems(List<PredefinedJobsItem> items)
{
var nextItemOrderNumber = items.Max(max => max.ItemOrder) + 1;
foreach (var item in items)
{
if (item.ItemOrder == null)
{
item.ItemOrder = nextItemOrderNumber;
nextItemOrderNumber++;
}
}
items.OrderBy(i => i.ItemOrder).ToList();
}
OrderBy creates a new query, that will, when executed, not alter your original list.
Why not just the Sort method of the List?
items.Sort((a, b) => a.ItemOrder.CompareTo(b.ItemOrder));
I think you were looking for List<>.Sort
class Cmp : IComparer<PredefinedJobsItem>
{
public int Compare(PredefinedJobsItem x, PredefinedJobsItem y)
{
return x.ItemOrder.CompareTo(y.ItemOrder);
}
}
var comparison = new Cmp();
items.Sort(comparison);

How to create session variables without knowing how many to create?

First of all, I'm using Visual Studio 2010 (Visual C#) and ASP.NET.
I'm working with a GridView that displays the current open positions at my company. I have a column of checkboxes where applicants can check off the position(s) for which they want to apply. To eliminate duplicate data, I created a linking table between my three main tables (POSITION, APPLICANT, and APPLICATION). It's made up of just the primary keys from each of those tables, so if one person applies for 3 positions, we won't have 3 whole applications to sift through.
I need to select the PositionID's of the positions they selected and store them in session variables for later use.
My question is, how do I do that without knowing how many they have checked? I don't want to just create a bunch of unnecessary variables that won't be used. I figure I'll have to use a foreach loop, but within the loop, I don't know how to tell it to create a new session variable and store the ID in it.
I sure hope this question makes sense. If you need clarification, let me know.
You can just create a List<int> or whatever datatype PositionID is and store that in the session variable.
In fact, I would create a property in the control or page as
public List<int> SelectedPositionIDList
{
get
{
if(Session["SelectedPositions"] != null)
return Session["SelectedPositions"] as List<int>;
return new List<int>();
}
set
{
Session["SelectedPositions"] = value;
}
}
you can iterate through the list of position ids as ,
foreach(int positionId in SelectedPositionIdList)
{
//Do something.
}
Of course, you need to grab the ids from the gridview when they want to save, or do some action. You can probably do that by looping through the gridview rows based on your implementation. Something like below.
List<int> positions = new List<int>();
foreach(GridviewRow row in gdPositions)
{
CheckBox cb = row.FindControl("checkbox") as CheckBox;
if(cb != null && cb.Checked)
positions.Add(/*find position id from row here*/);
}
if(positions.Count > 0)
Session["SelectedPositionIdList"] = positions;

Searching a DataGrid when it is sorted

I have implemented a simple search function that highlights a row in a DataGrid based on a search query. The gist of it is shown below:
public bool scrollToSearch(string query) {
dataGrid.SelectedItems.Clear();
for (; searchIndex < registrants.Count; searchIndex++) {
foreach (string field in registrants[searchIndex]) {
if (field.ToLower().Contains(query)) {
dataGrid.SelectedItem = registrants[searchIndex];
dataGrid.ScrollIntoView(registrants[searchIndex]);
searchIndex++;
return true;
}
}
}
}
It searches the list for a match, then highlights(selects) that row and scrolls it into view. The problem is that, when the DataGrid is sorted, the search will highlight a seemingly random row instead of the first result, because it is searching the original, unsorted list. Is there a way I can get at the sorted list to search it instead?
I agree with Anurag's comment that the ListCollectionView is the right way to go. See here for documentation on the ListCollectionView. The ListCollectionView creates another layer between your source and display which handles sorting, filtering and grouping. Bind your DataGrid to the ListCollectionView and use SetCurrent() to specify the selected item.
First do this....
var view = CollectionViewSource.GetDefaultView(registrants);
then use view in place of registrants in your code.

How do I obtain selected rows in a DataGridView from different pages

I have a windows forms DataGridView, where I have data and a checkbox for each row.
I will select check box for a particular row and all the selected rows will be populated in another page.
if (grdEmp.Rows.Count > 0)
{
var selectedEmpIDs= from DataGridViewRow coll in grdEmp.Rows
where Convert.ToBoolean(coll.Cells["Select"].Value) == true
select coll;
if (selectedEmpIDs.Count() > 0)
{
foreach (DataGridViewRow row in selectedEmpIDs)
{
selectedEmp+= row.Cells["EmpId"].Value + ",";
}
}
}
This works good only for one page.
When I navigate to another page, and click the selected rows, the previous one goes off.
How do I resolve it.
Thanks
cmrhema
Note :Sorry for the confusion, When I meant it works good for a page, I meant paging.
I think I need to add more inputs,
There are 10 pages in the gridview.
I select the first record from each page of the gridview, one after another by clicking next page( Page next button).
But only the record that was selected the last is getting displayed and others and ignored off.
What could be the prblm
You can use a List or Dictionary or any other collection type globally, using Program.cs or using a static class. And store the selected rows into the list before you leave the page.
Rather than using a comma delimited string string for your list of ids you can instead use a List.
Your code will then become something like this:
if (grdEmp.Rows.Count > 0)
{
var selectedEmpIDs= from DataGridViewRow coll in grdEmp.Rows
where Convert.ToBoolean(coll.Cells["Select"].Value) == true s
select coll;
if (selectedEmpIDs.Count() > 0)
{
foreach (DataGridViewRow row in selectedEmpIDs)
{
if (!listOfIds.Contains((int)row.Cells["EmpId"].Value))
{
listOfIds.Add(((int)row.Cells["EmpId"].Value));
}
}
}
}
You will need methods to remove items from this list so adding event handlers for the checkbox selected event will probably work better.
The List object itself can simple live as a class level object of the form that containst your DataGridView.
This gets a little bit more complicated if you are managing your paging across forms, but the same principles of maintaining a list of selected ids applies.

Categories