I'm trying to create a custom control which has a collection of items, but nothing appears on design-time even if I rebuild the project.
(Parents) is custom control
How can I make it appear in the collection editor without losing it after rebuilding the project.
public class ItemsTest : UserControl
{
Parents parent;
ObjectCollections itm ;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ObjectCollections Items { get { return itm; } }
public ItemsTest ()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
itm = new ObjectCollections(this);
parent = new Parents();
}
}
public class ObjectCollections : CollectionBase
{
private List<Parents> _contents;
private ItemsTest itemstest = null;
public ObjectCollections(ItemsTest owner)
{
_contents = new List<Parents>();
this.itemstest = owner;
}
public int IndexOf(Parents item)
{
return _contents.IndexOf(item);
}
public void Insert(int index, Parents item)
{
_contents.Insert(index, item);
}
public void RemoveAt(int index)
{
_contents.RemoveAt(index);
}
public Parents this[int index]
{
get
{
return _contents[index];
}
set
{
_contents[index] = value;
}
}
public void Add(Parents item)
{
_contents.Add(item);
this.itemstest.Controls.Add(item);
this.itemstest.Invalidate();
}
public void Clear()
{
_contents.Clear();
}
public bool Contains(Parents item)
{
if (_contents.Contains(item))
return true;
return false;
}
public void CopyTo(Parents[] array, int arrayIndex)
{
for (int _oj = 0; _oj < Count; _oj++)
{
array.SetValue(_contents[_oj], arrayIndex++);
}
}
public int Count
{
get { return _contents.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(Parents item)
{
return _contents.Remove(item);
}
IEnumerator<Parents> IEnumerable<Parents>.GetEnumerator()
{
//some code
}
IEnumerator IEnumerable.GetEnumerator()
{
//some code
}
}
When I try to add items into the collection editor, rebuild the project, and reopen the collection editor nothing is shown in it.
Related
I have a set of (totally) ordered objects of class OrderedObject
public class OrderedObject {
public int ord; // ordinal number
}
They have a field ordof type int that is unique for each object and represents their ordinal number. Nonetheless the value of ord can vary for each object. I would like to store the objects in a Dictionary<int*, OrderedObject> such that the key (asterisk stands for pointer to int, not sure how to achieve that with C#, but this is NOT my question) points to the ordinal number of the value object and changes automatically with the latter. Is that somehow possible?
For those who think my undertaking makes little sense: The background is that for now the ordinal number is a natural number represented by an integer, but later it will be replaced by another OrderedObject where ordering works slightly different from ordering the natural numbers.
Note that a key in any dictionary has to be unique so you wont be able to swap by changing one key after the other. in this case you have to implement this differently. but as long as you have unique keys at all times you can do the following:
class ReorderingDictionary : IDictionary<int, OrderItem>
{
private SortedList<int, OrderItem> sortedList = new SortedList<int, OrderItem>();
public void Add(int key, OrderItem value)
{
value.IneedToBeReordered += value_IneedToBeReordered;
sortedList.Add(key, value);
}
void value_IneedToBeReordered(object sender, ReOrderMeEventArgs e)
{
sortedList.Remove(e.OldKey);
OrderItem item = (OrderItem)sender;
sortedList.Add(item.Ord, item);
}
public bool ContainsKey(int key)
{
return sortedList.ContainsKey(key);
}
public ICollection<int> Keys
{
get { return sortedList.Keys; }
}
public bool Remove(int key)
{
return sortedList.Remove(key);
}
public bool TryGetValue(int key, out OrderItem value)
{
return sortedList.TryGetValue(key, out value);
}
public ICollection<OrderItem> Values
{
get { return sortedList.Values; }
}
public OrderItem this[int key]
{
get
{
return sortedList[key];
}
set
{
sortedList[key] = value;
}
}
public void Add(KeyValuePair<int, OrderItem> item)
{
item.Value.IneedToBeReordered += value_IneedToBeReordered;
sortedList.Add(item.Key, item.Value);
}
public void Clear()
{
sortedList.Clear();
}
public bool Contains(KeyValuePair<int, OrderItem> item)
{
return sortedList.ContainsKey(item.Key) && sortedList[item.Key] == item.Value;
}
public void CopyTo(KeyValuePair<int, OrderItem>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { return sortedList.Count; }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool Remove(KeyValuePair<int, OrderItem> item)
{
return sortedList.Remove(item.Key);
}
public IEnumerator<KeyValuePair<int, OrderItem>> GetEnumerator()
{
return sortedList.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return sortedList.GetEnumerator();
}
}
class OrderItem
{
private int ord;
public int Ord
{
get { return ord; }
set
{
int oldKey = ord;
ord = value;
if (IneedToBeReordered != null)
{
IneedToBeReordered(this, new ReOrderMeEventArgs(oldKey));
}
}
}
public event EventHandler<ReOrderMeEventArgs> IneedToBeReordered;
}
class ReOrderMeEventArgs : EventArgs
{
public int OldKey { get; private set; }
public ReOrderMeEventArgs(int oldKey)
{
this.OldKey = oldKey;
}
}
I have an assignment to implement a linear linked list using generics in C# console application.
The class should also contain a print() method to print the elements of the list.
Conditions for Linear List are to be as a long type (CNodeLong) or String Type (CNodeString), both inherited from CNode with virtual function Print().
I have a problem implementing the printing method. I don't know where should it stand, and how to override it.
Here is my CNode class and CList class:
public class CNode<T>
{
private CNode<T> next;
private T item;
public CNode<T> Next
{
get { return next; }
set { next = value; }
}
public T Item
{
get { return item; }
set { item = value; }
}
public CNode(T item)
: this(item,null)
{
}
public CNode(T item, CNode<T> next)
{
this.item = item;
this.next = next;
}
}
class CList<T>
{
private CNode<T> first;
private CNode<T> last;
private int count;
public CNode<T> First
{
get { return first; }
}
public CNode<T> Last
{
get { return last; }
}
public int Count
{
get { return count; }
}
public CList(string strListName)
{
count = 0;
first = last = null;
}
}
You probably should override ToString method and add the virtual Print method to CNode.
(I've also added the PrintList method to CList):
public class CNode<T>
{
...
public override string ToString()
{
return item.ToString();
}
virtual public void Print()
{
Console.WriteLine(item);
}
}
class CList<T>
{
...
public void PrintList()
{
CNode<T> current = first;
while (current != null)
{
Console.WriteLine(current.ToString());
current = current.Next;
}
}
}
Then you can override the virtual method in the child classes:
public class CNodeString : CNode<string>
{
public CNodeString(string item) : base(item) { }
override public void Print()
{
Console.WriteLine("Printing from CNodeString");
base.Print();
}
}
public class CNodeLong : CNode<long>
{
public CNodeLong(long item) : base(item) { }
override public void Print()
{
Console.WriteLine("Printing from CNodeLong");
base.Print();
}
}
On several recommendations, I started to assign a DataSource to my DataGridView instead of using DataGridView.Rows.Add(...). This is convenient since my data source is already a big list which doesn't change (much). However, when I use the DataSource assignment, it becomes impossible to sort the columns.
class MyGridView : DataGridView
{
private List<Person> m_personList;
private class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string first, string last)
{
FirstName = first;
LastName = last;
}
}
public MyGridView()
{
/* ...initialise stuff... */
m_personList.Add(new Person("Kate", "Smith"));
m_personList.Add(new Person("Bill", "Davids"));
m_personList.Add(new Person("Ann", "Roth"));
this.DataSource = m_personList;
}
}
I also tried to replace List<Person> by BindingList<Person> and by BindingSource, but none of that seems to matter. I also tried adding a custom sorter:
this.SortCompare += MyGridView_SortCompare;
private void MyGridView_SortCompare(object sender, EventArgs e)
{
/* ...Compare method...*/
}
but the thing doesn't even get called. Is there some other way to enable sorting with a DataSource?
Note: Note that my DataSource is not (necessarily) an SQL one, but just any List.
You need to coerce your datasource into a list that supports sorting (IBindingList and IBindingListView), and then it will work out of the box. There are lots of examples on the web, this is the one I've used in the past:
// usage:
// var sortableList = new SortableList(m_personList);
// dgv.DataSource = m_sortableList;
/// <summary>
/// Suitable for binding to DataGridView when column sorting is required
/// </summary>
/// <typeparam name="T"></typeparam>
public class SortableList<T> : BindingList<T>, IBindingListView
{
private PropertyComparerCollection<T> sorts;
public SortableList()
{
}
public SortableList(IEnumerable<T> initialList)
{
foreach (T item in initialList)
{
this.Add(item);
}
}
public SortableList<T> ApplyFilter(Func<T, bool> func)
{
SortableList<T> newList = new SortableList<T>();
foreach (var item in this.Where(func))
{
newList.Add(item);
}
return newList;
}
protected override bool IsSortedCore
{
get { return this.sorts != null; }
}
protected override bool SupportsSortingCore
{
get { return true; }
}
protected override ListSortDirection SortDirectionCore
{
get
{
return this.sorts == null
? ListSortDirection.Ascending
: this.sorts.PrimaryDirection;
}
}
protected override PropertyDescriptor SortPropertyCore
{
get
{
return this.sorts == null ? null : this.sorts.PrimaryProperty;
}
}
public void ApplySort(ListSortDescriptionCollection
sortCollection)
{
bool oldRaise = RaiseListChangedEvents;
RaiseListChangedEvents = false;
try
{
PropertyComparerCollection<T> tmp
= new PropertyComparerCollection<T>(sortCollection);
List<T> items = new List<T>(this);
items.Sort(tmp);
int index = 0;
foreach (T item in items)
{
SetItem(index++, item);
}
this.sorts = tmp;
}
finally
{
RaiseListChangedEvents = oldRaise;
ResetBindings();
}
}
public bool Exists(Predicate<T> func)
{
return new List<T>(this).Exists(func);
}
string IBindingListView.Filter
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
void IBindingListView.RemoveFilter()
{
throw new NotImplementedException();
}
ListSortDescriptionCollection IBindingListView.SortDescriptions
{
get { return (this.sorts == null ? null : this.sorts.Sorts); }
}
bool IBindingListView.SupportsAdvancedSorting
{
get { return true; }
}
bool IBindingListView.SupportsFiltering
{
get { return false; }
}
protected override void RemoveSortCore()
{
this.sorts = null;
}
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
ListSortDescription[] arr = { new ListSortDescription(prop, direction) };
ApplySort(new ListSortDescriptionCollection(arr));
}
}
public class PropertyComparerCollection<T> : IComparer<T>
{
private readonly PropertyComparer<T>[] comparers;
private readonly ListSortDescriptionCollection sorts;
public PropertyComparerCollection(ListSortDescriptionCollection
sorts)
{
if (sorts == null)
{
throw new ArgumentNullException("sorts");
}
this.sorts = sorts;
List<PropertyComparer<T>> list = new
List<PropertyComparer<T>>();
foreach (ListSortDescription item in sorts)
{
list.Add(new PropertyComparer<T>(item.PropertyDescriptor,
item.SortDirection == ListSortDirection.Descending));
}
this.comparers = list.ToArray();
}
public ListSortDescriptionCollection Sorts
{
get { return this.sorts; }
}
public PropertyDescriptor PrimaryProperty
{
get
{
return this.comparers.Length == 0
? null
: this.comparers[0].Property;
}
}
public ListSortDirection PrimaryDirection
{
get
{
return this.comparers.Length == 0
? ListSortDirection.Ascending
: this.comparers[0].Descending
? ListSortDirection.Descending
: ListSortDirection.Ascending;
}
}
int IComparer<T>.Compare(T x, T y)
{
int result = 0;
foreach (PropertyComparer<T> t in this.comparers)
{
result = t.Compare(x, y);
if (result != 0)
{
break;
}
}
return result;
}
}
public class PropertyComparer<T> : IComparer<T>
{
private readonly bool descending;
private readonly PropertyDescriptor property;
public PropertyComparer(PropertyDescriptor property, bool descending)
{
if (property == null)
{
throw new ArgumentNullException("property");
}
this.descending = descending;
this.property = property;
}
public bool Descending
{
get { return this.descending; }
}
public PropertyDescriptor Property
{
get { return this.property; }
}
public int Compare(T x, T y)
{
int value = Comparer.Default.Compare(this.property.GetValue(x),
this.property.GetValue(y));
return this.descending ? -value : value;
}
}
An alternate is to convert your List into DataTable and then bind that DataTable to the DataGridView through BindingSource. This way your DataGridView will inherit the sort options available with the DataTable.
For converting a list to DataTable, refer the article:
How to fill a datatable with List<T>
Here is a Basic Class with TheProperty in question:
class BasicClass {
public BasicClass() {
TheProperty = new Object();
Stamped = DateTime.Now;
}
public object TheProperty { get; set; }
public DateTime Stamped { get; private set; }
}
Here is the Basic List:
class BasicList {
private List<BasicClass> list;
public BasicList() {
list = new List<BasicClass>();
}
public BasicClass this[object obj] {
get { return list.SingleOrDefault(o => o.TheProperty == obj); }
}
public void Add(BasicClass item) {
if (!Contains(item.TheProperty)) {
list.Add(item);
}
}
public bool Contains(object obj) {
return list.Any(o => o.TheProperty == obj); // Picked this little gem up yesterday!
}
public int Count { get { return list.Count; } }
}
I'd like to add a class to BasicList that will return an array of items.
I could write it like this, using traditional C#:
public object[] Properties() {
var props = new List<Object>(list.Count);
foreach (var item in list) {
props.Add(item.TheProperty);
}
return props.ToArray();
}
...but how would I write that using a LINQ or Lambda query?
return list.Select(p=>p.TheProperty).ToArray()
Let me provide a bit of a history as to how I've reached this point.
I originally had a Property within my class that derived from CollectionsBase and had this collection mapped to the PropertyGrid and the user could Add/Edit/Remove items from the list at will.
However, I couldn't map the CollectionsBase with NHibernate, thus I had to scrap my initial implementation and instead of deriving from CollectionsBase, I had the class derive from IList.
Now I can map to NHibernate, but I am unable to edit the collection via the PropertyGrid.
I need some help getting the 2 to play nice with each other.
In my main class I have a property defined as:
public virtual ZoneCollection Zones
{
get { return zones; }
set { zones = value; }
}
My Zone Collection that inherits IList is defined as follows:
public class ZoneCollection : IList<Zone>, ICustomTypeDescriptor
{
private IList<Zone> _list;
public IList<Zone> _List
{
get { return _list; }
}
public ZoneCollection()
{
_list = new List<Zone>();
}
#region Implementation of IEnumerable
public IEnumerator<Zone> GetEnumerator()
{
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Implementation of ICollection<Zone>
public void Add(Zone item)
{
_list.Add(item);
}
public void Clear()
{
_list.Clear();
}
public bool Contains(Zone item)
{
return _list.Contains(item);
}
public void CopyTo(Zone[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public bool Remove(Zone item)
{
return _list.Remove(item);
}
public int Count
{
get { return _list.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
#endregion
#region Implementation of IList<Zone>
public int IndexOf(Zone item)
{
return _list.IndexOf(item);
}
public void Insert(int index, Zone item)
{
_list.Insert(index, item);
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
public Zone this[int index]
{
get { return (Zone)_list[index]; }
set { _list[index] = value; }
}
#endregion
// Implementation of interface ICustomTypeDescriptor
#region ICustomTypeDescriptor impl
public String GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public String GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
/// <summary>
/// Called to get the properties of this type. Returns properties with certain
/// attributes. this restriction is not implemented here.
/// </summary>
/// <param name="attributes"></param>
/// <returns></returns>
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return GetProperties();
}
/// <summary>
/// Called to get the properties of this type.
/// </summary>
/// <returns></returns>
public PropertyDescriptorCollection GetProperties()
{
// Create a collection object to hold property descriptors
PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
// Iterate the list of zones
for (int i = 0; i < this._list.Count; i++)
{
// Create a property descriptor for the zone item and add to the property descriptor collection
ZoneCollectionPropertyDescriptor pd = new ZoneCollectionPropertyDescriptor(this, i);
pds.Add(pd);
}
// return the property descriptor collection
return pds;
}
#endregion
}
/// <summary>
/// Summary description for CollectionPropertyDescriptor.
/// </summary>
public class ZoneCollectionPropertyDescriptor : PropertyDescriptor
{
private ZoneCollection collection = null;
private int index = -1;
public ZoneCollectionPropertyDescriptor(ZoneCollection coll, int idx) :
base("#" + idx.ToString(), null)
{
this.collection = coll;
this.index = idx;
}
public override AttributeCollection Attributes
{
get
{
return new AttributeCollection(null);
}
}
public override bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get
{
return this.collection.GetType();
}
}
public override string DisplayName
{
get
{
Zone zone = this.collection[index];
return zone.ID.ToString();
}
}
public override string Description
{
get
{
Zone zone = this.collection[index];
StringBuilder sb = new StringBuilder();
sb.Append(zone.ID.ToString());
if (zone.Streets.Route != String.Empty || zone.Streets.Crossing != String.Empty)
sb.Append("::");
if (zone.Streets.Route != String.Empty)
sb.Append(zone.Streets.Route);
if (zone.Streets.Crossing != String.Empty)
{
sb.Append(" and ");
sb.Append(zone.Streets.Crossing);
}
return sb.ToString();
}
}
public override object GetValue(object component)
{
return this.collection[index];
}
public override bool IsReadOnly
{
get { return false; }
}
public override string Name
{
get { return "#" + index.ToString(); }
}
public override Type PropertyType
{
get { return this.collection[index].GetType(); }
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
public override void SetValue(object component, object value)
{
// this.collection[index] = value;
}
}
}
Now, my ICustomTypeDescriptor and PropertyDescriptor worked fine when this class derived from CollectionsBase, but now it just shows the class name ZoneCollection in the property name without the "..." button to add/edit/remove the items from the list.
What am I doing wrong now that it is inherited from IList that this isn't working?
If I add:
[TypeConverter(typeof(ExpandableObjectConverter))]
To the beginning of the ZoneCollection, I get the items in the list listed in an expandable tree, but that's not what I am looking for. Where did the "..." button go that opened up a popup window that enabled me to add/edit/remove the items in the collection when I inherited from IList instead of CollectionBase?
The PropertyGrid is an old grumpy beast. It needs the non-generic IList explicit implementation, not the generic one.
As a site note, you could derive ZoneCollection directly from List<Zone>, and you don't need any ICustomTypeDescriptor / PropertyDescriptor, with regards to this PropertyGrid issue.
Here is an implementation that seems to work:
public class ZoneCollection : IList<Zone>, IList
{
private List<Zone> _list = new List<Zone>();
public ZoneCollection()
{
}
public int IndexOf(Zone item)
{
return _list.IndexOf(item);
}
public void Insert(int index, Zone item)
{
_list.Insert(index, item);
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
public Zone this[int index]
{
get
{
return _list[index];
}
set
{
_list[index] = value;
}
}
public void Add(Zone item)
{
_list.Add(item);
}
public void Clear()
{
_list.Clear();
}
public bool Contains(Zone item)
{
return _list.Contains(item);
}
public void CopyTo(Zone[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _list.Count; }
}
public bool IsReadOnly
{
get { return ((IList)_list).IsReadOnly; }
}
public bool Remove(Zone item)
{
return _list.Remove(item);
}
public IEnumerator<Zone> GetEnumerator()
{
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
int IList.Add(object value)
{
int index = Count;
Add((Zone)value);
return index;
}
bool IList.Contains(object value)
{
return Contains((Zone)value);
}
int IList.IndexOf(object value)
{
return IndexOf((Zone)value);
}
void IList.Insert(int index, object value)
{
Insert(index, (Zone)value);
}
bool IList.IsFixedSize
{
get { return ((IList)_list).IsFixedSize; }
}
bool IList.IsReadOnly
{
get { return ((IList)_list).IsReadOnly; }
}
void IList.Remove(object value)
{
Remove((Zone)value);
}
object IList.this[int index]
{
get
{
return this[index];
}
set
{
this[index] = (Zone)value;
}
}
void ICollection.CopyTo(Array array, int index)
{
CopyTo((Zone[])array, index);
}
bool ICollection.IsSynchronized
{
get { return ((ICollection)_list).IsSynchronized; }
}
object ICollection.SyncRoot
{
get { return ((ICollection)_list).SyncRoot; }
}
}