sachiek
asked on
Master child records in one table , treeview struct, deletion algorithm
Hi All,
I am looking forward for some good algorithm for one of my requirement.
I got a table which will be displayed in frontend in treeview.
So basically records will be navigated through child with parent id.
When building the tree it starts from first record. Then start building one by one with by category which represent which level current node to be built.
Now query here is i want a effective algorithm to delete those child records when parent record is deleted.
My table will be having
ID, Name, ParentID, CategoryID Details, etc...
1 Testing, null, ....., 1
2 Child1 , 1 , ..... , 2
3 Testing1, null, .......,1
4 Child2 , 3 , ..........,2
Above is sample records. But tree node depth will go down to 8 levels. So I cannot assume or hardcode any values.
I want to have a effective algoritm to delete those child nodes from table when deleting parent.
And that deletion should be genric enough to delete at any level. Like if i want to delete just the child alone then also it should be possible.
Let me know if any further details needed.
Looking for it.
Cheers!
Sachi
I am looking forward for some good algorithm for one of my requirement.
I got a table which will be displayed in frontend in treeview.
So basically records will be navigated through child with parent id.
When building the tree it starts from first record. Then start building one by one with by category which represent which level current node to be built.
Now query here is i want a effective algorithm to delete those child records when parent record is deleted.
My table will be having
ID, Name, ParentID, CategoryID Details, etc...
1 Testing, null, ....., 1
2 Child1 , 1 , ..... , 2
3 Testing1, null, .......,1
4 Child2 , 3 , ..........,2
Above is sample records. But tree node depth will go down to 8 levels. So I cannot assume or hardcode any values.
I want to have a effective algoritm to delete those child nodes from table when deleting parent.
And that deletion should be genric enough to delete at any level. Like if i want to delete just the child alone then also it should be possible.
Let me know if any further details needed.
Looking for it.
Cheers!
Sachi
ASKER
Sorry David. I think you took it different.
Everytime I delete a node or soo, I will be refreshing whole treeview. so that I get updated treeview.
But I want to delete those records from table. My point here will be to delete those records from table.
Treeview I do not have any problem.
Cheers!
Sachi
Everytime I delete a node or soo, I will be refreshing whole treeview. so that I get updated treeview.
But I want to delete those records from table. My point here will be to delete those records from table.
Treeview I do not have any problem.
Cheers!
Sachi
One posible solution is using a recursive method to gather in a collection the ID's to delete (easier to write but not the most efficient) .
ArrayList aID = new ArrayList();
aID.Add(firstID); // first you add the parent node ID - the one you want to delete
RecDelete(firstID); // call the recursive procedure to identify the IDs which are to be deleted
private void RecDelete(int myID)
{
for (int i=0;i<MyTable.Rows.Count;i
{
if (!MyTable.Rows[i].IsNull("
// if the ParentID is equal with parameter add the to collection and
// recursively call the procedure for the added ID
if ((int)MyTable.Rows[i]["Par
{
aID.Add((int)MyTable.Rows[
RecDelete((int)MyTable.Row
}
}
}
Now you have to scan the table one more time and delete the rows which ID's are in the collection.
Note: you cannot delete the records directly because it is posible to skip rows when you scan the table multiple times.
hth
This is an issue I recently ran into and solved. Here is the high level of what I did...
1) Create a class for the data, or multiple classes if needed. Inherit from a GenericClass ( I called mine DataClassBase ).
2) Create a strongly typed collection for the classes created in step 1, which inherit from a GenericClassCollection ( I called mine DataClassCollection) that inherits from CollectionBase (DotNET abstract class).
3) On the DataClassBase include three boolean properties IsNew, IsDirty, ShouldDelete. Also, for your case create a property that is of DataClassCollection.
4) On the DataClassBase include a public virtual Load( IDataReader dr ) {} and public virtual Save() {} plus whatever else you need. Note these methods will need to be overridden for each DataClassBase. Also for you case create a public virtual Delete() {}, which will simply set the ShouldDelete property to true and if the DataClassCollection property is not null loop throught that calling each of those classes delete method. Note the save will also need to loop through the DataClassCollection.
5) On the DataClassCollection include methods for loading the collection and saving the collection. Note, my load got the data from the DB, positioned the record pointer to a record and then called the DataClassBase load method. The save collection loops through contained classes calling the DataClassBase save method.
6) When a node is set to be deleted call the associated classes delete method.
7) When apply is hit (or what ever you want to trigger it) call the Save on the top DataClassCollection.
Obviously, you need to make sure you keep the connection between the node in the tree and the class in the collection.
By doing it this way you only have to load one layer at a time, i.e load the first collection then only load a subcollection when the node is expanded. All of the work is done in a business layer as opposed to the UI layer. A great deal of the work if generic enough can be done in the generic classes so that the children classes simply use the methods, which makes adding classes easier. The DataClasses can be used to translate the data to look like you want, for example if I have Id, FName and LName in a class but I want the tree node to look like ID : Full Name then I can create a property like public string TNode { get { return Id + ": " + fName + " " + lName; } }. If I do this on all of my data classes then it does not matter what the data is in the class I simply use the TNode property to get what I want to display in the tree.
Anyhow, if you are interested in this I can give you some sample code.
nipnfriar_tuck
1) Create a class for the data, or multiple classes if needed. Inherit from a GenericClass ( I called mine DataClassBase ).
2) Create a strongly typed collection for the classes created in step 1, which inherit from a GenericClassCollection ( I called mine DataClassCollection) that inherits from CollectionBase (DotNET abstract class).
3) On the DataClassBase include three boolean properties IsNew, IsDirty, ShouldDelete. Also, for your case create a property that is of DataClassCollection.
4) On the DataClassBase include a public virtual Load( IDataReader dr ) {} and public virtual Save() {} plus whatever else you need. Note these methods will need to be overridden for each DataClassBase. Also for you case create a public virtual Delete() {}, which will simply set the ShouldDelete property to true and if the DataClassCollection property is not null loop throught that calling each of those classes delete method. Note the save will also need to loop through the DataClassCollection.
5) On the DataClassCollection include methods for loading the collection and saving the collection. Note, my load got the data from the DB, positioned the record pointer to a record and then called the DataClassBase load method. The save collection loops through contained classes calling the DataClassBase save method.
6) When a node is set to be deleted call the associated classes delete method.
7) When apply is hit (or what ever you want to trigger it) call the Save on the top DataClassCollection.
Obviously, you need to make sure you keep the connection between the node in the tree and the class in the collection.
By doing it this way you only have to load one layer at a time, i.e load the first collection then only load a subcollection when the node is expanded. All of the work is done in a business layer as opposed to the UI layer. A great deal of the work if generic enough can be done in the generic classes so that the children classes simply use the methods, which makes adding classes easier. The DataClasses can be used to translate the data to look like you want, for example if I have Id, FName and LName in a class but I want the tree node to look like ID : Full Name then I can create a property like public string TNode { get { return Id + ": " + fName + " " + lName; } }. If I do this on all of my data classes then it does not matter what the data is in the class I simply use the TNode property to get what I want to display in the tree.
Anyhow, if you are interested in this I can give you some sample code.
nipnfriar_tuck
ASKER
Hi Nipnfriar_tuck,
Sorry for delay in reply. I was working on other things.
So your explaintation gives me a small picture.
Ya I would be better to view a sample.
But wondering whether it is possible to do it still more simpler. Have a class will solve this issue means..anyway lets see your example.
I only intrested in delete method. Other things i do not need.
Currently I am using a exhaustive way which do not give me full result i want too.
Hope to solve this sooner.
Well, you are understand the whole deletion part correctly right.
When deleting parent all the child records should be deleted. i.e even if child have sub-childs too. So deleting child again need to delete sub-child too. Hope your did acheived it with your logic already :) ..
Looking for your reply.
Thanks and Regards,
Sachi
Sorry for delay in reply. I was working on other things.
So your explaintation gives me a small picture.
Ya I would be better to view a sample.
But wondering whether it is possible to do it still more simpler. Have a class will solve this issue means..anyway lets see your example.
I only intrested in delete method. Other things i do not need.
Currently I am using a exhaustive way which do not give me full result i want too.
Hope to solve this sooner.
Well, you are understand the whole deletion part correctly right.
When deleting parent all the child records should be deleted. i.e even if child have sub-childs too. So deleting child again need to delete sub-child too. Hope your did acheived it with your logic already :) ..
Looking for your reply.
Thanks and Regards,
Sachi
Alrighty then here you go....
Lets say you have a dataclass defined like:
using System;
using System.Data;
using System.Collections;
namespace MyCompany.MyDomain.MyNames pace
{
public class DataClassBase : IDisposable
{
#region Fields
protected bool isNew;
protected bool isDirty;
protected bool shouldDelete;
protected string id = String.Empty;
protected string name = String.Empty;
protected string description=String.Empty;
#endregion
#region Properties
public bool IsNew
{
get { return isNew; }
set { isNew = value; }
}
public bool IsDirty
{
get { return isDirty; }
set { isDirty = value; }
}
public bool ShouldDelete
{
get { return shouldDelete; }
set { shouldDelete = value; }
}
public string ID
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; isDirty=true; }
}
public string Description
{
get { return description;}
set { description = value;
isDirty=true;
}
}
#endregion
#region Base
public SecClassBase()
{
IsNew = true;
IsDirty = false;
ShouldDelete = false;
}
#region IDisposable Members
public void Dispose()
{
}
#endregion
#endregion
#region Methods
public virtual void Load( IDataReader dr ) {}
public virtual void Save( SecDataAccess DA ) {}
public virtual System.Windows.Forms.TreeN ode Populate()
{
System.Windows.Forms.TreeN ode treeNode = new System.Windows.Forms.TreeN ode();
treeNode.Text=this.Name;
treeNode.Tag=this.ID;
return treeNode;
}
public virtual System.Windows.Forms.ListV iewItem Populate(int index)
{
System.Windows.Forms.ListV iewItem lvItem = new System.Windows.Forms.ListV iewItem();
lvItem.Text=this.Name;
lvItem.Tag=this.ID;
lvItem.SubItems[lvItem.Sub Items.Coun t - 1].Text= index.ToString();
return lvItem;
}
public virtual void Define( ref System.Windows.Forms.ListV iew lv, System.Windows.Forms.Horiz ontalAlign ment ha ){}
#endregion
}
I would also create a collection class...
public class DataClassCollection : CollectionBase, IDisposable
{
#region Collection Methods
protected override void OnValidate( Object value )
{
if ( value.GetType().Name != "DataClassBase")
throw new ArgumentException( "value must be of type DataClassBase.", "value" );
}
public DataClassBase this[int index]
{
get
{
if ( index >= 0 && index < List.Count )
{
return((DataClassBase)List [index]);
}
else
{
return null;
}
}
set
{
if ( index >= 0 && index < List.Count )
{
List[index] = value;
}
}
}
public int Add(DataClassBase dataClassBase)
{
int index = List.Add(dataClassBase);
return index;
}
public void Remove(DataClassBase dataClassBase)
{
List.Remove(dataClassBase) ;
}
public void Insert( int index, DataClassBase value )
{
List.Insert( index, value );
}
public int IndexOf( DataClassBase value )
{
return( List.IndexOf( value ) );
}
#endregion
#region PublicMethods
/// <summary>
/// This is base class method which Loads the Collection with all the Items.
/// </summary>
public virtual void LoadObjects(){}
/// <summary>
/// This is Base class method
/// This method Loops through all the Items in the collection and perform the
/// Save action. Item can marked for ShouldDelete/IsDirty/IsNew . Based upon
/// whats if the flag type action is taken.
/// ShouldDelete := Item is Removed from the Collection and from database
/// IsDirty := Item is updated for changes made
/// IsNew := New item is added to collection and to database.
/// </summary>
public virtual void SaveObjects(){}
/// <summary>
///
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public virtual DataClassCollection Filter( string Id ) { return null; }
/// <summary>
///
/// </summary>
/// <param name="Uid"></param>
/// <returns></returns>
public virtual DataClassCollection Filter( int Uid ,bool isUser ) { return null; }
/// <summary>
///
/// </summary>
/// <param name="parentNode"></param>
/// <param name="lastLevel"></param>
public virtual void Populate(ref System.Windows.Forms.TreeN ode parentNode, bool lastLevel)
{
for ( int i = 0; i < List.Count; i++ )
{
System.Windows.Forms.TreeN ode tNode = this[i].Populate();
if ( !lastLevel )
{
tNode.Nodes.Add( new System.Windows.Forms.TreeN ode() );
}
parentNode.Nodes.Add( tNode );
}
}
/// <summary>
///
/// </summary>
/// <param name="rootNode"></param>
/// <param name="lastLevel"></param>
public virtual void Populate(ref System.Windows.Forms.TreeV iew rootNode, bool lastLevel)
{
for ( int i = 0; i < List.Count; i++ )
{
System.Windows.Forms.TreeN ode tNode = this[i].Populate();
if ( !lastLevel )
{
tNode.Nodes.Add( new System.Windows.Forms.TreeN ode() );
}
rootNode.Nodes.Add( tNode );
}
}
/// <summary>
///
/// </summary>
/// <param name="lvw"></param>
public virtual void Populate(ref System.Windows.Forms.ListV iew lvw)
{
for ( int i = 0; i < List.Count; i++ )
{
lvw.Items.Add( this[i].Populate(i) );
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
if ( List != null )
{
foreach ( object obj in List )
{
DataClassBase c = obj as DataClassBase;
if ( c != null )
{
c.Dispose();
}
}
List.Clear();
}
}
#endregion
}
}
With the Base Classes done I would create classes inherited from the base class...
using System;
using System.Data;
using System.Collections;
namespace MyCompany.MyDomain.MyNames pace
{
public class MyDataClass: DataClassBase
{
#region Fields
private string myDataUid;
private string myDataText;
#endregion
#region Properties
public string MyDataUid
{
get{return myDataUid;}
set{myDataUid=value;isDirt y=true;}
}
public string MyDataText
{
get{return myDataText;}
set{myDataText=value;isDir ty=true;}
}
#endregion
#region Base
public SecRoleObjectRights()
{
}
#endregion
#region Public Methods
/// <summary>
/// This method load a single Item the class
/// </summary>
/// <param name="dr"></param>
public override void Load( IDataReader dr )
{
this.myDataUid=OracleDataA ccess.GetR eaderStrin g( dr, "MyData_UID" );
this.MyDataText=OracleData Access.Get ReaderStri ng( dr, "MyData_Text" );
}
/// <summary>
/// This method is for Saving individual Item of the Class.
/// Also this method is called form SecRoleObjectRightsCollect ion
/// which help to load the SecRoleObjectRightsCollect ion
/// </summary>
public override void Save( )
{
if (shouldDelete)
{
// Delete the record
}
else if ( isDirty || isNew )
{
if ( isNew ) {
// Insert record
}
else {
// Update record
}
}
}
/// <summary>
/// This method Adds Columns to the Listview object, which is passed as parameter
/// </summary>
/// <param name="lv">Listview Object</param>
/// <param name="ha">Horizontal Position</param>
public override void Define( ref System.Windows.Forms.ListV iew lv, System.Windows.Forms.Horiz ontalAlign ment ha )
{
lv.Columns.Add("My Data Id", 96, ha);
lv.Columns.Add("My Data Text", 96, ha);
}
/// <summary>
/// This method Creates new ListViewItem and returns , so that the ListView object can be populated
/// </summary>
/// <param name="index"></param>
/// <returns>ListViewItem</ret urns>
public override System.Windows.Forms.ListV iewItem Populate(int index)
{
System.Windows.Forms.ListV iewItem lvi = new System.Windows.Forms.ListV iewItem( this.MyDataUid );
lvi.Tag = this.MyDataUid;
lvi.SubItems.Add( this.MyDataText );
return lvi;
}
#endregion
}
public class MyDataCollection: DataClassCollection, IDisposable
{
#region Collection Methods
protected override void OnValidate( Object value )
{
if ( value.GetType().Name != "MyDataClass")
throw new ArgumentException( "value must be of type MyDataClass.", "value" );
}
public new MyDataClass this[int index]
{
get
{
if ( index >= 0 && index < List.Count )
{
return((MyDataClass)List[i ndex]);
}
else
{
return null;
}
}
set
{
if ( index >= 0 && index < List.Count )
{
List[index] = value;
}
}
}
public int Add(MyDataClass myDC)
{
int index = List.Add(myDC);
return index;
}
public void Remove(MyDataClass myDC)
{
List.Remove(myDC);
}
public void Insert( int index, MyDataClass value )
{
List.Insert( index, value );
}
public int IndexOf( MyDataClass value )
{
return( List.IndexOf( value ) );
}
#endregion
#region Public Methods
/// <summary>
/// Loads the MyDataCollection with all the Items.
/// </summary>
public override void LoadObjects()
{
List.Clear();
IDataReader Reader = null;
// Get the data into the reader...
while( true == Reader.Read() )
{
MyDataClass dc = new MyDataClass();
dc.Load( Reader );
this.Add(r);
}
Reader = null;
}
/// <summary>
/// This method Loops through all the Items in the collection and perform the
/// Save action. Item can marked for ShouldDelete/IsDirty/IsNew . Based upon
/// whats if the flag type action is taken.
/// ShouldDelete := Item is Removed from the Collection and from database
/// IsDirty := Item is updated for changes made
/// IsNew := New item is added to collection and to database.
/// </summary>
public override void SaveObjects( )
{
ArrayList exCol = new ArrayList();
foreach ( MyDataClass u in this )
{
try
{
u.Save();
}
catch ( Exception ex )
{
exCol.Add( ex );
}
}
if ( exCol.Count > 0 )
{
string exMsg = "";
foreach ( Exception ex in exCol )
{
exMsg += ex.Message + "; ";
}
throw new Exception( exMsg );
}
}
public override MyDataCollection Filter( string Id )
{
MyDataCollection col = new MyDataCollection();
for ( int i = 0; i < this.Count; i++ )
{
MyDataClass su = this[i] as MyDataClass;
if ( su != null && su.MyDataUid.Equals( Id ) )
{
col.Add( su );
}
}
return col;
}
#endregion
#region IDisposable Members
public new void Dispose()
{
if ( List != null )
{
foreach ( object obj in List )
{
MyDataClass c = obj as MyDataClass;
if ( c != null )
{
c.Dispose();
}
}
List.Clear();
}
}
#endregion
}
}
Finally, you use these classes in a tree like this:
You would have methods like this in the class...
#region Company
private void FillCompanyTree()
{
treeViewCompanies.Nodes.Cl ear();
MyDataCollection sccol = new MyDataCollection();
sccol.LoadObjects( );
sccol.Populate( ref treeViewCompanies, false );
}
private void treeViewCompanies_AfterSel ect(object sender, System.Windows.Forms.TreeV iewEventAr gs e)
{
try
{
string[] nodes = e.Node.FullPath.Split( '\\' );
int nodeValue=int.Parse(e.Node .Tag.ToStr ing());
if ( nodes.Length > 3 )
{
bool isUser = false;
foreach ( string n in nodes )
{
if ( n.Equals("Users") )
{
isUser = true;
break;
}
}
string nodeName = nodes[nodes.Length - 1];
PopulateCompanyListView(no deValue,is User);
}
}
catch( Exception Err )
{
ReportErr( Err );
}
}
private void treeViewCompanies_BeforeEx pand(objec t sender, System.Windows.Forms.TreeV iewCancelE ventArgs e)
{
TreeNode tNode = e.Node;
string[] nodes = e.Node.FullPath.Split( '\\' );
switch ( nodes.Length )
{
case 1:
FillNode(new MyDataCollection(), tNode.Tag.ToString(), false, ref tNode);
break;
case 2:
tNode.Nodes.Clear();
TreeNode uNode = new TreeNode();
uNode.Text = "Users";
uNode.Tag = "Users";
uNode.Nodes.Add( new TreeNode() );
tNode.Nodes.Add( uNode );
TreeNode wNode = new TreeNode();
wNode.Text = "Workstations";
wNode.Tag = "Workstations";
wNode.Nodes.Add( new TreeNode() );
tNode.Nodes.Add( wNode );
break;
default:
string nodeName = nodes[nodes.Length - 1];
switch ( nodeName )
{
case "Users":
FillNode(new MyDataCollection(), tNode.Parent.Tag.ToString( ), true, ref tNode);
break;
case "Workstations":
FillNode(new MyDataCollection(), tNode.Parent.Tag.ToString( ), true, ref tNode);
break;
}
break;
}
}
private void PopulateCompanyListView(st ring Id)
{
System.Windows.Forms.Curso r formerCursor = this.Cursor;
this.Cursor = System.Windows.Forms.Curso rs.WaitCur sor;
try
{
MyDataCollection sur = new MyDataCollection();
sur.LoadObjects( );
viewUWRCol = (MyDataCollection)sur.Filt er(Id);
FillDataView( viewUWRCol, ref listViewCOMP );
this.statusBarPanelRpt.Tex t = "";
}
catch( Exception Err )
{
EH.PerformCatch(Err);
this.statusBarPanelRpt.Tex t = "ERROR: " + Err.Message;
}
finally
{
this.Cursor = formerCursor;
}
}
private void PopulateCompanyListView(in t Id, bool isUser)
{
System.Windows.Forms.Curso r formerCursor = this.Cursor;
this.Cursor = System.Windows.Forms.Curso rs.WaitCur sor;
try
{
MyDataCollection sur = new MyDataCollection();
sur.LoadObjects( );
viewUWRCol = (MyDataCollection)sur.Filt er(Id, isUser);
FillDataView( viewUWRCol, ref listViewCOMP );
this.statusBarPanelRpt.Tex t = "";
}
catch( Exception Err )
{
EH.PerformCatch(Err);
this.statusBarPanelRpt.Tex t = "ERROR: " + Err.Message;
}
finally
{
this.Cursor = formerCursor;
}
}
private void FillNode( MyDataCollection scc, string id, bool lastLevel, ref TreeNode ParentNode )
{
scc.LoadObjects( );
ParentNode.Nodes.Clear();
scc.Filter( id ).Populate( ref ParentNode, lastLevel );
}
private void NewUWR()
{
try
{
if ( DlgEditComp == null )
{
DlgEditComp = new DlgSecurityEditUserRole( SEC, DA );
}
// pop up the record dialog
DlgEditComp.PopulateScreen ( );
DlgEditComp.FormParent = this;
DlgEditComp.AlignTo(-1,-1, -1,-1,fals e,System.D ateTime.Pa rse("1/1/1 800"),Syst em.DateTim e.Parse("1 /1/1800")) ;
DlgEditComp.Closed += new EventHandler(DlgEditComp_C losed);
DlgEditComp.ShowDialog( this );
}
catch( Exception Err )
{
ReportErr( Err );
}
}
private void UpdateUWR()
{
try
{
if ( 1 == this.listViewCOMP.Selected Items.Coun t)
{
System.Windows.Forms.ListV iewItem LVI = this.listViewCOMP.Selected Items[0];
// pop up the record dialog
DlgEditComp.PopulateScreen ( );
// set the possibilities for the record we are editing
string[] aIds = ((string)LVI.Tag).Split(ne w char[] { ' ' } );
System.DateTime dEff = System.DateTime.Parse( "1/1/1800" );
if ( "" != LVI.SubItems[5].Text )
{
dEff = System.DateTime.Parse( LVI.SubItems[5].Text );
}
System.DateTime dExp = System.DateTime.Parse( "1/1/1800" );
if ( "" != LVI.SubItems[6].Text )
{
dExp = System.DateTime.Parse( LVI.SubItems[6].Text );
}
string ndx = listViewCOMP.SelectedItems [0].SubIte ms[listVie wCOMP.Sele ctedItems[ 0].SubItem s.Count-1] .Text;
int index = int.Parse(ndx);
secUWR = viewUWRCol[index];
secUWR.IsNew = false;
DlgEditComp.AlignTo( secUWR );
DlgEditComp.FormParent = this;
DlgEditComp.Closed += new EventHandler(DlgEditComp_C losed);
DlgEditComp.Show( );
}
}
catch( Exception Err )
{
ReportErr( Err );
}
}
private void DeleteUWR()
{
try
{
string ndx = listViewCOMP.SelectedItems [0].SubIte ms[listVie wCOMP.Sele ctedItems[ 0].SubItem s.Count-1] .Text;
int index = int.Parse(ndx);
secUWR = viewUWRCol[index];
secUWR.ShouldDelete = true;
secUWR.Delete( DA );
PopulateCompanyListView(tr eeViewComp anies.Sele ctedNode.T ext);
}
catch( Exception Err )
{
ReportErr( Err );
}
}
#endregion
Anyhow, this is code that we are actively using, except that I have change the sensitive parts ... i.e. data connection et al. This code is very fast and reactive on the UI and has allowed a great deal of re-use... If I get the time I will try to put together a working solution, but that will not be anytime soon.
Regards.
Lets say you have a dataclass defined like:
using System;
using System.Data;
using System.Collections;
namespace MyCompany.MyDomain.MyNames
{
public class DataClassBase : IDisposable
{
#region Fields
protected bool isNew;
protected bool isDirty;
protected bool shouldDelete;
protected string id = String.Empty;
protected string name = String.Empty;
protected string description=String.Empty;
#endregion
#region Properties
public bool IsNew
{
get { return isNew; }
set { isNew = value; }
}
public bool IsDirty
{
get { return isDirty; }
set { isDirty = value; }
}
public bool ShouldDelete
{
get { return shouldDelete; }
set { shouldDelete = value; }
}
public string ID
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; isDirty=true; }
}
public string Description
{
get { return description;}
set { description = value;
isDirty=true;
}
}
#endregion
#region Base
public SecClassBase()
{
IsNew = true;
IsDirty = false;
ShouldDelete = false;
}
#region IDisposable Members
public void Dispose()
{
}
#endregion
#endregion
#region Methods
public virtual void Load( IDataReader dr ) {}
public virtual void Save( SecDataAccess DA ) {}
public virtual System.Windows.Forms.TreeN
{
System.Windows.Forms.TreeN
treeNode.Text=this.Name;
treeNode.Tag=this.ID;
return treeNode;
}
public virtual System.Windows.Forms.ListV
{
System.Windows.Forms.ListV
lvItem.Text=this.Name;
lvItem.Tag=this.ID;
lvItem.SubItems[lvItem.Sub
return lvItem;
}
public virtual void Define( ref System.Windows.Forms.ListV
#endregion
}
I would also create a collection class...
public class DataClassCollection : CollectionBase, IDisposable
{
#region Collection Methods
protected override void OnValidate( Object value )
{
if ( value.GetType().Name != "DataClassBase")
throw new ArgumentException( "value must be of type DataClassBase.", "value" );
}
public DataClassBase this[int index]
{
get
{
if ( index >= 0 && index < List.Count )
{
return((DataClassBase)List
}
else
{
return null;
}
}
set
{
if ( index >= 0 && index < List.Count )
{
List[index] = value;
}
}
}
public int Add(DataClassBase dataClassBase)
{
int index = List.Add(dataClassBase);
return index;
}
public void Remove(DataClassBase dataClassBase)
{
List.Remove(dataClassBase)
}
public void Insert( int index, DataClassBase value )
{
List.Insert( index, value );
}
public int IndexOf( DataClassBase value )
{
return( List.IndexOf( value ) );
}
#endregion
#region PublicMethods
/// <summary>
/// This is base class method which Loads the Collection with all the Items.
/// </summary>
public virtual void LoadObjects(){}
/// <summary>
/// This is Base class method
/// This method Loops through all the Items in the collection and perform the
/// Save action. Item can marked for ShouldDelete/IsDirty/IsNew
/// whats if the flag type action is taken.
/// ShouldDelete := Item is Removed from the Collection and from database
/// IsDirty := Item is updated for changes made
/// IsNew := New item is added to collection and to database.
/// </summary>
public virtual void SaveObjects(){}
/// <summary>
///
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public virtual DataClassCollection Filter( string Id ) { return null; }
/// <summary>
///
/// </summary>
/// <param name="Uid"></param>
/// <returns></returns>
public virtual DataClassCollection Filter( int Uid ,bool isUser ) { return null; }
/// <summary>
///
/// </summary>
/// <param name="parentNode"></param>
/// <param name="lastLevel"></param>
public virtual void Populate(ref System.Windows.Forms.TreeN
{
for ( int i = 0; i < List.Count; i++ )
{
System.Windows.Forms.TreeN
if ( !lastLevel )
{
tNode.Nodes.Add( new System.Windows.Forms.TreeN
}
parentNode.Nodes.Add( tNode );
}
}
/// <summary>
///
/// </summary>
/// <param name="rootNode"></param>
/// <param name="lastLevel"></param>
public virtual void Populate(ref System.Windows.Forms.TreeV
{
for ( int i = 0; i < List.Count; i++ )
{
System.Windows.Forms.TreeN
if ( !lastLevel )
{
tNode.Nodes.Add( new System.Windows.Forms.TreeN
}
rootNode.Nodes.Add( tNode );
}
}
/// <summary>
///
/// </summary>
/// <param name="lvw"></param>
public virtual void Populate(ref System.Windows.Forms.ListV
{
for ( int i = 0; i < List.Count; i++ )
{
lvw.Items.Add( this[i].Populate(i) );
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
if ( List != null )
{
foreach ( object obj in List )
{
DataClassBase c = obj as DataClassBase;
if ( c != null )
{
c.Dispose();
}
}
List.Clear();
}
}
#endregion
}
}
With the Base Classes done I would create classes inherited from the base class...
using System;
using System.Data;
using System.Collections;
namespace MyCompany.MyDomain.MyNames
{
public class MyDataClass: DataClassBase
{
#region Fields
private string myDataUid;
private string myDataText;
#endregion
#region Properties
public string MyDataUid
{
get{return myDataUid;}
set{myDataUid=value;isDirt
}
public string MyDataText
{
get{return myDataText;}
set{myDataText=value;isDir
}
#endregion
#region Base
public SecRoleObjectRights()
{
}
#endregion
#region Public Methods
/// <summary>
/// This method load a single Item the class
/// </summary>
/// <param name="dr"></param>
public override void Load( IDataReader dr )
{
this.myDataUid=OracleDataA
this.MyDataText=OracleData
}
/// <summary>
/// This method is for Saving individual Item of the Class.
/// Also this method is called form SecRoleObjectRightsCollect
/// which help to load the SecRoleObjectRightsCollect
/// </summary>
public override void Save( )
{
if (shouldDelete)
{
// Delete the record
}
else if ( isDirty || isNew )
{
if ( isNew ) {
// Insert record
}
else {
// Update record
}
}
}
/// <summary>
/// This method Adds Columns to the Listview object, which is passed as parameter
/// </summary>
/// <param name="lv">Listview Object</param>
/// <param name="ha">Horizontal Position</param>
public override void Define( ref System.Windows.Forms.ListV
{
lv.Columns.Add("My Data Id", 96, ha);
lv.Columns.Add("My Data Text", 96, ha);
}
/// <summary>
/// This method Creates new ListViewItem and returns , so that the ListView object can be populated
/// </summary>
/// <param name="index"></param>
/// <returns>ListViewItem</ret
public override System.Windows.Forms.ListV
{
System.Windows.Forms.ListV
lvi.Tag = this.MyDataUid;
lvi.SubItems.Add( this.MyDataText );
return lvi;
}
#endregion
}
public class MyDataCollection: DataClassCollection, IDisposable
{
#region Collection Methods
protected override void OnValidate( Object value )
{
if ( value.GetType().Name != "MyDataClass")
throw new ArgumentException( "value must be of type MyDataClass.", "value" );
}
public new MyDataClass this[int index]
{
get
{
if ( index >= 0 && index < List.Count )
{
return((MyDataClass)List[i
}
else
{
return null;
}
}
set
{
if ( index >= 0 && index < List.Count )
{
List[index] = value;
}
}
}
public int Add(MyDataClass myDC)
{
int index = List.Add(myDC);
return index;
}
public void Remove(MyDataClass myDC)
{
List.Remove(myDC);
}
public void Insert( int index, MyDataClass value )
{
List.Insert( index, value );
}
public int IndexOf( MyDataClass value )
{
return( List.IndexOf( value ) );
}
#endregion
#region Public Methods
/// <summary>
/// Loads the MyDataCollection with all the Items.
/// </summary>
public override void LoadObjects()
{
List.Clear();
IDataReader Reader = null;
// Get the data into the reader...
while( true == Reader.Read() )
{
MyDataClass dc = new MyDataClass();
dc.Load( Reader );
this.Add(r);
}
Reader = null;
}
/// <summary>
/// This method Loops through all the Items in the collection and perform the
/// Save action. Item can marked for ShouldDelete/IsDirty/IsNew
/// whats if the flag type action is taken.
/// ShouldDelete := Item is Removed from the Collection and from database
/// IsDirty := Item is updated for changes made
/// IsNew := New item is added to collection and to database.
/// </summary>
public override void SaveObjects( )
{
ArrayList exCol = new ArrayList();
foreach ( MyDataClass u in this )
{
try
{
u.Save();
}
catch ( Exception ex )
{
exCol.Add( ex );
}
}
if ( exCol.Count > 0 )
{
string exMsg = "";
foreach ( Exception ex in exCol )
{
exMsg += ex.Message + "; ";
}
throw new Exception( exMsg );
}
}
public override MyDataCollection Filter( string Id )
{
MyDataCollection col = new MyDataCollection();
for ( int i = 0; i < this.Count; i++ )
{
MyDataClass su = this[i] as MyDataClass;
if ( su != null && su.MyDataUid.Equals( Id ) )
{
col.Add( su );
}
}
return col;
}
#endregion
#region IDisposable Members
public new void Dispose()
{
if ( List != null )
{
foreach ( object obj in List )
{
MyDataClass c = obj as MyDataClass;
if ( c != null )
{
c.Dispose();
}
}
List.Clear();
}
}
#endregion
}
}
Finally, you use these classes in a tree like this:
You would have methods like this in the class...
#region Company
private void FillCompanyTree()
{
treeViewCompanies.Nodes.Cl
MyDataCollection sccol = new MyDataCollection();
sccol.LoadObjects( );
sccol.Populate( ref treeViewCompanies, false );
}
private void treeViewCompanies_AfterSel
{
try
{
string[] nodes = e.Node.FullPath.Split( '\\' );
int nodeValue=int.Parse(e.Node
if ( nodes.Length > 3 )
{
bool isUser = false;
foreach ( string n in nodes )
{
if ( n.Equals("Users") )
{
isUser = true;
break;
}
}
string nodeName = nodes[nodes.Length - 1];
PopulateCompanyListView(no
}
}
catch( Exception Err )
{
ReportErr( Err );
}
}
private void treeViewCompanies_BeforeEx
{
TreeNode tNode = e.Node;
string[] nodes = e.Node.FullPath.Split( '\\' );
switch ( nodes.Length )
{
case 1:
FillNode(new MyDataCollection(), tNode.Tag.ToString(), false, ref tNode);
break;
case 2:
tNode.Nodes.Clear();
TreeNode uNode = new TreeNode();
uNode.Text = "Users";
uNode.Tag = "Users";
uNode.Nodes.Add( new TreeNode() );
tNode.Nodes.Add( uNode );
TreeNode wNode = new TreeNode();
wNode.Text = "Workstations";
wNode.Tag = "Workstations";
wNode.Nodes.Add( new TreeNode() );
tNode.Nodes.Add( wNode );
break;
default:
string nodeName = nodes[nodes.Length - 1];
switch ( nodeName )
{
case "Users":
FillNode(new MyDataCollection(), tNode.Parent.Tag.ToString(
break;
case "Workstations":
FillNode(new MyDataCollection(), tNode.Parent.Tag.ToString(
break;
}
break;
}
}
private void PopulateCompanyListView(st
{
System.Windows.Forms.Curso
this.Cursor = System.Windows.Forms.Curso
try
{
MyDataCollection sur = new MyDataCollection();
sur.LoadObjects( );
viewUWRCol = (MyDataCollection)sur.Filt
FillDataView( viewUWRCol, ref listViewCOMP );
this.statusBarPanelRpt.Tex
}
catch( Exception Err )
{
EH.PerformCatch(Err);
this.statusBarPanelRpt.Tex
}
finally
{
this.Cursor = formerCursor;
}
}
private void PopulateCompanyListView(in
{
System.Windows.Forms.Curso
this.Cursor = System.Windows.Forms.Curso
try
{
MyDataCollection sur = new MyDataCollection();
sur.LoadObjects( );
viewUWRCol = (MyDataCollection)sur.Filt
FillDataView( viewUWRCol, ref listViewCOMP );
this.statusBarPanelRpt.Tex
}
catch( Exception Err )
{
EH.PerformCatch(Err);
this.statusBarPanelRpt.Tex
}
finally
{
this.Cursor = formerCursor;
}
}
private void FillNode( MyDataCollection scc, string id, bool lastLevel, ref TreeNode ParentNode )
{
scc.LoadObjects( );
ParentNode.Nodes.Clear();
scc.Filter( id ).Populate( ref ParentNode, lastLevel );
}
private void NewUWR()
{
try
{
if ( DlgEditComp == null )
{
DlgEditComp = new DlgSecurityEditUserRole( SEC, DA );
}
// pop up the record dialog
DlgEditComp.PopulateScreen
DlgEditComp.FormParent = this;
DlgEditComp.AlignTo(-1,-1,
DlgEditComp.Closed += new EventHandler(DlgEditComp_C
DlgEditComp.ShowDialog( this );
}
catch( Exception Err )
{
ReportErr( Err );
}
}
private void UpdateUWR()
{
try
{
if ( 1 == this.listViewCOMP.Selected
{
System.Windows.Forms.ListV
// pop up the record dialog
DlgEditComp.PopulateScreen
// set the possibilities for the record we are editing
string[] aIds = ((string)LVI.Tag).Split(ne
System.DateTime dEff = System.DateTime.Parse( "1/1/1800" );
if ( "" != LVI.SubItems[5].Text )
{
dEff = System.DateTime.Parse( LVI.SubItems[5].Text );
}
System.DateTime dExp = System.DateTime.Parse( "1/1/1800" );
if ( "" != LVI.SubItems[6].Text )
{
dExp = System.DateTime.Parse( LVI.SubItems[6].Text );
}
string ndx = listViewCOMP.SelectedItems
int index = int.Parse(ndx);
secUWR = viewUWRCol[index];
secUWR.IsNew = false;
DlgEditComp.AlignTo( secUWR );
DlgEditComp.FormParent = this;
DlgEditComp.Closed += new EventHandler(DlgEditComp_C
DlgEditComp.Show( );
}
}
catch( Exception Err )
{
ReportErr( Err );
}
}
private void DeleteUWR()
{
try
{
string ndx = listViewCOMP.SelectedItems
int index = int.Parse(ndx);
secUWR = viewUWRCol[index];
secUWR.ShouldDelete = true;
secUWR.Delete( DA );
PopulateCompanyListView(tr
}
catch( Exception Err )
{
ReportErr( Err );
}
}
#endregion
Anyhow, this is code that we are actively using, except that I have change the sensitive parts ... i.e. data connection et al. This code is very fast and reactive on the UI and has allowed a great deal of re-use... If I get the time I will try to put together a working solution, but that will not be anytime soon.
Regards.
ASKER
Oh i c. So you are using this for winforms.
Anyway let me take a look and update you.
Sachi
Anyway let me take a look and update you.
Sachi
ASKER
Hi,
I am not really satisfied to go with your class for now. Because I want to make this still more simplier.
Here below i am sharing my logic. I am looking forward to optimize this 100%.
method DeleteMain (id, parentid, categoryid)
{
Reason is that i can check all the levels after given categoryid.
Example... if category id was given as 4, then drill down from 4 to 8.
So for each category i will call a seperate function.
Switch(categoryid)
{
Call the methods accordingly.
//methods will be like cat3, cat4 etc......
}
}
method cat3(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat4 method passing on that ds
else
simply delete the record
}
}
method cat4(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat5 method passing on that ds
else
simply delete the record
}
}
method cat5(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat6 method passing on that ds
else
simply delete the record
}
}
method cat6(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat7 method passing on that ds
else
simply delete the record
}
}
method cat7(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat8 method passing on that ds
else
simply delete the record
}
}
method cat8(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat8 method passing on that ds
else
simply delete the record
}
}
Well, something i am missing. I think this logic can be seriously simplified.
Cheers!
Sachi
I am not really satisfied to go with your class for now. Because I want to make this still more simplier.
Here below i am sharing my logic. I am looking forward to optimize this 100%.
method DeleteMain (id, parentid, categoryid)
{
Reason is that i can check all the levels after given categoryid.
Example... if category id was given as 4, then drill down from 4 to 8.
So for each category i will call a seperate function.
Switch(categoryid)
{
Call the methods accordingly.
//methods will be like cat3, cat4 etc......
}
}
method cat3(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat4 method passing on that ds
else
simply delete the record
}
}
method cat4(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat5 method passing on that ds
else
simply delete the record
}
}
method cat5(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat6 method passing on that ds
else
simply delete the record
}
}
method cat6(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat7 method passing on that ds
else
simply delete the record
}
}
method cat7(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat8 method passing on that ds
else
simply delete the record
}
}
method cat8(ds)
{
Get through each row in dataset.
{
check if any immediate child is avilable.
if soo invoke cat8 method passing on that ds
else
simply delete the record
}
}
Well, something i am missing. I think this logic can be seriously simplified.
Cheers!
Sachi
Hi,
I still consider that a recursive method is the simplest way to solve the problem. The method will receive an ID as parameter, will scan the collection of rows and if encounters a row whose parentID is equal with the parameter will delete the row and it will call itself giving the rowID as the new parameter.
For the given example
ID, Name, ParentID, CategoryID Details, etc...
1 Testing, null, ....., 1
2 Child1 , 1 , ..... , 2
3 Testing1, null, .......,1
4 Child2 , 3 , ..........,2
if I delete the first row I will call the method with parameter ID=1, RecDelete(1), the value 1 will be find in ParentID column on the second row and the method will be called with parameter ID=2, RecDelete(2). The second call will delete nothing because value 2 is not found on ParentID column.
Actually you can not delete the records directly, you have to collect the IDs first and delete the records after you have returned from the recursive method.
cheers
I agree with sumix on using a recursive method, that is if you are not going to use the strongly typed/object concept that I stated earlier.
ASKER
Hi,
I had solved it with a trigger, and it was a different choice.
Put a colum to track parent and those child nodes under each parent.
It means i will be able to retreive all those child node when i give "1.%" means all those records i can get it. If I want only to child level then it mean i will retrive with sql statment as "1.1.%" it means i will get those nodes which are under child first level.
I guess so this question can be deleted.
Thanks anyway for all of your support.
Regards,
Sachi
I had solved it with a trigger, and it was a different choice.
Put a colum to track parent and those child nodes under each parent.
It means i will be able to retreive all those child node when i give "1.%" means all those records i can get it. If I want only to child level then it mean i will retrive with sql statment as "1.1.%" it means i will get those nodes which are under child first level.
I guess so this question can be deleted.
Thanks anyway for all of your support.
Regards,
Sachi
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
// want a tree and a table, and when you delete a node from the tree, it will delete all the child
// nodes from both that tree and the table.
// So to that, i made a tree node class. When you make the node, pass in the id.
// Your program can subscribe to the Delete event in each node, and add a callback function that
// will in turn delete the row with the deleted nodes id.
using System;
using System.Windows.Forms;
namespace MyNameSpace
{
class TreeNodeDerived : TreeNode
{
private int myId;
public delegate void DeleteCallback(int deletedNodeID);
public event DeleteCallback Delete;
public TreeNodeDerived(int ID)
{
myId = ID;
}
public void DeleteNodeAndChildren()
{
foreach (TreeNodeDerived currentNode in this.Nodes)
currentNode.DeleteNodeAndC
// remove this node or add your own delete implementation
this.OnDelete();
}
protected void OnDelete()
{
this.Remove();
Delete(myId);
}
public int ID
{
get
{
return myId;
}
}
}
}