I wish my program worked like this:
1.It takes data form MS SQL
2.Makes tree based on this data(data rows)
3.I have few textBoxex and I want to fill them with data based on clicked node.
I`ve made class for SQL connection:
class Baza
{
private SqlConnection connection;
string dbdir = "Data Source=CS24\\SQLEXPRESS;user id=sa;password=alamakota;database=SHARP;connection timeout=3";
public Baza()
{
connection = new SqlConnection();
connection.ConnectionString = dbdir;
connection.Open();
}
public DataTable get_data(string q)
{
DataTable dt = new DataTable();
SqlDataReader dr ;
SqlCommand sqlc = new SqlCommand(q);
sqlc.Connection = this.connection;
dr = sqlc.ExecuteReader();
dt.Load(dr);
return dt;
}
}
And treeView creator:
private void Form1_Load(object sender, EventArgs e)
{
Baza baza = new Baza();
DataTable dt = new DataTable();
dt = baza.get_data("Select * from Users order by Id asc");
foreach (DataRow dr in dt.Rows)
{
TreeNode node = new TreeNode(dr["Name"].ToString() + " " + dr["Surname"].ToString());
treeView1.Nodes.Add(node);
}
}
I thought that node should have ID which I can use to make data filling but I dont really know how to do it. Can you help me with that?
I had the same trouble and i did this :
(I had hierarchical data in columns ParentID, IsGroup, OrderInGroup)
Create a class for the TreeViewItems
public class xTreeViewItem : INotifyPropertyChanged
{
public int id { get; set; }
public string Header { get; set; }
public int parent;
public List<xTreeViewItem > children { get; set; }
public bool isGroup = false;
public bool ParentResolved = false;
public float order {get;set;}
public void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Now make a list with the SQL Data
private void LoadTree()
{
ObservableCollection<xTreeViewItem> treeSource = new ObservableCollection<xTreeViewItem>();
List<xTreeViewItem> Buffer = new List<xTreeViewItem>();
foreach (DataRow row in table.Rows)
{
xTreeViewItem itm = new TreeViewItem();
// Load the required heirarchial data
Buffer.Add(itm);
}
for (int i = Buffer.Count - 1; i >= 0; i--)
{
if (Buffer[i].parentID != 0)
{
if (Buffer[i].ParentResolved)
Buffer.RemoveAt(i);
else
{
Buffer[i].ParentResolved = true;
while (Buffer.Any(c => (c.parentID == Buffer[i].menuID && !c.ParentResolved)))
{
Buffer[i].children.Add(FindChildren(Buffer[i]));
}
xTreeViewItem parent = Buffer.First(c => c.menuID == Buffer[i].parentID);
parent.children.Add(Buffer[i]);
Buffer[i].parent = parent;
Buffer.RemoveAt(i);
}
}
}
// remaining items acts as mainItems, so add it to the treeSource
foreach (xTreeViewItem x in Buffer)
{
treeSource.Add(x);
}
treeView.ItemsSource = treeSource;
}
xTreeViewItem FindChildren(xMenuItem x)
{
List<xTreeViewItem > subBuffer = Buffer.Where(c => (c.parentID == x.menuID && !c.ParentResolved)).ToList(); // if there is, get all of them to a list
subBuffer = subBuffer.OrderBy(c => c.order).ToList(); // << THIS IS CRAZY, BUT FIXES :P // order all of them by their 'order' property
int indx = Buffer.IndexOf(subBuffer[0]); // get the first item after ordering
Buffer[indx].ParentResolved = true;
Buffer[indx].parent = x; // changing parentresolved property to prevent infinte loops
while (Buffer.Any(c => (c.parentID == Buffer[indx].menuID && !c.ParentResolved))) // again checking if the current child got any other child.
{
Buffer[indx].children.Add(FindChildren(Buffer[indx])); // adding all the childs
}
return Buffer[indx]; // return the child
}
In XAML : put this as the TreeView's ItemTemplate :)
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding children}">
<ContentPresenter Content="{Binding Header}" Margin="2,0"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
This did the work for me :P
and to get the selected item, just use :
xTreeViewItem selectedItem = (xTreeViewItem)treeView.SelectedItem;
make your view update with this 'selectedItem'; .. :)
Hope it helped :)
** Sometimes the items may appear reversed. Simply give the List.Reverse(); in the point where it is so, i havent run the code myself, anyway this should work for all types, like loading MenuItems from Database and desplaying (useful if you have multiple languages) :)
** Im just 18 yrs old :P havent got a degree in CS, this is just a code that i came up with :) yea there might be flaws but it works for me :P
:)
Related
public partial class DataGrid_HBD : UserControl
{
public DataGrid_HBD()
{
InitializeComponent();
// 2 Seconds Timer before connecting to the Database.
// This improves UI rendering on button click
DataGrid_Data();
}
/// <summary>
/// Loading Data Grid
/// </summary>
public void DataGrid_Data()
{
// 2 second delay before loading DataGrid
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
timer.Start();
timer.Tick += (sender, args) =>
{
timer.Stop();
// Attempt to connect to SQL Server database and populate DataGrid with database tables.
try
{
string connectionString = ("Data Source=\\SQLEXPRESS;Initial Catalog=CustomerRelations;Integrated Security=True;");
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("SELECT [hb_Disputes].[DSP_ID], [hb_disputes].[ACCOUNT], [Users].[TX_EMPLOYEE], [hb_CsrNames].[NM_USER], [hb_disputes].[CUST_NAME],[hb_disputes].[PREM_ADDR], [hb_status].[Status], [hb_disputes].[OPENED], [hb_disputes].[DEADLINE], [hb_disputes].[DATERSLVD], [hb_rpttype].[ReportType], [hb_ratetype].[RateType], [hb_Disputes].[FR_DT_FIRSTREV], [hb_Disputes].[FR_TS_LATESTUPD], [hb_Disputes].[COMMENT], [hb_Disputes].[FR_DSP_CLSF], [hb_Disputes].[FR_CUST_CNTCT], [hb_Disputes].[FR_WRK_REQ], [hb_Disputes].[FR_OPN_ERR], [hb_Disputes].[FR_SO_TP], [hb_Disputes].[FR_SO_DTLS], [hb_Disputes].[FR_SO_DT_WNTD], [hb_Disputes].[FR_SO_ISSD_BY], [hb_Disputes].[FR_CMMNT] FROM [hb_disputes]" +
" LEFT JOIN [Users] ON [hb_disputes].[ASSGNTO] = [Users].[KY_USER_ID] LEFT JOIN [hb_CsrNames] ON [hb_disputes].[WFMUSER] = [hb_CsrNames].[KY_USER_ID] LEFT JOIN [hb_status] ON [hb_disputes].[STATUS] = [hb_status].[STSID] LEFT JOIN [hb_rpttype] ON [hb_disputes].[RPTTYPE] = [hb_rpttype].[RPTID] LEFT JOIN [hb_ratetype] ON [hb_disputes].[REV_CLS] = [hb_ratetype].[RTID]", connection);
connection.Open();
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
connection.Close();
dtGrid.DataContext = dt;
}
catch
{
MessageBox.Show("Database connection is not available at this time. Please contact your database administrator ");
}
};
}
private void dtGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// User double clicks on DataGrid Row
// Open new Window
// Populate selected textboxes with selected datarow
DataGrid gd = (DataGrid)sender;
DataRowView row_selected = gd.SelectedItem as DataRowView;
var windowToOpen = new Window1();
if(gd !=null )
{
// Textboxes
windowToOpen.txt_RowRecrd.Text = row_selected["DSP_ID"].ToString();
windowToOpen.txt_acctnumber.Text = row_selected["ACCOUNT"].ToString();
windowToOpen.txt_analyst.Text = row_selected["TX_EMPLOYEE"].ToString();
windowToOpen.txt_custname.Text = row_selected["CUST_NAME"].ToString();
windowToOpen.txt_address.Text = row_selected["PREM_ADDR"].ToString();
windowToOpen.txt_Status.Text = row_selected["Status"].ToString();
windowToOpen.txt_opened.Text = row_selected["OPENED"].ToString();
windowToOpen.txt_deadline.Text = row_selected["DEADLINE"].ToString();
windowToOpen.txt_DateResolved.Text = row_selected["DATERSLVD"].ToString();
windowToOpen.txt_revcls.Text = row_selected["RateType"].ToString();
windowToOpen.txt_WFMissuedBy.Text = row_selected["NM_USER"].ToString();
windowToOpen.txt_firstreview.Text = row_selected["FR_DT_FIRSTREV"].ToString();
windowToOpen.txt_Latestupdate.Text = row_selected["FR_TS_LATESTUPD"].ToString();
windowToOpen.txt_reviewNotes.Text = row_selected["FR_CMMNT"].ToString();
windowToOpen.txt_ResolutionNotes.Text = row_selected["COMMENT"].ToString();
// Comboboxes
windowToOpen.cmb_UtilityRptTyp.SelectedItem = row_selected["ReportType"].ToString();
windowToOpen.Show();
}
else
{
return;
}
}
When the user double clicks on a row in the Datagrid, it opens a new window and populates the textboxes with the selected cells. However, it does not work for CombinationBoxes. I attached an image of the new window (Window1) that the information is populating to. The image shows the code behind for the combobox with the populated table from the SQL Server database.
First of all, You must set Datasource of cmb_UtilityRptTyp with a list of available report types like this:
// Define ReportType Class
class ReportType {
public int ID { get; set; }
public string Title { get; set; }
public ReportType(int id, string title)
{
ID = id;
Title = title;
}
}
Then set DataSource in first line of dtGrid_MouseDoubleClick:
ReportType[] list = new ReportType[] {
new ReportType(1, "Type 1"),
new ReportType(2, "Type 2"),
};
windowToOpen.cmb_UtilityRptTyp.DataSource = list;
windowToOpen.cmb_UtilityRptTyp.DisplayMember = "Title";
windowToOpen.cmb_UtilityRptTyp.ValueMember = "ID";
After that you must use the SelectedText instead of Text in ComboBox, like this:
windowToOpen.cmb_UtilityRptTyp.SelectedText = row_selected["RPTTYPE"].ToString();
Also, You can use SelectedIndex, to find item index you could IndexOf like this
string rptType = row_selected["RPTTYPE"].ToString();
int index = windowToOpen.cmb_UtilityRptTyp.Items.IndexOf(rptType );
windowToOpen.cmb_UtilityRptTyp.SelectedIndex = index;
Or use FindStringExact
string rptType = row_selected["RPTTYPE"].ToString();
int index = windowToOpen.cmb_UtilityRptTyp.FindStringExact(rptType );
windowToOpen.cmb_UtilityRptTyp.SelectedIndex = index;
I've searched all over here and Google and still can't find an answer to this. I'm playing around with Amazon's API and am making a simple Windows Form to try and display the data in a DataGridView. The GridView is generating 10 rows for the 10 results I am getting, but is not filling the rows with the actual data. They're just blank.
The code below is a method (GetResults) that return a DataTable. I didn't show all of it because there is a bunch of code above to get the data.
DataTable dt = new DataTable();
dt.Columns.Add("ASIN", typeof(string));
dt.Columns.Add("Title", typeof(string));
// write out the results
foreach (var item in response.Items[0].Item)
{
Product product = new Product(item.ASIN, item.ItemAttributes.Title);
Console.WriteLine(product.ASIN);
var dr = dt.NewRow();
dr["ASIN"] = product.ASIN;
dr["Title"] = product.Title;
dt.Rows.Add();
}
return dt;
}
private void btnSearch_Click(object sender, EventArgs e)
{
dgvProducts.AutoGenerateColumns = false;
dgvProducts.DataSource = GetReults();
}
I know it is getting the info because I am writing it to console and it is showing up correctly.
I also have this basic class for the Product:
public class Product
{
private string asin;
private string title;
public Product() { }
public Product(string newAsin, string newTitle)
{
this.asin = newAsin;
this.title = newTitle;
}
public string ASIN
{
get { return asin; }
set { asin = value; }
}
public string Title
{
get { return title; }
set { title = value; }
}
I've tried setting AutoGenerateColumns = false and setting the column data bindings myself, but that didn't do anything either.
You're adding an empty row to the table, not adding your newly created row.
Change
dt.Rows.Add();
To
dt.Rows.Add(dr);
I have a TreeView and on a separate panel 3 checkboxes.
The Treeview data is being pulled from a database,this database has two tables Product and Model inside the Model table there are 3 columns - twoDoor, threeDoor and fiveDoor.
My TreeView looks like this;
CAR(PRODUCT)
->FORD(MODEL)
->BMW(MODEL)
->VW(MODEL)
On a separate panel there are 3 checkboxes;
Two Door
Three Door
Five Door
The state of these checkboxes are already defined in the Model db.
How can i display their state against whatever child Node(Model) is picked in the TreeView?
My Code:
private void Form1_Options_Load(object sender, EventArgs e)
{
String connectionString;
connectionString = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
conn = new SqlConnection(connectionString);
String Sequel = "SELECT id,ProductName,ModelType FROM Product";
SqlDataAdapter da = new SqlDataAdapter(Sequel, conn);
DataTable dt = new DataTable();
conn.Open();
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
parentNode = treeView1.Nodes.Add(dr["ProductName"].ToString());
//treeView1.Nodes.Add(dr["ModelType"].ToString());
PopulateTreeView(Convert.ToInt32(dr["Id"].ToString()), parentNode);
}
}
public void PopulateTreeView(int parentId, TreeNode parentNode)
{
String Seqchildc = "Select * From Model WHERE ProductId = "+ parentId + "";
SqlDataAdapter dachildmnuc = new SqlDataAdapter(Seqchildc, conn);
DataTable dtchildc = new DataTable();
dachildmnuc.Fill(dtchildc);
TreeNode childNode;
foreach (DataRow dr in dtchildc.Rows)
{
if (parentNode == null)
childNode = treeView1.Nodes.Add(dr["ModelName"].ToString());
//childNode.Nodes.Add(dr["ModelType"].ToString());
else
childNode = parentNode.Nodes.Add(dr["ModelName"].ToString());
//PopulateTreeView(Convert.ToInt32(dr["Id"].ToString()), childNode);
}
treeView1.ExpandAll();
//Connect();
}
Here is what you can do if I understand correctly the question.
First, create a class to hold your Model information:
class ModelInfo
{
public string Name { get; set; }
public bool TwoDoor { get; set; }
public bool ThreeDoor { get; set; }
public bool FiveDoor { get; set; }
}
Second, modify your PopulateTreeView method as follows:
public void PopulateTreeView(int parentId, TreeNode parentNode)
{
// ...
foreach (DataRow dr in dtchildc.Rows)
{
// Populate model info from the data
var modelInfo = new ModelInfo { Name = dr["ModelName"].ToString() };
//modelInfo.TwoDoor = dr[...];
//modelInfo.ThreeDoor = dr[...];
//modelInfo.FiveDoor = dr[...];
// Create and add a new node
var childNode = (parentNode == null ? treeView1.Nodes : parentNode.Nodes).Add(modelInfo.Name);
// Associate info with the node
childNode.Tag = modelInfo;
}
// ...
}
The essential part is to store the ModelInfo inside the Node.Tag property so it can be retrieved later.
Finally, subscribe to the TreeView.AfterSelect event and put inside the event handler something like this:
// Get the model info from the selected node
var modelInfo = e.Node != null ? e.Node.Tag as ModelInfo : null;
// Update the checkboxes accordingly
chkTwoDoor.Checked = modelInfo != null && modelInfo.TwoDoor;
chkThreeDoor.Checked = modelInfo != null && modelInfo.ThreeDoor;
chkFiveDoor.Checked = modelInfo != null && modelInfo.FiveDoor;
for the two-state checkbox style, or alternatively
chkTwoDoor.CheckState = modelInfo == null ? CheckState.Indeterminate : modelInfo.TwoDoor ? CheckState.Checked : CheckState.Unchecked;
chkThreeDoor.CheckState = modelInfo == null ? CheckState.Indeterminate : modelInfo.ThreeDoor ? CheckState.Checked : CheckState.Unchecked;
chkFiveDoor.CheckState = modelInfo == null ? CheckState.Indeterminate : modelInfo.FiveDoor ? CheckState.Checked : CheckState.Unchecked;
for the tri-state checkbox style.
I get data from tow database, db1,db2, then Now I want to create treeView for the data in the resulting datagridview; in datagrid view there are: id, name, director,the first record is the prim director, that mean he has not up director(he is owner), each record has no other record or has more records(child), and each child has grandchild and so on, this scenario Just Like in the this page:
I want to create treeview (parent and child and grandchild and so on), depending on xml file
when i used this snippet after some :
void setTree()
{
{
foreach(DataGridViewRow dt in DataGridView1.Rows)
{
var per = this.DataGridView1.Rows.Cast<DataGridViewRow>().Select(n => new person
{
name = dt.Cells[0].Value.ToString(),
Sex = dt.Cells[1].Value.ToString(),
Status = dt.Cells[2].Value.ToString(),
child = dt.Cells[3].Value.ToString(),
id = dt.Cells[4].Value.ToString(),
father = dt.Cells[5].Value.ToString()
}).ToList();
var rootTreeNode = GetTree(per, "").First();.........(1)
treeView1.Nodes.Add(rootTreeNode);
}
}
}
private TreeNode[] GetTree(List<person> per, string parent)
{
return per.Where(p => p.father == parent).Select(p =>
{
var node = new TreeNode(p.name);
node.Tag = p.id;
node.Nodes.AddRange(GetTree(per, p.id));
return node;
}).ToArray();
}
Now, when I use this code, I get error at mark(1),it say:Additional information: Sequence contains no elements.
thank you
after several readings in internet and attempts to solve this small problem, I successed finally.
this is the solution:
{
.............
TreeNode tn = new TreeNode(this.DataGridView2.Rows[0].Cells[0].Value.ToString());//text
tn.Tag = this.DataGridView2.Rows[0].Cells[4].Value.ToString();// id
tn.Name = this.DataGridView2.Rows[0].Cells[5].Value.ToString();//directorid
treeView1.Nodes.Add(tn);
settree(tn);
}
public void settree(TreeNode ns)
{
foreach (DataGridViewRow dr in DataGridView2.Rows)
{
if (dr.Cells[5].Value.ToString() == ns.Tag.ToString())
{
TreeNode tsn = new TreeNode(dr.Cells[0].Value.ToString());
tsn.Tag = dr.Cells[4].Value.ToString();
tsn.Name = dr.Cells[5].Value.ToString();
ns.Nodes.Add(tsn);
settree(tsn);
}
}
}
i will be happy if you benefit from this code.
I'm hoping someone can help. A long time windows forms/aspx user, moving to WPF.
Not expecting a coded answer to this, but any pointers on a different way to approach would be greatly appreciated - I am probably approaching this in a very backward way.
So the objective is to have an ObservableCollection with sub ObservableCollection "childen" within to then bind to my WPF treeview control.
I can bind my collection to the treeview without issues, and have styled it with checkboxes images as desired, frustratingly, its the ObservableCollection with children of children of children I am having trouble generating in the first place.
I have a table in SQL with LDAP Paths, and various other information I'm storing against that LDAP path, which I read into my ObservableCollection.
Single level, no problem, the bit I'm struggling with is sorted the sub objects of sub objects by LDAP Path, so when I bind to the treeview is presented as AD OU's are structured.
EG:
TopOU
Users
Front Office Users
Helpdesk Users
Example LDAP Paths in my DB
LDAP://OU=Front Office Users,OU=Users,OU=TopOU,DC=dev,DC=local
LDAP://OU=Helpdesk Users,OU=Users,OU=TopOU,DC=dev,DC=local
LDAP://OU=OU=Users,OU=TopOU,DC=dev,DC=local
LDAP://OU=OU=TopOU,DC=dev,DC=local
private ObservableCollection<AssignmentData> OUTreeAssignmentsCollection = new ObservableCollection<AssignmentData>();
public class AssignmentData : INotifyPropertyChanged
{
public Int32 AssignmentID { get; set; }
public String AssignmentName { get; set; }
public AssignmentTypes AssignmentType { get; set; }
//other stuff....
//For TreeView all sub nodes
public ObservableCollection<AssignmentData> Children { get; set; }
}
I then start to read from my db in a rather nasty way, and this is where it all goes wrong, and I could use some pointers.
cmd = new SqlCommand("SELECT UserGroups.UserGroupID, UserGroups.Name, UserGroups.LDAPPath FROM UserGroups WHERE UserGroups.TypeID=1", DBCon);
reader = cmd.ExecuteReader();
while (reader.Read())
{
String strLDAPHierarchical = GetLDAPHierarchical(reader[2].ToString());
AssignmentData newItem = new AssignmentData()
{
AssignmentID = Convert.ToInt32(reader[0]),
AssignmentName = reader[1].ToString(),
AssignmentImage = ouIcon,
AssignmentLDAPPath = reader[2].ToString(),
AssignmentCNPath = GetCNFromLDAPPath(reader[2].ToString()),
AssignmentTooltip = GetADSLocationTooltip(reader[2].ToString()),
AssignmentType = AssignmentTypes.UserOU,
AssignmentLDAPHierarchical = strLDAPHierarchical
};
if (strLDAPHierarchical.Contains(","))
{
//Now check all the root nodes exist to continue
String strLDAPHierarchicalCheckPath = strLDAPHierarchical;
String[] SplitLDAPHierarchical = strLDAPHierarchical.Split(new Char[] { ',' });
Int32 reverseI = SplitLDAPHierarchical.Length - 1;
String prevPath = "";
for (int i = 0; i < SplitLDAPHierarchical.Length; i++)
{
String path = SplitLDAPHierarchical[reverseI];
//now check if this node is already there and if not look it up and create it
if (path != "")
{
if (i == 0) { strLDAPHierarchicalCheckPath = path; }
else { strLDAPHierarchicalCheckPath = path + "," + prevPath; }
WriteLog("CHECK:" + strLDAPHierarchicalCheckPath);
LookupItemByLDAPHierarchical(strLDAPHierarchicalCheckPath, newItem);
if (i == 0) { prevPath = path; }
else { prevPath = path + "," + prevPath; }
reverseI = reverseI - 1;
}
}
}
else
{
//is top level object, so create at the root of the collection
UserOUCollection.Add(newItem);
}
Function to add sub items :-/
internal AssignmentData LookupItemByLDAPHierarchical(String strLDAPHierarchical, AssignmentData fromItem)
{
AssignmentData currentItem = null;
foreach (AssignmentData d in UserOUCollection)
{
if (d.AssignmentLDAPHierarchical == strLDAPHierarchical) { currentItem = d; break; }
if (d.Children != null)
{
currentItem = CheckChildNodesByLDAPHierarchical(d, strLDAPHierarchical);
if (currentItem != null) { break; }
}
}
String strMessage = "null";
if (currentItem != null) { strMessage = currentItem.AssignmentLDAPPath; }
if (currentItem == null)
{
String strWhere = "LDAPPath LIKE 'LDAP://" + strLDAPHierarchical + "%'";
SqlConnection DBCon = new SqlConnection(SQLString);
DBCon.Open();
SqlCommand cmd = new SqlCommand("SELECT UserGroupID, Name, LDAPPath FROM UserGroups WHERE " + strWhere + " AND TypeID=1", DBCon);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
strLDAPHierarchical = GetLDAPHierarchical(reader[2].ToString());
AssignmentData newItem = new AssignmentData()
{
AssignmentID = Convert.ToInt32(reader[0]),
AssignmentName = reader[1].ToString(),
AssignmentImage = ouIcon,
AssignmentLDAPPath = reader[2].ToString(),
AssignmentCNPath = GetCNFromLDAPPath(reader[2].ToString()),
AssignmentTooltip = GetADSLocationTooltip(reader[2].ToString()),
AssignmentType = AssignmentTypes.UserOU,
AssignmentLDAPHierarchical = strLDAPHierarchical
};
String strLDAPHierarchicalCheckPath = strLDAPHierarchical;
foreach (String path in strLDAPHierarchical.Split(new Char[] { ',' }))
{
//now check if this node is already there and if not look it up and create it
if (path != "")
{
strLDAPHierarchicalCheckPath = strLDAPHierarchicalCheckPath.Replace(path + ",", "");
currentItem = LookupItemByLDAPHierarchical(strLDAPHierarchicalCheckPath, currentItem);
if (null == currentItem)
{
UserOUCollection.Add(newItem); //new root item
}
else
{
if (currentItem.Children == null)
{
//add new child
currentItem.Children = new ObservableCollection<AssignmentData> { newItem };
}
else
{
//add more children to exisiting
currentItem.Children.Add(newItem);
}
}
currentItem = null;
}
}
//Find a current Item to add the node to
//currentItem = LookupItemByLDAPHierarchical(strLDAPHierarchical);
}
reader.Close();
reader.Dispose();
DBCon.Close();
DBCon.Dispose();
}
return currentItem;
}
With my current solution, I get a treeview, with sub nodes of sub nodes, but they are wrong/lots of duplication etc. I have spent literally days trying to fix my probably overcomplicated attempt above - but have come to the conclusion I'm probably going about it the wrong way.
Any help greatly appreciated!
Just having a peruse ;) through your code. Think I can see why you have lots of duplications. Looks like your first SQL query get's all parent/child records. Then the second query will go and get some of those records again, if that makes sense.
One approach would be to only get the top level items in your first query. Possibly by getting SQL to count the number of commas.
SELECT UserGroups.UserGroupID, UserGroups.Name, UserGroups.LDAPPath,
LENGTH(LDAPPath) - LENGTH(REPLACE(LDAPPath, ',', '')) as CommaCount
FROM UserGroups
WHERE UserGroups.TypeID=1
AND CommaCount = 2
Since you asked for different approach id say it's not very efficient to repeatedly query the database in a loop. When I'm building a tree of parent child objects I'd normally get all parent/child records in one query. Build a flat dictionary of all the objects. Then loop through it and make the parent/child associations.
The dictionary can also be useful to lookup your objects later on either directly by key or to loop through without having to make a recursive function that crawls the tree.
So I'd suggest that you break it down into 2 blocks of code.
First block: Using your existing query that get's all of the items, create a flat Dictionary with everything in.
They key of each item should probably be the result from GetLDAPHierarchical().
Second block: Next loop through the dictionary and create the hierarchy. Add anything with no parent directly to the UserOUCollection
foreach(AssignmentData d in myDictionary.Values)
{
String parentKey = GetParentLDAPKey(d.AssignmentLDAPHierarchical);
if (myDictionary.ContainsKey(parentKey))
{
myDictionary(parentKey).children.Add(d);
}
else
{
UserOUCollection.Add(d);
}
}
GetParentLDAPKey() will need to produce the same key as it's parent by removing the first part of the LDAP Path.
Hope that points you in the right direction.
H
(SMASH)
Thanks so much to hman, who pointed me in a much more logical direction. I used LDAPPath as my dictionary key.
Dictionary<String, AssignmentData> OUDictionary = new Dictionary<String, AssignmentData>();
//Read from DB
cmd = new SqlCommand("SELECT UserGroups.UserGroupID, UserGroups.Name, UserGroups.LDAPPath FROM UserGroups WHERE UserGroups.TypeID=1", DBCon);
reader = cmd.ExecuteReader();
while (reader.Read())
{
AssignmentData newItem = new AssignmentData()
{
AssignmentID = Convert.ToInt32(reader[0]),
AssignmentName = reader[1].ToString(),
AssignmentImage = ouIcon,
AssignmentLDAPPath = reader[2].ToString(),
AssignmentCNPath = GetCNFromLDAPPath(reader[2].ToString()),
AssignmentTooltip = GetADSLocationTooltip(reader[2].ToString()),
AssignmentType = AssignmentTypes.UserOU,
};
UserOUDictionary.Add(reader[2].ToString(), newItem);
}
reader.Close();
reader.Dispose();
//Now Read OU List into TreeView Collection
foreach (AssignmentData d in UserOUDictionary.Values)
{
String parentKey = GetParentLDAPPath(d.AssignmentLDAPPath);
if (UserOUDictionary.ContainsKey(parentKey))
{
AssignmentData parentItem = UserOUDictionary[parentKey];
if (parentItem.Children == null) { parentItem.Children = new ObservableCollection<AssignmentData> { d }; } //add first child
else { parentItem.Children.Add(d); } //add more children to exisiting
}
else
{
UserOUCollection.Add(d); //add to root of control
}
}
private String GetParentLDAPKey(String strLDAPPath)
{
String retParentKey = strLDAPPath;
if (strLDAPPath.Contains(","))
{
retParentKey = retParentKey.Replace("LDAP://", "");
retParentKey = retParentKey.Remove(0, retParentKey.IndexOf(",") + 1);
retParentKey = "LDAP://" + retParentKey;
}
return retParentKey;
}