I have series of listboxes and comboboxes that I want to update, so that the items listed are the same as those listed in a IEnumerable<string>. I do understand that data binding might help, but I think it could be a bit of a struggle now, and I'd rather avoid it.
I wrote something like this:
public static void UpdateListboxWithStrings(
ListControl listcontrol,
IEnumerable<string> stringlist)
{
Object listcontrolitems;
if (listcontrol is ListBox)
{
listcontrolitems =
((ListBox)listcontrol).Items;
}
else if (listcontrol is ComboBox)
{
listcontrolitems =
((ComboBox)listcontrol).Items;
}
else
{
//// Wrong control type.
//// EARLY EXIT
return;
}
int itemscount = listcontrolitems.Count;
/// More code here...
}
... And troubles begin. Depending on what I add / remove, listcontrolitems appears undefined, or must be initialized, or it doesn't have properties such as Count.
How do you write a function that works with a combobox or in a listbox without code duplication?
LE. It is a Windows Application, NET Framework 4.5 using System.Windows.Forms. I want to add / remove items, count, get and set selection. Also, there might be duplicates. So converting to items to strings won't work.
You won't be able to do this in a convenient way, unless you only need the features that are available in the IList type. In that case, you can skip the wrapper described below and just declare the items local variable as IList, assigning it directly to the Items property in each control-type-specific if branch.
If all you needed was the Count property value, you could assign a local int variable in each type-specific branch (i.e. in the if statement blocks).
But you state that you want to actually manipulate the collections. The System.Windows.Forms.ComboBox.Items and System.Windows.Forms.ListBox.Items collections are two completely different, unrelated types. So if you can't use IList, the only way you would be able to share code that manipulates them would be to wrap the collections in a new type that understands both.
For example:
abstract class ListControlItems
{
public abstract int Count { get; }
public abstract int Add(object item);
public abstract void RemoveAt(int index);
// etc.
}
class ListBoxControlItems : ListControlItems
{
private ListBox.ObjectCollection _items;
public ListBoxControlItems(ListBox.ObjectCollection items)
{
_items = items;
}
public override int Count { get { return _items.Count; } }
public override int Add(object item) { return _items.Add(item); }
public override void RemoveAt(int index) { _items.RemoveAt(index); }
// etc.
}
Do the same thing for a ComboBoxControlItems type. Then in your handler, you can create the appropriate abstract type, and use that to manipulate the collections:
public static void UpdateListboxWithStrings(
ListControl listcontrol,
IEnumerable<string> stringlist)
{
ListControlItems items;
if (listcontrol is ListBox)
{
items = new ListBoxControlItems(((ListBox)listcontrol).Items);
}
else if (listcontrol is ComboBox)
{
items = new ComboBoxControlItems(((ComboBox)listcontrol).Items);
}
else
{
//// Wrong control type.
//// EARLY EXIT
return;
}
int itemscount = items.Count;
/// More code here...
}
Related
I'm trying to display list of items in XAML. I get the list from public API, convert it to the class I need and then I want to display it.
public static async Task PopulateListAsync(ObservableCollection<MyClass> myList) {
var listContainer = await GetListAsync();
foreach (var item in listContainer) {
//converting from one class to another, editing some properties and such
myList.Add(item );
}
}
and on the MainPage.cs I had
public ObservableCollection<MyClass> Value { get; set; }
public MainPage() {
this.InitializeComponent();
Value = new ObservableCollection<MyClass>();
}
private async void Page_Loaded(object sender, RoutedEventArgs e) {
await PopulateListAsync(Value);
}
And I displayed in the XAML fine.
But then I wanted to introduce filtering. So I get the data, convert them to some class and insert them to a list, which I then filter with LINQ (seems easier then filtering in ObservableCollection).
Basically I replaced the PopulateListAsync() with FormatListAsync() which instead of inserting the data directly into the ObservableCollection<>, returns a List<>. Then I have a "middle man" function
public static async Task PopulateListAsync(ObservableCollection<MyClass> myList) {
myList = new ObservableCollection<MyClass>(await FormatListAsync());
//filtering itself isn't implemented yet, but it would be placed here
}
I probably could just loop trough mylist and add it one by one into the ObservableCollection<>, but I feel like there surely is a better way.
I think I'm supposed to implement some PropertyChanged event or something like that, but I tried a few (this one for example), unsuccessfully. I don't think I quite understand how to implement it.
If you are assign new value for method parameter then you just change reference's copy to the collection and don't change source reference. You can read more about passing reference types as method parameters on MSDN.
Also, if you will change property that not implements INotifyPropertyChanged itself then you'll have no changes in UI because your view doesn't know about the changes.
In the simple and easy way you can manipulate source collection instead of creating new one. Just do something like
public static async Task PopulateListAsync(ObservableCollection<MyClass> myList)
{
// newList can be an List<MyClass> type, not ObservableCollection
var newList = await FormatListAsync();
// change displayed list with new data
myList.Clear();
foreach(var newValue in newList)
myList.Add(newValue);
}
The other option, you can implement INotifyPropertyChanged for your ViewModel and raise PropertyChanged event in the setter of Value property:
private ObservableCollection<MyClass> _value;
public ObservableCollection<MyClass> Value
{
get
{
return _value;
}
set
{
// I hope this line of code will convince you to give more clear variable name
if(value != _value)
{
_value = value;
NotifyPropertyChanged(nameof(Value));
}
}
}
Also, you'll need to assign Value directly in the PopulateListAsync():
public static async Task PopulateListAsync()
{
Value = new ObservableCollection<MyClass>(await FormatListAsync());
}
In general terms, a program I'm making involves storing a small number of entries (probably less than 30 at any given time) which can be categorized. I want to allow these entries to be seen but not altered from outside the class using them. I made a class called Entry which could be modified and another called ReadOnlyEntry which is a wrapper for an Entry object. The easiest way to organize these Entry objects it seems is to create a List<List<Entry>>, where each List<Entry> is a category. But then exposing that data in a readonly way became messy and complicated. I realized I would have to have one object of each of the following types:
List<List<Entry>> data;
List<List<ReadOnlyEntry>> // Where each ReadOnlyEntry is a wrapper for the Entry in the same list and at the same index as its Entry object.
List<IReadOnlyCollection<ReadOnlyEntry>> // Where each IReadOnlyCollection is a wrapper for the List<ReadOnlyEntry> at the same index in data.
IReadOnlyCollection<IReadOnlyCollection<ReadOnlyList>> readOnlyList // Which is a wrapper for the first item I listed.
The last item in the list would be exposed as public. The first lets me change entries, the second lets me add or delete entries, and the third lets me add or delete categories. I would have to keep these wrappers accurate whenever the data changes. This seems convoluted to me, so I'm wondering if there's a blatantly better way to handle this.
Edit 1:
To clarify, I know how to use List.asReadOnly(), and the stuff I proposed doing above will solve my problem. I'm just interested in hearing a better solution. Let me give you some code.
class Database
{
// Everything I described above takes place here.
// The data will be readable by this property:
public IReadOnlyCollection<IReadOnlyCollection<ReadOnlyList>> Data
{
get
{
return readOnlyList;
}
}
// These methods will be used to modify the data.
public void AddEntry(stuff);
public void DeleteEntry(index);
public void MoveEntry(to another category);
public void AddCategory(stuff);
public void DeleteCategory(index);
}
You can use List<T>.AsReadOnly() to return ReadOnlyCollection<T>.
Also, you're torturing the List<T> class storing the data the way you are. Build your own hierarchy of classes which store your individual lists.
.NET collections should support covariance, but they don't support it themselves (instead some interfaces support covariance https://msdn.microsoft.com/ru-ru/library/dd233059.aspx). Covariance means List<Conctrete> behaves like subclass of List<Base> if Concrete is subclass of Base. You can use interfaces covariation or just use casting like this:
using System.Collections.Generic;
namespace MyApp
{
interface IEntry
{
}
class Entry : IEntry
{
}
class Program
{
private List<List<Entry>> _matrix = null;
public List<List<IEntry>> MatrixWithROElements
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
}
}
public IReadOnlyList<List<IEntry>> MatrixWithRONumberOfRows
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
}
}
public List<IReadOnlyList<IEntry>> MatrixWithRONumberOfColumns
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry) as IReadOnlyList<IEntry>);
}
}
public IReadOnlyList<IReadOnlyList<IEntry>> MatrixWithRONumberOfRowsAndColumns
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
}
}
public void Main(string[] args)
{
}
}
}
Thanks to Matthew Watson for pointing on errors in my previous answer version.
You could make an interface for Entry which contains only getters; you would expose elements via this interface to provide read-only access:
public interface IEntry
{
int Value { get; }
}
The writable implementation would be simply:
public sealed class Entry : IEntry
{
public int Value { get; set; }
}
Now you can take advantage of the fact that you can return a List<List<Entry>> as a IReadOnlyCollection<IReadOnlyCollection<IEntry>> without having to do any extra work:
public sealed class Database
{
private readonly List<List<Entry>> _list = new List<List<Entry>>();
public Database()
{
// Create your list of lists.
List<Entry> innerList = new List<Entry>
{
new Entry {Value = 1},
new Entry {Value = 2}
};
_list.Add(innerList);
}
public IReadOnlyCollection<IReadOnlyCollection<IEntry>> Data => _list;
}
Note how simple the implementation of the Data property is.
If you need to add new properties to IEntry you would also have to add them to Entry, but you wouldn't need to change the Database class.
If you're using C#5 or earlier, Data would look like this:
public IReadOnlyCollection<IReadOnlyCollection<IEntry>> Data
{
get { return _list; }
}
I know similar questions have been asked before, but I've come to a dead end while trying to find the best design pattern I can use.
I am trying to make a class-library with a factory class that provides enumerators for different items via method calls.
Note: Those items don't exist in a collection and can only be created knowing the previous one. (e.g. x(i) = x(i-1) + θ) Because of that I cannot implement those items as IEnumerable(s)
What I thought until now:
public static class AllItems {
public sealed class ItemsEnumerator: IEnumerator<Item>{
//those classes have non-public constructors
.....
}
public static ItemsEnumerator GetItemsEnumerator() {
return new ItemsEnumerator();
}
public sealed class OtherItemsEnumerator:IEnumerator<OtherItem>{
....
}
public static ItemsEnumerator GetOtherItemsEnumerator() {
return new ItemsOtherEnumerator();
}
}
this way i could do :
foreach(var item in AllItems.GetItemsEnumerator()){
//do something with item
}
which won't work, because according to c# spec ItemsEnumerator doesn't have a GetEnumerator function(To be used in a foreach statement)
If I change it to this
public static class AllItems {
public sealed class ItemsEnumerator: IEnumerator{
.....
public IEnumerator<Item> GetEnumerator() {
return this;
}
}
public static ItemsEnumerator GetItemsEnumerator() {
return new ItemsEnumerator();
}
}
Is this a good design in general, or am I missing something here?
EDIT: Clarification on c# spec limitation
I am trying to make a class-library with a factory class that provides enumerators for different items via method calls. Note: Those items don't exist in a collection and can only be created knowing the previous one. (e.g. x(i) = x(i-1) + θ) Because of that I cannot implement those items as IEnumerable(s)
You don't need to go to that level of detail - you can just use yield to achieve a "conceptual" collection without having to wire in all of the enumeration plumbing:
public IEnumerable<Item> GetItems()
{
int i = 0;
while(i < 100) // or foreach(Item item in _baseItems), etc.
{
Item item = new Item();
item.X = i;
i += 10;
yield return item;
}
}
Note that this is just for illustration to show one way of returning a "collection" of items that are generated on-the fly. You are free to adapt this to your situation in whatever way is appropriate.
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>();
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");
}