Reputation: 9888
I am creating a custom control that inherits from ComboBox. I need to perform a custom action when a Item is added to the ComboBox. It doesn't matter if it's C# or Vb.NET, but I don't know how to do it.
Looks like it's not worth the effort to modify everything to detect it, as seen here:
Event to detect item added to ComboBox
So maybe is possible to create my custom .Add();
method, but then I need to hide the Items
property so the control doesn't allow two different method of adding and only one working.
I tried using things in this thread but I didn't manage to make it work. For example, I tried putting the overloaded property private
, but didn't change anything, as seen in this example in Vb.NET:
Private Overloads ReadOnly Property Items() As ObjectCollection
'...
Can it be done?
Upvotes: 1
Views: 1441
Reputation: 4489
MyComboBox acts as normal combobox (by overwriting Item property) with extended events (ItemAdded, ItemRemoved)
Sample Usage:
MyComboBox1.Items.Add("0")
MyComboBox1.Items.AddRange(New String() {"1", "2", "3", "55"})
MyComboBox1.Items.Remove("55")
MyComboBox1.Items.RemoveAt(0)
Debug.WriteLine(MyComboBox1.Items.IndexOf("2")) '2
Debug.WriteLine(MyComboBox1.Items.IndexOf("55")) '-1
MyComboBox1.Items.Clear()
VB.NET Code:
Public Class MyComboBox
Inherits ComboBox
Public Event ItemAdded As EventHandler
'Public Event ItemsAdded As EventHandler
Public Event ItemRemoved As EventHandler
'Public Event ItemInserted(sender As Object, insertedIndex As Integer, e As EventArgs)
Private ItemContainer_ As ItemContainer
Sub New()
ItemContainer_ = New ItemContainer(Me)
AddHandler ItemContainer_.ItemAdded, AddressOf ItemContainer_ItemAdded
'AddHandler ItemContainer_.ItemsAdded, AddressOf ItemContainer_ItemsAdded
AddHandler ItemContainer_.ItemRemoved, AddressOf ItemContainer_ItemRemoved
'AddHandler ItemContainer_.ItemInserted, AddressOf ItemContainer_ItemInserted
End Sub
Private Sub ItemContainer_ItemAdded(sender As Object, e As EventArgs)
RaiseEvent ItemAdded(Me, e)
End Sub
'Private Sub ItemContainer_ItemsAdded(sender As Object, e As EventArgs)
' RaiseEvent ItemsAdded(Me, e)
'End Sub
Private Sub ItemContainer_ItemRemoved(sender As Object, e As EventArgs)
RaiseEvent ItemRemoved(Me, e)
End Sub
'Private Sub ItemContainer_ItemInserted(sender As Object, insertedIndex As Integer, e As EventArgs)
' RaiseEvent ItemInserted(Me, insertedIndex, e)
'End Sub
Public Shadows ReadOnly Property Items As ItemContainer
Get
Return ItemContainer_
End Get
End Property
Public Shadows ReadOnly Property Items(index As Integer) As Object
Get
Return ItemContainer_.Item(index)
End Get
End Property
Public Class ItemContainer
Inherits System.Windows.Forms.ComboBox.ObjectCollection
Public Event ItemAdded As EventHandler
'Public Event ItemsAdded As EventHandler
Public Event ItemRemoved As EventHandler
'Public Event ItemInserted(sender As Object, insertedIndex As Integer, e As EventArgs)
Private owner_ As ComboBox
Sub New(owner As ComboBox)
MyBase.New(owner)
owner_ = owner
End Sub
Public Overloads Sub Add(item As Object)
owner_.Items.Add(item)
RaiseEvent ItemAdded(Me, New EventArgs)
End Sub
Public Overloads Sub AddRange(item() As Object)
owner_.Items.AddRange(item)
'RaiseEvent ItemsAdded(Me, New EventArgs)
RaiseEvent ItemAdded(Me, New EventArgs)
End Sub
Public Overloads Sub Insert(index As Integer, item As Object)
owner_.Items.Insert(index, item)
'RaiseEvent ItemInserted(Me, index, New EventArgs)
RaiseEvent ItemAdded(Me, New EventArgs)
End Sub
Public Overloads Sub Remove(item As Object)
owner_.Items.Remove(item)
RaiseEvent ItemRemoved(Me, New EventArgs)
End Sub
Public Overloads Sub RemoveAt(index As Integer)
owner_.Items.RemoveAt(index)
RaiseEvent ItemRemoved(Me, New EventArgs)
End Sub
Public Overloads Sub Clear()
owner_.Items.Clear()
RaiseEvent ItemRemoved(Me, New EventArgs)
End Sub
Public Overloads Function IndexOf(value As Object) As Integer
Return owner_.Items.IndexOf(value)
End Function
Public Overloads Function Contains(value As Object) As Boolean
Return owner_.Items.Contains(value)
End Function
Public Overloads Function GetHashCode() As Integer
Return owner_.Items.GetHashCode
End Function
Public Overloads Function ToString() As String
Return owner_.Items.ToString
End Function
Public Overloads Function GetEnumerator() As System.Collections.IEnumerator
Return owner_.Items.GetEnumerator
End Function
Public Overloads Function Equals(obj As Object) As Boolean
Return owner_.Items.Equals(obj)
End Function
Public Overloads Function Equals(objA As Object, objB As Object) As Boolean
Return Object.Equals(objA, objB)
End Function
Public Overloads Sub CopyTo(Destination() As Object, arrayIndex As Integer)
owner_.Items.CopyTo(Destination, arrayIndex)
End Sub
Public Overloads ReadOnly Property Count As Integer
Get
Return owner_.Items.Count
End Get
End Property
Public Overloads ReadOnly Property IsReadOnly As Boolean
Get
Return owner_.Items.IsReadOnly
End Get
End Property
Public Overloads ReadOnly Property Item(index As Integer) As Object
Get
Return owner_.Items.Item(index)
End Get
End Property
End Class
End Class
C# Code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class MyComboBox : System.Windows.Forms.ComboBox
{
public event EventHandler ItemAdded;
//public event EventHandler ItemsAdded;
public event EventHandler ItemRemoved;
public event ItemInsertedEventHandler ItemInserted;
public delegate void ItemInsertedEventHandler(object sender, int insertedIndex, EventArgs e);
private ItemContainer ItemContainer_;
public MyComboBox()
{
ItemContainer_ = new ItemContainer(this);
ItemContainer_.ItemAdded += ItemContainer_ItemAdded;
//ItemContainer_.ItemsAdded += ItemContainer_ItemsAdded;
ItemContainer_.ItemRemoved += ItemContainer_ItemRemoved;
ItemContainer_.ItemInserted += ItemContainer_ItemInserted;
}
private void ItemContainer_ItemAdded(object sender, EventArgs e)
{
if (ItemAdded != null)
{
ItemAdded(this, e);
}
}
/*private void ItemContainer_ItemsAdded(object sender, EventArgs e)
{
if (ItemsAdded != null)
{
ItemsAdded(this, e);
}
}*/
private void ItemContainer_ItemRemoved(object sender, EventArgs e)
{
if (ItemRemoved != null)
{
ItemRemoved(this, e);
}
}
private void ItemContainer_ItemInserted(object sender, int insertedIndex, EventArgs e)
{
if (ItemInserted != null)
{
ItemInserted(this, insertedIndex, e);
}
}
public new ItemContainer Items
{
get { return ItemContainer_; }
}
public class ItemContainer : System.Windows.Forms.ComboBox.ObjectCollection
{
public event EventHandler ItemAdded;
//public event EventHandler ItemsAdded;
public event EventHandler ItemRemoved;
public event ItemInsertedEventHandler ItemInserted;
public delegate void ItemInsertedEventHandler(object sender, int insertedIndex, EventArgs e);
private System.Windows.Forms.ComboBox owner_;
public ItemContainer(System.Windows.Forms.ComboBox owner)
: base(owner)
{
owner_ = owner;
}
public new void Add(object item)
{
owner_.Items.Add(item);
if (ItemAdded != null)
{
ItemAdded(this, new EventArgs());
}
}
public new void AddRange(object[] item)
{
owner_.Items.AddRange(item);
/*if (ItemsAdded != null)
{
ItemsAdded(this, new EventArgs());
}*/
if (ItemAdded != null)
{
ItemAdded(this, new EventArgs());
}
}
public new void Insert(int index, object item)
{
owner_.Items.Insert(index, item);
if (ItemInserted != null)
{
ItemInserted(this, index, new EventArgs());
}
}
public new void Remove(object item)
{
owner_.Items.Remove(item);
if (ItemRemoved != null)
{
ItemRemoved(this, new EventArgs());
}
}
public new void RemoveAt(int index)
{
owner_.Items.RemoveAt(index);
if (ItemRemoved != null)
{
ItemRemoved(this, new EventArgs());
}
}
public new void Clear()
{
owner_.Items.Clear();
if (ItemRemoved != null)
{
ItemRemoved(this, new EventArgs());
}
}
public new int IndexOf(object value)
{
return owner_.Items.IndexOf(value);
}
public new bool Contains(object value)
{
return owner_.Items.Contains(value);
}
public new int GetHashCode()
{
return owner_.Items.GetHashCode();
}
public new string ToString()
{
return owner_.Items.ToString();
}
public new System.Collections.IEnumerator GetEnumerator()
{
return owner_.Items.GetEnumerator();
}
public new bool Equals(object obj)
{
return owner_.Items.Equals(obj);
}
public new bool Equals(object objA, object objB)
{
return object.Equals(objA, objB);
}
public new void CopyTo(object[] Destination, int arrayIndex)
{
owner_.Items.CopyTo(Destination, arrayIndex);
}
public new int Count
{
get { return owner_.Items.Count; }
}
public new object this[int index]
{
get { return owner_.Items[index]; }
}
public new bool IsReadOnly
{
get { return owner_.Items.IsReadOnly; }
}
}
}
Upvotes: 1
Reputation: 54453
Yes and no.
Yes you can hide the original Items collection and replace it by something else.
However your new 'custom' Items won't be able to work; it is always null - at least that's what I found. Maybe somebody can jump in with details of the why..?
Which is why I replace it with something rather useless - a naked object.
What you can do is this: You can expose the original Items in a man-in-the-middle class and use it in your custom ComboBox class. You could e.g. name the exposed class Items_ and create a class to replace the Items in the final inheritance level. This class needs to implement all methods and properties you want your custom class's consumers to have.
I have named to custom Items 'items' and implemented one method and one property. (For non-case sensitive VB maybe Itemz would be better ;-)
Here is the minimal man-in-the-middle :
[ToolboxItem(false)]
public class mimComboBox : ComboBox
{
public ObjectCollection items_;
public mimComboBox()
{
items_ = Items;
}
}
And here is a custom ComboBox:
public class myComboBox : mimComboBox
{
public myComboBox()
{
Items = new object(); // (1)
items = new myItems(this);
}
new public object Items { get; set; } // this hides the real Items (2)
public myItems items { get; set; } // this is the custom property
public class myItems // the custom Items class with all necessary methods etc..
{
public myItems( myComboBox parent )
{
parentCB = parent; // reference to the outer class
}
private mimComboBox parentCB; // the man-in-the-middle
// all your methods..
new public int Add(object o) // one example of a custom method
{
// add your item-added event here
return parentCB.items_.Add(o);
}
// one of many many properties to provide..
public int Count { get { return parentCB.items_.Count; } }
}
}
This is an example how the ComboBox can be used:
private void button1_Click(object sender, EventArgs e)
{
myComboBox1.items.Add("uiuiuiiui");
// myComboBox1.Items.Add("sadsds"); // <== this doesn't compile! (3)
button1.Text = myComboBox1.items.Count.ToString();
}
This sounds like quite some work!!
(Maybe the man-in-the-middle class is not necessary..? I'd like to learn about that..!)
Edit: I have changed a few details according to the link in Plutonix' comment.
Notes:
As noted, the original question is better solved by listening to the ComboBox system messages
I still wonder why this happens:
When I replace (1) & (2) by
Items = new myItems(this);
new public myItems Items { get; set; }
then (3) will compile just fine. However upon runtime it throws a null object reference error.
Is this a belated effect of the missing Overridable attribute? @Jon Skeet to the rescue! ;-)
Upvotes: 2