exposing the combobox Items property in a usercontrol - c#

I have a user control that contains a combobox. I want to be able to edit the Items property for the combo box but im not really sure how to do that. I've tried adding the Items property to my user control class but im not sure what the value is thats returned when you set the property in the properties menu of visual studio. I have the property setup like this:
[Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design",
"System.Drawing.Design.UITypeEditor, System.Drawing")]
public ComboBox.ObjectCollection Items
{
get
{
return this.comboBox.Items;
}
set
{
this.comboBox.Items.Add(value);
}
}

Wrap the Items property of your UserControl's ComboBox in a property like this:
[Description("The items in the UserControl's ComboBox."),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design", typeof(System.Drawing.Design.UITypeEditor))]
public System.Windows.Forms.ComboBox.ObjectCollection MyItems {
get {
return comboBox1.Items;
}
}
The EditorAttribute in the property specifies the UI element used for changing the property in the designer.

Try adding two methods for adding and removing ComboBox items:
public void AddItem(Object item)
{
this.comboBox.Items.Add(item);
}
public void RemoveItem(Object item)
{
this.comboBox.Items.Remove(item);
}

Related

ICollectionView adds filter to ObservableCollection

I'm having problem with with my WPF application, where the search filter is applied to the observablecollection, when I add a filter to the ICollectionView.
I got two views which two separate viewmodels. In this case, one view allows you to search on a collection and manipulate it, and the second view has a combobox which allows the user to choose an item from the collection.
At first, I'm retrieving the items to my observablecollection as you can see in the code under. Then I'm setting the CollectionViewSource. As now, I'm adding filter to the CollectionView, which is a search I've implemented. My problem is that I thought that the filter would only apply to the ICollectionView collection, which I'm using in the listbox, but it shows out that it also applies to the ObservableCollection. The listbox is using the CollectionView and the combobox is using the ObservableCollection of the categories. But I don't want the filter to be applied to the combobox collection, which uses the observablecolelction, as I want to show all the available items all the time.
How can I fix this?
public ViewModel ()
{
CollectionViewSource.GetDefaultView(Categories);
}
public ObservableCollection<Category> Categories
{
get
{
return this._categories;
}
set
{
if (this._categories!= value)
{
this._categories= value;
this.OnPropertyChanged("Categories");
}
}
}
private ICollectionView _categoriesCollection;
public ICollectionView CategoriesCollection
{
get
{
return this._categoriesCollection;
}
set
{
if (this._categoriesCollection!= value)
{
this._categoriesCollection= value;
this.OnPropertyChanged("CategoriesCollection");
}
}
}
You are binding to the same view: Should I bind to ICollectionView or ObservableCollection
Instead of setting your CategoriesCollection property to the return value of CollectionViewSource.GetDefaultView(_categories), you could create a new view to "fix" this:
CategoriesCollection = new ListCollectionView(_categories);

Make ComboBox accept only specific type

I currently have a ComboBox in my Windows Forms Application. In order to specify which values the ComboBox will contain, I set DataSource property of the ComboBox to some array so that ComboBox contains values from that array. I could also use Items.Add() to add new values to ComboBox. However, I want to make sure that ComboBox can be populated with objects of some specific type. So, if I have a class called X, then I want to make it so that only an array of type X can be used as a data source for the ComboBox. Right now, ComboBox accepts objects of type System.Object. How can I achieve it? Is there a property of ComboBox that I need to set to be equal to my data type's name? Or is there an event that will check whether an object added to my ComboBox is of the needed type and will throw an exception if not?
I was thinking of creating a new class as a subtype of ComboBox, and overriding the Add method of Items property so that Add checks whether its argument is of the needed type (not sure if and how I can do it). Even if I do that, there are still other ways to add new values into ComboBox (AddRange, CopyTo, etc.), so I think there should be a more elegant solution to this problem.
If you want to control the type of item that the ComboBox can contain, you could try creating a new class derived form ComboBox, but you'd run into the problem that it still has the ComboBox.ObjectCollection Items property which would still accept any type! And (unfortunately for your idea of overriding) the Add method isn't virtual.
The only practical solution that I could think of would be to abstract the ComboBox somehow. If this isn't shared code, I would recommend just creating a method that you would use to add items to the ComboBox. Something like:
// NOTE: All items that are added to comboBox1 need to be of type `SomeType`.
private void AddItemToComboBox(SomeType item)
{
comboBox1.Items.Add(item);
}
Any attempt to add a non-SomeType object to the ComboBox would be met with a compiler error. Unfortunately, there's no easy way to prevent someone from still adding a non-SomeType item to ComboBox.Items directly.
Again, if this isn't shared code, it shouldn't really be an issue.
You can hide Items property by your
own Items property of custom type which taking as parameter original ItemsCollection
Example class for testing
public class Order
{
public Int32 ID { get; set; }
public string Reference { get; set; }
public Order() { }
public Order(Int32 inID, string inReference)
{
this.ID = inID;
this.Reference = (inReference == null) ? string.Empty : inReference;
}
//Very important
//Because ComboBox using .ToString method for showing Items in the list
public override string ToString()
{
return this.Reference;
}
}
With next class I tried wrap ComboBox's items collection in own type.
Where adding items must be concrete type
Here you can add other methods/properties you need (Remove)
public class ComboBoxList<TCustomType>
{
private System.Windows.Forms.ComboBox.ObjectCollection _baseList;
public ComboBoxList(System.Windows.Forms.ComboBox.ObjectCollection baseItems)
{
_baseList = baseItems;
}
public TCustomType this[Int32 index]
{
get { return (TCustomType)_baseList[index]; }
set { _baseList[index] = value; }
}
public void Add(TCustomType item)
{
_baseList.Add(item);
}
public Int32 Count { get { return _baseList.Count; } }
}
Here custom combobox class derived from ComboBox
Added: generic type
public class ComboBoxCustomType<TCustomType> : System.Windows.Forms.ComboBox
{
//Hide base.Items property by our wrapping class
public new ComboBoxList<TCustomType> Items;
public ComboBoxCustomType() : base()
{
this.Items = new ComboBoxList<TCustomType>(base.Items);
}
public new TCustomType SelectedItem
{
get { return (TCustomType)base.SelectedItem; }
}
}
Next code used in the Form
private ComboBoxCustomType<Order> _cmbCustom;
//this method used in constructor of the Form
private void ComboBoxCustomType_Initialize()
{
_cmbCustom = new ComboBoxCustomType<Order>();
_cmbCustom.Location = new Point(100, 20);
_cmbCustom.Visible = true;
_cmbCustom.DropDownStyle = ComboBoxStyle.DropDownList;
_cmbCustom.Items.Add(new Order(0, " - nothing - "));
_cmbCustom.Items.Add(new Order(1, "One"));
_cmbCustom.Items.Add(new Order(2, "Three"));
_cmbCustom.Items.Add(new Order(3, "Four"));
_cmbCustom.SelectedIndex = 0;
this.Controls.Add(_cmbCustom);
}
Instead of overriding ComboBox (which wont work as stated in itsme86's answer) you could override usercontrol, add a combobox to this, and then only expose the elements that you wish to work with. Something similar to
public partial class MyComboBox<T> : UserControl where T: class
{
public MyComboBox()
{
InitializeComponent();
}
public void Add(T item)
{
comboBox1.Items.Add(item);
}
public IEnumerable<T> Items
{
get { return comboBox1.Items.Cast<T>(); }
}
}
Please note however that some pieces of automated software rely on access the the underlying controls however so this may cause some issues.
This approach never changes the Items of the combobox so they will still store as objects but when you access them, you are casting them to the correct type and only allowing them to be added of that type. You can create a new combobox via
var myCB = new MyComboBox<ItemClass>();

How to notify the UI that a change has taken place in one or several of a List's members

I have the following problem:
I am adding an element to a favorites list (List<Item>) through a ContextMenu. Each Item has a derived property IsFavorite that changes depending on the favorites list - like so:
public bool IsFavorite
{
get { return ItemController.FavoriteList.Contains( this ); }
}
When I add something (or delete it) from the ContextMenu, the ContextMenu must be immediately updated.
Now, I know this is possible through using an ObservableCollection, but due to a few factors out of my control, I must make due with List objects. Now, is there any way I can get this to refresh?
public void DeleteFromFavorites(Item item)
{
Item itemInMainList = MainList.First(item);
itemInMainList.Refresh();
}
Item.cs:
public bool IsFavorite
{
get { return ItemController.FavoriteList.Contains( this ); }
}
public void Refresh()
{
NotifyPropertyChanged("IsFavorite");
}

Listbox text not updating

I have a listbox that the text is not updating when I change the underlying objects properties:
ClassA testObj = new ClassA(){name="myname"};
ListBox1.items.Add(testObj);
Class ClassA
{
public string name {get;set;}
public override string ToString()
{
return name;
}
}
so if I update the ClassA.name property the Listbox does not update. I have tried to call the listboxes refresh() method.
Try replacing the item in the list with a new reference:
ClassA item = (ClassA)listBox1.Items[0];
item.name = "New Item";
listBox1.Items[0] = item;
Unfortunately, a bad side effect of this is it fires the SelectedIndexChanged event multiple times, so you would need to remove the handler and add it back in or set a flag to work around that behavior.
Or just have your class implement INotifyPropertyChanged and have your list use a BindingList<ClassA> as it's DataSource, then any changes to an item's property would automatically update the ListBox.
Your class "ClassA" should inherit an interface called "INumerable"

Binding Dependency Properties

So here I come creating a user control. It consists of a treeview dropping down from a combobox.
Actually, there is a button with a control (DropTree) dropping down from its contextmenu. So I have a control DropTree.
public partial class DropTree : UserControl
{
public TreeView TreeView
{ get{return treeView;} }
public DropTree()
{ InitializeComponent(); }
}
to simplify it, I made the TreeView control public, then I have my main control which is called ComboTreeView.
Now I need to represent some treeview properties in it, so I define several dependency properties:
public static DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(ComboTreeView), new FrameworkPropertyMetadata { Inherits = true, IsNotDataBindable = false, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
and in constructor it is:
public ComboTreeView()
{
InitializeComponent();
TreeViewControl.SetBinding(TreeView.SelectedItemProperty, new Binding("SelectedItem") { Source = this, Mode = BindingMode.TwoWay });
}
and it all seems ok, until i run it. It crashes saying that SelectedItem cannot be binded to data. I don't understand?
The same goes for ItemsSource and SelectedValue... but only SelectedValuePath property defined this way goes fine.
Can anybody help? Or is there any other way to bind it correctly?
PS: by the way, I need to use DataBinding for ComboTreeView in my code later.
Try to set the Binding on SelectedValue instead of SelectedItem.
TreeView.SelectedItem is a readonly property. You can't set it, whether explicitly or through binding. In order to select a node in a TreeView, you must set the TreeViewItem.IsSelected property to true.

Categories