Author Topic: Creating a collection class  (Read 7430 times)

0 Members and 1 Guest are viewing this topic.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Creating a collection class
« Reply #15 on: July 10, 2013, 11:25:36 PM »
I am just starting to learn MVC 4, and maybe some else knows but I thought you could easily bind data to your controls, but I do not know enough.

This isn't MVC, it is ASP and the controls that I am required to use don't bind all of the data. It merely binds the display text and display value.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Creating a collection class
« Reply #16 on: July 11, 2013, 07:49:23 AM »
MVC is an Architecture and can be used outside of ASP though its primary purpose is for ASP.
Revit 2019, AMEP 2019 64bit Win 10

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Creating a collection class
« Reply #17 on: July 11, 2013, 10:11:37 AM »
oh, really ... didn't know that
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

BlackBox

  • King Gator
  • Posts: 3770
Re: Creating a collection class
« Reply #18 on: July 11, 2013, 12:23:45 PM »
Out of curiosity, which collection did you choose to inherit from... List<T> (non-virtual methods), Collection<T> (virtual methods), ObservableCollection<T>, or BindingList<T>?
"How we think determines what we do, and what we do determines what we get."

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Creating a collection class
« Reply #19 on: July 11, 2013, 02:01:53 PM »
I used List<T> because it was the simplest to implement.

The structure is as follows for this specific portion of the project:
+FacilityManager
     +FacilityCollection
         +FacilityItem
             -ReportsManager
             -ContractsManager

I created several events for the facility collection that fire when List<T> changes  ... actually I just raise the event in the exposed properties and methods that modify the List<T> object. Then in FacilityManager I capture the event and add the item to my ASP control.

The FacilityManager object only exposes the methods related to reading the initial data from the database. All subsequent modifications should be done through the Items property that returns the FacilityCollection, so:
Code - C#: [Select]
  1. //instantiate the facility manager
  2. FacilityManager fm = New FacilityManager(_listControl, _datalayer);
  3. //load all the facility items the current user has access to
  4. fm.LoadAll(_currentUserID);

When the user creates a new item from data input, this happens:
Code - C#: [Select]
  1. //Create a new facility item
  2. FacilityItem fi = New FacilityItem();
  3. //set the various properties from the controls
  4. //...
  5. //Add the new FacilityItem to the FacilityCollection
  6. fm.Items.Add(fi);

At this point, FacilityCollection raises the event ItemAdded and it is captured in the FacilityManager class
Code - C#: [Select]
  1. private void _facilities_ItemAdded(object sender, ListItemArgs e)
  2. {
  3.     //Add the item to the control
  4.     _listControl.Add(sender.ToListItem);
  5. }
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Creating a collection class
« Reply #20 on: July 11, 2013, 03:06:36 PM »
If "Items" is a property of FacilityManager, why create an event for ItemAdded?  Why not call _listControl.Add(sender.ToListItem) in the setter?
Revit 2019, AMEP 2019 64bit Win 10

BlackBox

  • King Gator
  • Posts: 3770
Re: Creating a collection class
« Reply #21 on: July 11, 2013, 03:12:12 PM »
Knowing Keith was after certain events, is one of the reasons I asked which collection type was being inherited from... For example, the BindingList<T> type's built-in AddingNew, and ListChanged events.
"How we think determines what we do, and what we do determines what we get."

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Creating a collection class
« Reply #22 on: July 11, 2013, 04:55:15 PM »
If "Items" is a property of FacilityManager, why create an event for ItemAdded?  Why not call _listControl.Add(sender.ToListItem) in the setter?

See if this makes sense:
Code - C#: [Select]
  1. Class FacilityManager
  2. {
  3.      private FacilityCollection _fc = New FacilityCollection();
  4.      private object _listControl;
  5.  
  6.     _fc.ItemAdded += fc_ItemAdded;
  7.  
  8.      FacilityCollection property Items()
  9.      {
  10.            get
  11.            {
  12.                return _fc;
  13.            }
  14.            private set(FacilityCollection collection)
  15.            {
  16.                _fc = collection;
  17.            }
  18.     }
  19.     static void fc_ItemAdded(object sender, ListItemEventArgs e)
  20.     {
  21.         if !e.IsTransient
  22.         {
  23.             if AddItemToControl((FacilityItem)sender)
  24.             {
  25.                  //Do other work here
  26.             }
  27.         }
  28.     }
  29.  
  30.     private AddItemToControl(FacilityItem item)
  31.     {
  32.         bool success = false;
  33.         switch (typeof(item).ToString.ToLower())
  34.         {
  35.             case "ListType1":
  36.                  ListType1 _listObj = (ListType1)_listControl;
  37.                  _listObj.Add(item.ToListType1Item);
  38.                  success = true;
  39.                  break;
  40.             case "ListType2":
  41.                  ListType2 _listObj = (ListType2)_listControl;
  42.                  _listObj.Add(item.ToListType1Item);
  43.                  success = true;
  44.                  break;
  45.             case "ListType3":
  46.                  ListType3 _listObj = (ListType3)_listControl;
  47.                  _listObj.Add(item.ToListType1Item);
  48.                  success = true;
  49.                  break;
  50.             case "ListType4":
  51.                  ListType4 _listObj = (ListType4)_listControl;
  52.                  _listObj.Add(item.ToListType1Item);
  53.                  success = true;
  54.        }
  55.         return success;
  56.     }
  57. }
  58.  
  59. Class FacilityCollection
  60. {
  61.     private  List<FacilityItem> _contents;
  62.      
  63.     public event ItemAdded(object sender, ListItemEventArgs e);
  64.  
  65.     void Add(FacilityItem item)
  66.     {
  67.         _contents.Add(item) ;
  68.         ItemAdded(item, New ListItemEventArgs(!item.HasChanges && Validate(item.GUID)));
  69.     }
  70. }
  71.  

There may be a better way of dealing with what I am doing, but if there is, I don't see it.

I could do as you suggest and simply add the new FacilityItem directly to the _listControl from the FacilityManager, but I would have to create another function to do it .. and then I would still have to add the item to the FacilityCollection.

I suppose I could simply let FacilityCollection inherit List<FacilityItem> and then manipulate it as though FacilityCollection is nothing more than a List<FacilityItem> with some additional methods and properties that I define ... but I would still have to run the function to add the object to the control.

At least this way, when _contents is added to, FacilityManager knows about it and can do what it needs to do whenever the collection changes.

Unless I have completely missed the point ... and that wouldn't be unreasonable ...
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Creating a collection class
« Reply #23 on: July 12, 2013, 08:13:59 AM »

See if this makes sense:
Code - C#: [Select]
  1. Class FacilityManager
  2. {
  3.      private FacilityCollection _fc = New FacilityCollection();
  4.      private object _listControl;
  5.  
  6.     _fc.ItemAdded += fc_ItemAdded;
  7.  
  8.      FacilityCollection property Items()
  9.      {
  10.            get
  11.            {
  12.                return _fc;
  13.            }
  14.            private set(FacilityCollection collection)
  15.            {
  16.                _fc = collection;
  17.            }
  18.     }
  19.     static void fc_ItemAdded(object sender, ListItemEventArgs e)
  20.     {
  21.         if !e.IsTransient
  22.         {
  23.             if AddItemToControl((FacilityItem)sender)
  24.             {
  25.                  //Do other work here
  26.             }
  27.         }
  28.     }
  29.  
  30.     private AddItemToControl(FacilityItem item)
  31.     {
  32.         bool success = false;
  33.         switch (typeof(item).ToString.ToLower())
  34.         {
  35.             case "ListType1":
  36.                  ListType1 _listObj = (ListType1)_listControl;
  37.                  _listObj.Add(item.ToListType1Item);
  38.                  success = true;
  39.                  break;
  40.             case "ListType2":
  41.                  ListType2 _listObj = (ListType2)_listControl;
  42.                  _listObj.Add(item.ToListType1Item);
  43.                  success = true;
  44.                  break;
  45.             case "ListType3":
  46.                  ListType3 _listObj = (ListType3)_listControl;
  47.                  _listObj.Add(item.ToListType1Item);
  48.                  success = true;
  49.                  break;
  50.             case "ListType4":
  51.                  ListType4 _listObj = (ListType4)_listControl;
  52.                  _listObj.Add(item.ToListType1Item);
  53.                  success = true;
  54.        }
  55.         return success;
  56.     }
  57. }
  58.  
  59. Class FacilityCollection
  60. {
  61.     private  List<FacilityItem> _contents;
  62.      
  63.     public event ItemAdded(object sender, ListItemEventArgs e);
  64.  
  65.     void Add(FacilityItem item)
  66.     {
  67.         _contents.Add(item) ;
  68.         ItemAdded(item, New ListItemEventArgs(!item.HasChanges && Validate(item.GUID)));
  69.     }
  70. }
  71.  

There may be a better way of dealing with what I am doing, but if there is, I don't see it.

I could do as you suggest and simply add the new FacilityItem directly to the _listControl from the FacilityManager, but I would have to create another function to do it .. and then I would still have to add the item to the FacilityCollection.

I suppose I could simply let FacilityCollection inherit List<FacilityItem> and then manipulate it as though FacilityCollection is nothing more than a List<FacilityItem> with some additional methods and properties that I define ... but I would still have to run the function to add the object to the control.

At least this way, when _contents is added to, FacilityManager knows about it and can do what it needs to do whenever the collection changes.

Unless I have completely missed the point ... and that wouldn't be unreasonable ...

This looks different than what I commented on before but this is how I would write this.

Code - C#: [Select]
  1. public class FacilityManager
  2. {
  3.     private var FacilityCollection = new List<FacilityItem>();
  4.     private var _listControl = new List<object>();
  5.  
  6.     public AddItemToControl(FacilityItem item)
  7.     {
  8.         FacilityCollection.Add(item);
  9.  
  10.         bool success = false;
  11.         switch (typeof(item).ToString.ToLower())
  12.         {
  13.             case "listtype1":
  14.                  _listControl.Add(item.ToListType1Item);
  15.                  success = true;
  16.                  break;
  17.             case "listtype2":
  18.                  _listControl.Add(item.ToListType1Item);
  19.                  success = true;
  20.                  break;
  21.             case "listtype3":
  22.                  _listControl.Add(item.ToListType1Item);
  23.                  success = true;
  24.                  break;
  25.             case "listtype4":
  26.                  _listControl.Add(item.ToListType1Item);
  27.                  success = true;
  28.        }
  29.        
  30.         If (success)
  31.         {
  32.              //Do other work here
  33.          }
  34.     }
  35. }
  36.  

Call it like this
Code - C#: [Select]
  1. var fc = new FacilityManager();
  2. var item = new FacilityItem();
  3. fc.AddItemToControl(item);
  4.  

No need to create another class just to hold a List<T> with no added functionality so you could ditch FacilityCollection.  If your needing to Get FacilityCollection just expose it as a Property instead of a private member.  The entire method AddItemToControl looks suspicious but without knowing what your doing with the results its hard to suggest another way of doing it. Programming is like skinning cats this is just how I would do it.
Revit 2019, AMEP 2019 64bit Win 10

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Creating a collection class
« Reply #24 on: July 13, 2013, 10:45:48 AM »
I see what you are talking about. However, the underlying issue is that there are at least 17 different scenarios where the collection of objects is to be used is other classes.

Also, I don't know if you did it intentionally, but the _listControl isn't a List<object> and I'm pretty sure it wouldn't work in my application.

For most of the objects where we are showing the data, there is no implied casting. The controls that we are using won't work in that manner, so our objects must be cast to the object type needed for the control (in this case the FacilityItem object). For example, when we use a checked treeview, we have to utilize the ToCheckableTreeNode method of FacilityItem ... and inside FacilityItem there is other magic that goes on as well. It simplifies the task of populating a myriad of different objects with the data when the object type being populated is not known at design time.

One of the things I didn't talk about in my last couple posts is that the Add method isn't the only way to add objects to the FacilityManager collection of FacilityItem. When the FacilityManager is instantiated, it loads all of the items from the database that meet the criteria passed in the constructor. Those items, as well as any user created items, are displayed in a control ... which control and which type of control is entirely dependent upon the page the user is viewing. In some pages the objects in the manager are displayed in multiple locations because a property in one object may have a loose connection with another object. That is figured out in the code and displayed differently for the user.

I know that I can add a List<FacilityItem> to the FacilityManager class, however, because that collection has other internal methods (I've shown only a simplified version here) and the collection needs to be modular.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Creating a collection class
« Reply #25 on: July 18, 2013, 03:45:57 PM »
I've rethought my collection class and I can see where it would make sense to just implement List<T> in my manager class, however, today I ran across a problem that seems to beg for a collection. Is there any way to get around it other than what feels like a clumsy approach?

This is the basic class info:
Code - C#: [Select]
  1. public class StaffManager : List<StaffItem>
  2. {
  3.       pubic overrides string ToString()
  4.       {
  5.            stringbuilder sb = new stringbuilder();
  6.            for (int i = 0; i < this.Items.Count ; i++)
  7.            {
  8.                     if (i != 0)
  9.                     {
  10.                          sb.append(", ");
  11.                     }
  12.                     sb.append(this.Item(i).ToString());
  13.            }
  14.            return sb.ToString();
  15.       }
  16. }
  17.  
  18. public class StaffItem
  19. {
  20.      private string _longName;
  21.      private List<DisciplineItem> _disciplines;
  22.  
  23.      public overrides string ToString()
  24.      {
  25.          return _longName();
  26.      }
  27.  
  28.      public List<DisciplineItem> Disciplines()
  29.      {
  30.            get
  31.            {
  32.                  return _disciplines;
  33.            }
  34.      }
  35.  
  36.     public string DisciplinesAsString()
  37.    {
  38.          get
  39.         {
  40.            stringbuilder sb = new stringbuilder();
  41.            for (int i = 0; i < _disciplines.Count ; i++)
  42.            {
  43.                     if (i != 0)
  44.                     {
  45.                          sb.append(", ");
  46.                     }
  47.                     sb.append(this.Item(i).ToString());
  48.            }
  49.            return sb.ToString();
  50.         }
  51.     }
  52. }

What is at issue is that I need to return a delimited string containing all of the certified disciplines for a staff member.

I could just get the List<DisciplineItem> object and iterate through it in the code, but I will be doing this multiple times. It seems logical to house it in the object itself. Placing the code inside the StaffItem makes a little more sense because I only have to write the code once, where it applies to a staff member. However, multiple classes will have a List<DisciplineItem> and I would have to write code in each of them to do the exact same thing.

In this case, I think it makes sense to create a collection class:
Code - C#: [Select]
  1. public class DisciplineCollection : List<DisciplineItem>
  2. {
  3.       pubic overrides string ToString()
  4.       {
  5.            stringbuilder sb = new stringbuilder();
  6.            for (int i = 0; i < this.Items.Count ; i++)
  7.            {
  8.                     if (i != 0)
  9.                     {
  10.                          sb.append(", ");
  11.                     }
  12.                     sb.append(this.Item(i).ToString());
  13.            }
  14.            return sb.ToString();
  15.       }
  16. }

Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Creating a collection class
« Reply #26 on: July 19, 2013, 07:53:54 AM »
You can Join any IEnumerable like so.

Code - C#: [Select]
  1. var Items = new List<Item>();
  2. var joinedString = string.Join(", ", Items);
  3.  

That does the exact same thing as your override ToString().

Revised to use Items instead of StafffItem.
« Last Edit: July 19, 2013, 08:03:04 AM by MexicanCustard »
Revit 2019, AMEP 2019 64bit Win 10

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Creating a collection class
« Reply #27 on: July 19, 2013, 08:46:23 AM »
:shock:
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

CADDOG

  • Newt
  • Posts: 82
  • wishbonesr
Re: Creating a collection class
« Reply #28 on: July 22, 2013, 10:21:23 PM »
Custard's example needs to be further qualified, as that process will only work on List<string>.
Code: [Select]
string.join(", ", Items.Select(str => str.[strprop]))
...otherwise you might get something like namespace.FacilityItem, namespace.FacilityItem... due to System.Object.ToString() that you must override.

Food for thought.
http://msdn.microsoft.com/en-us/ms182142.aspx

As Custard said
Quote from: Custard
skinning cats

Here's some psudo of what it might look like with collectionbase.  Making many assumptions though. I just picked one of the versions posted.
NOTE:  I wouldn't put the GetDisciplineList, rather just implement a ToString() override in the FacilityItem.  Appropriately using LINQ on the outside of the class is sufficient.
Code: [Select]
public class FacilityItem
{
private string name;
public string Name
{ get {return name;}
set {name = value;}
}
private string discipline;
public string Discipline
{ get { return discipline;}
  set { discipline = value;}
}
public FacilityItem()
{}
public FacilityItem(string facilityName)
{ name = facilityName;}
}
public class Facility : CollectionBase
{
#region properties
public string GetDisciplineList()
{
return string.Join(", ", List.OfType<FacilityItem>().ToList().Select(str => str.Name.ToString()));
}

//enable int indexing
public FacilityItem this[int itemIndex]
{
get { return (FacilityItem)List[itemIndex]; }
set { List[itemIndex] = value; }
}
//enable string indexing
public FacilityItem this[string itemIndex]
{
get
{
foreach (FacilityItem item in List)
{
if (item.Name == itemIndex)
return item;
}
return null;
}
}
#endregion

#region initialization methods
//no facilityitem needed, string or otherwise
public FacilityList()
{}
//usefull in new from cloning
public FacilityList(IEnumerable<FacilityItem> initialItems)
{
foreach (FacilityItem item in initialItems)
Add(item);
}
//alternative, usefull if creating several facilities the same
public FacilityList(IEnumerable<string> initialItems)
{
foreach (string item in initialItems)
List.Add(item);
}
//single initializer for facilitylist
public FacilityList(string initialItem)
{
List.Add(initialItem);
}
#endregion

#region maintenance overrides
//strongly typed maintenance make API usable
public void Add(FacilityItem newItem)
{ List.Add(newItem); }
public void Add(string newItem)
{ List.Add(new FacilityItem(newItem)); }
public FacilityItem Add(string newItem)
{
FacilityItem it = new FacilityItem(newItem);
List.Add(it);
return it;
}
public void Remove(FacilityItem oldItem)
{ List.Remove(oldItem); }
public void Remove(string oldItem)
{ List.Remove(oldItem); }
//deep cloning could go here if needed
#endregion
}

Here's an example of a Student->Grades->Grade heirarchal class..  Again, something you might find usefull in class design.

Code: [Select]
public class Grade
{
private int score;
public int Score
{
get
{
return score;
}
set
{
if (value <= 100 && value >= 0)
score = value;
else
throw (new ArgumentOutOfRangeException("Score", value,
"Please enter a percentage based grade [0~100]"));
}
}

//public Grade(Grade newGrade)
//{
//    score = newGrade.Score;
//}
//private Grade()
//{
//}
public override string ToString()
{
return Score.ToString();
}
}

public class Grades : CollectionBase
{
public double Average()
{
int tally = 0;
foreach (Grade storedGrades in List)
{
tally += storedGrades.Score;
}
return Convert.ToDouble(tally) / List.Count;
}
public void Add(Grade newGrade)
{
List.Add(newGrade);
}
public void Remove(Grade oldGrade)
{
List.Remove(oldGrade);
}
public Grade this[int gradeIndex]
{
get
{
return (Grade)List[gradeIndex];
}
set
{
List[gradeIndex] = value;
}
}
}

public class Student
{
private Grades grades = new Grades();  //not sure if the Grades collection should be a property?
//private Grade[] grades;
private string studentName;
//public readonly string studentName;
public Grades Grades
{
get
{
return this.grades;
}
}
public string StudentName
{
get
{
return studentName;
}
set
{
StudentName = studentName;
}
}

public Student(string newStudentName)
{
studentName = newStudentName;

}
private Student()
{
}
}

EDIT1: c# formatting = no scroll bars
EDIT2: Apparently System.Collections.CollectionBase was superceded by Generics when they were introduced in 2.0  eecch.  I took a shot at it, but I have a little to learn about them.  I attempted ICollectionT...  attempted.
« Last Edit: July 24, 2013, 09:51:10 PM by CADDOG »