How do I display non-normalized data in a hierarchical structure? - c#

My issue is that I want to display data in a hierarchal structure as so:
Democrat
County Clerk
Candidate 1
Candidate 2
Magistrate
Candidate 1
Candidate 2
Candidate 3
But I'm retrieving the dataset like this:
Party | Office | Candidate
--------------------------------------------
Democrat | County Clerk | Candidate 1
Democrat | County Clerk | Candidate 2
Democrat | Magistrate | Candidate 1
Democrat | Magistrate | Candidate 2
Democrat | Magistrate | Candidate 3
I planned on using nested repeaters, but I need a distinct value of Party, and then distinct values of office name within that party in order to do it.
Are there any .NET functions to easily do what I'm attempting to? Would there be a better way of displaying the information other than repeaters?
Thanks in advance!

If you can modify the stored procedure, I would recommend a result set in the form of an adjacency list:
ID Name ParentID
1 Democrat NULL
2 County Clerk 1
3 Magistrate 1
4 Candidate 1 2
5 Candidate 2 2
6 Candidate 1 3
7 Candidate 2 3
8 Candidate 3 3
It reduces the amount of data retrieved, and allows you to program recursively against the parent-child relationships. You simply start at the root. This approach avoids the hassle of using nested Repeaters by directly building the string literal in a StringBuilder.
StringBuilder sb = new StringBuilder();
DataTable dt = new DataTable();
someDataAdapter.Fill(dt);
// find the roots:
DataRow[] roots = dt.Select("parentId is null");
sb.Append("<ul>");
foreach (DataRow child in roots)
{
WriteNode(child, sb);
}
sb.Append("</ul>");
// recursively write out each node
public static void WriteNode(DataRow row, StringBuilder sb) {
sb.Append("<li>");
sb.Append(row["Name"]);
// find children rows...
DataRow[] children = row.Table.Select("parentId = " + row["id"].ToString());
if (children.Length > 0)
{
sb.Append("<ul>");
foreach (DataRow child in children)
{
WriteNode(child, sb);
}
sb.Append("</ul>");
}
sb.Append("</li>");
}

I am not sure if you would be interested to use a third party control, but I had EXACTLY SAME requirement of yours and needed to display the data hierarchically, showing repeating values as single one with collapsible nodes (like trees). However, Tree controls are not data bound and still needs a fair bit of coding. But this style of Tree building based on non-normalized data can be easily done with DataBinding. The FlexGrid I have used was DataBound to the DataTable and just needed a few lines of code to display EXACTLY what you require, albeit based on a Third Party Control.
I have used the ComponentOne FlexGrid control and used its 'SubTotal' feature to generate the hierarchical data. I am pasting the code below, just in case you are interested to use the ComponentOne FlexGrid. You can download a Demo copy and check.
// Showing master policy in GROUPS
// -----------------------------------------------------------------------------------
// set tree mode to show settings in GROUPS
flxAvailableSettings.Tree.Style = TreeStyleFlags.Simple;
// show outline tree on column 1.
flxAvailableSettings.Tree.Column = 0;
flxAvailableSettings.Cols[0].Visible = true;
flxAvailableSettings.Cols[0].Width = 15;
flxAvailableSettings.AllowMerging = AllowMergingEnum.Nodes;
// subtotal on column 1, outline level 0
flxAvailableSettings.Subtotal(AggregateEnum.None, 0, 0, 0, "{0}");
// use owner draw to suppress repeating group names in Non-Node rows
flxAvailableSettings.DrawMode = DrawModeEnum.OwnerDraw;
flxAvailableSettings.OwnerDrawCell += new C1.Win.C1FlexGrid.OwnerDrawCellEventHandler(flxAvailableSettings_OwnerDrawCell);
// done, autosize columns to finish
flxAvailableSettings.AutoSizeCols();
// -----------------------------------------------------------------------------------
private void flxAvailableSettings_OwnerDrawCell(object sender, C1.Win.C1FlexGrid.OwnerDrawCellEventArgs e)
{
if (!flxAvailableSettings.Rows[e.Row].IsNode && flxAvailableSettings.Cols[e.Col].Name == "PolicyGroup")
e.Text = "";
}
The flxAvailableSettings.Subtotal(AggregateEnum.None, 0, 0, 0, "{0}") line generates the Tree Groups, though I needed to group on a single column, you can use groups on multiple columns. Just refer their docs example on Subtotalling.
Hope this helps.

A TreeView control would give you the format you want. However populating it from the database takes some effort. There is an article here about populating asp.net TreeViews from a database:
http://www.codeproject.com/KB/tree/TreeViewWithDatabase.aspx

Related

Fastest way to add to a list elements not in second list

I have 2 listBoxes with BindingLists as their data sources. The idea is to make a List builder (as MSDN names it), where first listBox shows currently added columns and second listBox shows the rest of available columns. First list contains ViewColumn objects, while the other list contains strings.
I load chosen columns into first listBox from database and then I want to load the rest of available columns into the second listBox (the list itself comes from another place in database). Considering there are no limits on the number of columns, I want to do that in the fastest way possible - what would that be?
Here's some code to visualize that:
ViewTable _view;
BindingList<ViewColumn> _viewColumns = new BindingList<ViewColumn>();
BindingList<string> _detailsColumns = new BindingList<string>();
void CustomInitialize()
{
_view = //get view and its columns
_viewColumns = new BindingList<ViewColumn>(_view.Columns);
listBox_CurrentColumns.DataSource = _viewColumns;
listBox_CurrentColumns.DisplayMember = "Name";
var detailsTable = //get the list of available columns
foreach (var row in detailsTable)
{
//TODO: if _viewColumns does not contain this value, add it to _detailsColumns
_detailsColumns.Add(row.ColumnName);
}
listBox_AvailableColumns.DataSource = _detailsColumns;
}
I think you want to do something like:
_detailsColumns = _allColumns.Except(_viewColumns.Select(c => c.Name))
This should get you all entries in the _allColumns collection excluding the entries in the _viewColumns collection.
I assume here that _allColumns contains the overall collection of possible columns.

Concise way to format columns in a gridview?

I realize this is an easy question, but despite searching I can't find anything specific towards my problem.
I have a gridview populated with 9 or so columns. I want to both change the column names and edit the number of visible columns. So instead of
| x | y | z |
2 6 7
I'd like
|new x|new z|
2 7
I realize that I can manually edit the column names and set them to visible or not, but is there a way to do something like: if (column = y) then (display column) and (column name = new y)?
Much appreciated.
You can handle the GridView.RowDataBound event to modify the columns when they are bound and apply any changes you want at that point.
you could do something like this inside the page load event, or grid load:
foreach(BoundField b in grid.Columns)
{
if(b.HeaderText == 'y')
{
b.HeaderText = "new y";
} else {
b.Visible = false;
}
}

Error with selected item from the ListBox property (C#)?

for (int counter = 0; counter < countSelected; counter++)
{
string groupName = txt_GroupName.Text;
//Get GroupID from the created group
string GroupIDQueryText = "SELECT GroupID FROM tbl_group WHERE GroupName ";
int groupID = Convert.ToInt32(server.performQuery(GroupIDQueryText, groupName, MySqlDbType.VarChar));
//To get User ID
string firstName = ListBoxMembers.SelectedItems[counter].Value;
}
This isn't returning the selected value, but instead returns the 1st person in the list even if I haven't selected it. Where am I going wrong?
System.Web.UI.WebControls does not contain defenition for listboxmembers.selectedItems error
You are using .Items which is the collection of all items in the ListBox. I think you intend to use .SelectedItems (documentation on MSDN).
// When counter = 0, this is the very first item in the listbox
ListBoxMembers.Items[counter].Value;
// When counter = 0, this is the first of the selected items in the listbox
ListBoxMembers.SelectedItems[counter].Value;
EDIT Web ListBox controls are different than WinForms ListBox controls, so knowing that context is very valuable. Here's an article from MSDN on how to determine the selected items in a multi-selection list control (scroll down to the multi-selection section). The idea is to loop through all .Items and check the .Selected property on each.
I think you should use ListBox.SelectedValue("Somval"); to set selected Value
It's quite simple:
If you have multiple listbox members, like
# | Name | Selected
0 | a | false
1 | b | false
2 | c | true
3 | d | true
then your for loop with counter = 0 selects the entry (0,a,false) and not (2,c,true) at
string firstName = ListBoxMembers.Items[counter].Value;
You have to map the value of the variable counter to the counter+1-th selected item or use SelectedItems, as mentioned by David.

Binding DataGridViewCheckBoxColumn to array of booleans

I'm currently trying to build a DataGridView in a Windows Form to display to a user a list of settings that they can turn on and off. The DatGridView will have two columns, the first column will describe the setting and the second column will containg a check box allowing the user to turn the setting on or off. So for example the DataGridView would look like:
| Descriptions | Set |
---------------------------------
| Description 1 | true |
| Description 2 | false |
| Description 3 | false |
...
In my project settings I currently have a list of booleans, Pref1, Pref2, Pref3 etc which I'd like to bind to the CheckBoxes within the DataGridView so that they can be manipulated automatically without me having to do any manual checks whenever a cells value has been edited and so I can save the changes between different instances of the application.
I tried searching for a few solutions and came across the following and added it to the forms constructor:
// Build preference dictionary
Dictionary<String, bool> Preferences = new Dictionary<String, bool>();
preferences.Add("Description 1", Settings.Default.Pref1);
preferences.Add("Description 2", Settings.Default.Pref2);
....
// Copy dictionary to list
List<KeyValuePair<String, bool>> PreferenceList = new List<KeyValuePair<String, bool>>();
foreach (KeyValuePair<String, bool> item in Preferences)
PreferenceList.Add(item);
// Set the GridView DataSource and values displayed in each column
GridView.AutoGenerateColuns = false;
GridView.DataSource = new BindingList<KeyValuePair<String, bool>>(PreferenceList);
GridView.Columns[0].DataPropertyName = "Key";
GridView.Columns[1].DataPropertyName = "Value";
When the form loads the DataGridView populates as expected but the CheckBoxes in the second column can't be manipulated. After some debugging I've notcied the second column becomes ReadOnly when I set the DataPropertyName to "Value" and I can't change this ReadOnly setting without throwing an exception.
Is there any way around this readonly issue? I've also read and thought about creating my own Preference class something along the lines of:
public Class Preference
{
public String Description { get, set }
public bool Selected { get, set }
}
And then creating an array of these preferences, binding each Preference's 'Selected' property to one of the booleans in my Settings class and then setting the Preference array as the DataGridViews DataSource. Is this a viable solution or is there maybe another alternative that I'm not considering/aware of?
Sorry for the essay of a question but I just like to try and explain everything so theres no confusion in what I'm asking :)
Thanks in advance.
GridView.DataSource = Preferences
.Select(p => new Preference {Description = p.Key, Selected = p.Value})
.ToList();
GridView.Columns[0].DataPropertyName = "Description";
GridView.Columns[1].DataPropertyName = "Selected";

Representing hierarchical data .net winforms

How i can represent the following hierarchical data ?
What control should be used , if possible example will help.
-node
-----node1 - -data--data --data
-------------node111 -- data -- data
-------------node112 -- data -- data
-------------node113 -- data -- data
-----node2 - -data--data --data
-------------node1121 -- data -- data
-----node3 - -data--data --data
and if possible i need to put in most of the cells several icons .
I have found this tutorial Link , can someone support me more information ?
Is that possible if yes how ?
Thanks a lot .
The built-in Windows Forms controls aren't great for this. What you're actually looking for is a tree-grid hybrid control (AKA multi-column tree view or TreeList).
DevExpress has an XtraTreeList, which is what I use (not free), and is probably the closest to what you're asking for. Telerik's GridView can also display grid data in a hierarchical fashion if you set up the groupings right.
If those prices are too steep, you might try FlexibleTreeView. Or if you desperately need something free, check out this CodeProject page: Advanced TreeView for .NET. It's going to be a lot more quirky and difficult to use than the commercial products, but it will do the job.
Note that I'm assuming that the data is uniform, that you basically want to display the same data for every node in the hierarchy. If the data is heterogeneous (completely different columns depending on the type of node or level), what you actually want to use is a hierarchical GridView. You can get those from the same publishers listed above. I'm not aware of any half-decent free version.
Use a TreeView Control
One way might be to create a derived TreeNode object, force it to contain a List<data> :
// requires declaration of : using System.Windows.Forms;
// sample data class
public class data
{
public string Name;
public int ID;
}
public class XTreeNode : TreeNode
{
List<data> theData = new List<data>();
public XTreeNode(string theNodeID)
{
this.Text = theNodeID;
}
public void addData(data newData)
{
theData.Add(newData);
}
}
Here's a (not elegant) sample of what building an instance of the above data structure might look like (on a WinForm) : assume you have a TreeView, named 'treeView1 on the Form :
XTreeNode currentNode;
data currentData;
for (int i = 0; i < 10; i++)
{
// create the node and add it to the 'treeView1
currentNode = new XTreeNode(i.ToString());
treeView1.Nodes.Add(currentNode);
// add some data entries to the List<data> of the derived TreeNode
currentData = new data {Name = "one", ID = 100};
currentNode.addData(currentData);
currentData = new data { Name = "two", ID = 200 };
currentNode.addData(currentData);
currentData = new data { Name = "three", ID = 300 };
currentNode.addData(currentData);
// sample of adding a child node
currentNode.Nodes.Add(new XTreeNode((i * 100).ToString()));
}
For the question of how you woud visually display the List<data> associated with each Node : the usual way would be to combine the Treeview with a ListView, and synchronize their locations and item heights : then display the List<data> on the same "row" as the corresponding TreeNode.
Of course you can implement your own Node and NodeCollection entities which are completely independent of any control : this example presents a mixed-case of relying on a .NET control to serve as both data structure and presentation mechanism.
There's an excellent example of a combination TreeView/ListView on CodeProject that has been maintained, updated, and extended, for years : Phillip Piper's : "A Much Easier to Use ListView", first published 2006, last update October, 2009 : its functionality is so rich that if compares favorably, imho, with commercial components.

Categories

Resources