iterate between complex object list - c#

I use this code to move between instances of a class
public class Orders : List<Order>
{
int currentIndex = 0;
public int CurrentIndex {
get {
if (currentIndex == Count) {
currentIndex = 0;
} else if (currentIndex > Count - 1) {
currentIndex = Count - 1;
} else if (currentIndex < 0) {
currentIndex = 0;
}
return currentIndex;
}
set { currentIndex = value; }
}
public void MoveNext()
{
currentIndex++;
}
public void MovePrevious()
{
currentIndex--;
}
public Order Current {
get {
return this[CurrentIndex];
}
}
}
public class Cart
{
public string id_ordine { get; set; }
public string nome { get; set; }
public string cognome { get; set; }
public double prezzo { get; set; }
}
but I can't figure out hot to make the same with complex class that includes a list like in following case.
I have tried it in many way..
public class Order
{
public string nome { get; set; }
public string cognome { get; set; }
public List<OrdersList> ordersList { get; set; }
}
public class OrdersList
{
public string id_ordine { get; set; }
public double prezzo { get; set; }
}
to be more specific considider that i have one order that contain more order list
for example
order orderslist
-------------+-----------------
name surname id_ordine prezzo
-------------+-----------------
john doe --> 1 10
--> 2 12
--> 3 22

I would take a different approach entirely. A list of items doesn't naturally have a "cursor" in it - it's entirely logical to be able to have multiple cursors, like multiple bookmarks in a book.
I would stick to a List<OrdersList> for the list itself, but create an IndexedListEnumerator<T> type or something similar, with a GetIndexedEnumerator() extension method (probably on IList<T>). That would implement IEnumerator<T>, but also have a MovePrevious() method and an Index property.
That separates out the concern for "I want to have a more flexible cursor" from the concern of "I want a list of orders".
I'd also strongly recommend that unless you really, really need the sort of "wrap-around" functionality you currently have, that you make the index a simple property, but make Current throw an exception when trying to access an invalid index. (So the initial index would be -1, and Current would be invalid. Calling MoveNext() would take you to index 0 which would be valid for any non-empty list, etc.) As a rough sketch:
public static class ListExtensions
{
public static IndexedListEnumerator<T> GetIndexedListEnumerator<T>(this IList<T> list) =>
new IndexedListEnumerator<T>(list);
}
public sealed class IndexedListEnumerator<T> : IEnumerator<T>
{
private readonly IList<T> list;
// You *could* restrict this to the range [-1, list.Count] if you wanted.
public int Index { get; set; }
// You might want to make this public
private bool IndexIsValid => Index >= 0 && Index < list.Count;
public IndexedListEnumerator(IList<T> list)
{
this.list = list;
Index = -1; // Before the first element
}
// TODO: Consider using checked to throw an exception around int.MaxValue
public bool MoveNext()
{
Index++;
return IndexIsValid;
}
public bool MovePrevious()
{
Index--;
return IndexIsValid;
}
public T Current => list[Index];
object IEnumerator.Current => Current;
void IDisposable.Dispose() {}
void IEnumerator.Reset() => Index = -1;
}
Example usage, although you'd normally want to check the result of MovePrevious() and MoveNext():
List<Order> orders = ...;
var enumerator = orders.GetIndexedListEnumerator();
enumerator.Index = 10;
Console.WriteLine(enumerator.Current); // Prints orders[10]
enumerator.MovePrevious();
Console.WriteLine(enumerator.Current); // Prints orders[9]
enumerator.MoveNext();
enumerator.MoveNext();
Console.WriteLine(enumerator.Current); // Prints orders[11]

Related

How to remove items from listbox

i tried to remove item from listbox using MultiExtended as selection mode
this is my code
im using this snippet code for getid from listbox
private int getid(int index)
{
int.TryParse(lb_ItemList.Items[lb_ItemList.SelectedIndices[index]].ToString().Split('-')[0], out index);
return index;
}
and this code im using for remove index from listbox
for (int i = lb_ItemList.Items.Count - 1; i > -1; i--)
{
lb_ItemList.Items.RemoveAt(lb_ItemList.SelectedIndices[getid(i)]);
}
but for any reason it doesn't work... any suggestion? thanks
I assume you need to copy the content of lb_ItemList.SelectedIndices before you start remove items out of lb_ItemList because every call to Remove(...) will update the content of lb_ItemList.SelectedIndices
You only need remove by index and SelectedIndices has the index:
for (int i = lb_ItemList.SelectedIndices.Count - 1; i > -1; i--)
{
var index = lb_ItemList.SelectedIndices[i];
lb_ItemList.Items.RemoveAt(index);
}
You don't need your Id to remove selected items.
If you need work with you class in the Items, I recomended you create an Item class:
public class YourItem
{
public YourItem()
{
}
public YourItem(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return $"{this.Id} - {this.Name}";
}
}
Then, you add your items to the ListBox:
this.lb_ItemList.Items.Add(new YourItem(1, "A"));
this.lb_ItemList.Items.Add(new YourItem(2, "B"));
this.lb_ItemList.Items.Add(new YourItem(3, "C"));
this.lb_ItemList.Items.Add(new YourItem(4, "D"));
The text of each item is that you return in ToString method of YourItem. For example "1 - A" for the first item.
And, when you are working with the ListBox, simply cast to your class:
var item = (YourItem)this.lb_ItemList.Items[1];
var id = item.Id;
var name = item.Name;
If you class is big, you can use your Item as a wrapper:
public class YourClass
{
public int Id { get; set; }
public string Name { get; set; }
// Other properties, methods...
}
public class YourItem
{
private YourClass _item;
public YourItem(YourClass obj)
{
this._item = obj;
}
public int Id
{
get { return this._item.Id}
set { this._item.Id = value; }
}
public string Name
{
get { return this._item.Name}
set { this._item.Name = value; }
}
public override string ToString()
{
return $"{this.Id} - {this.Name}";
}
}
Your class maybe have lost of information but for your item, you only expose Id and Name, for example.

How to keep custom object sequence property in model when there are multiple lists of that type used in application

I have a wpf application with MVVM pattern. There is a view which displays the warnings (text) on screen, which are of type WarningModel.cs
In my ViewModel I have 3 properties of type ObservableCollection<WarningModel>(), these are bound to 3 different grids (telerik for wpf grids). Three grids because there are 3 types of warnings.
My requirement is when user adds new warning (new object of type WarningModel) into the grid, that object should be assigned an order sequence starting with 1 followed by 2 and so on for next object.
What I tried is added a static field in Warning.cs which will be auto incremented each time the constructor is called. Now the problem is since there are 3 ObservableCollection each time the object is instantiated from any of the collection, sequence gets incremented but all the three collection should maintain their own sequence.
What else can be employed here to achieve the desired output.
Here are my classes
public class Warning
{
public string MESSAGE { get; set; }
private int _type;
public int TYPE
{
get { return _type; }
set
{
if (value != _type)
_type = value;
}
}
public int SORT_ORDER { get; set; }
public static int SORT_SEQUENCE = 0;
public Warning()
{
SORT_ORDER = Interlocked.Increment(ref SORT_SEQUENCE);
}
}
public class WarningsViewModel
{
public ObservableCollection<WarningModel> WarningBP { get; set; }
public ObservableCollection<WarningModel> WarningPP { get; set; }
public ObservableCollection<WarningModel> WarningPB { get; set; }
public VoyageInfoViewModel()
{
WarningBP = new ObservableCollection<WarningModel>();
WarningPP = new ObservableCollection<WarningModel>();
Warning PB = new ObservableCollection<WarningModel>();
}
}
You can attach handler for CollectionChanged of ObservableCollection and assign ID as index of inserted item.
public VoyageInfoViewModel()
{
WarningBP = new ObservableCollection<WarningModel>();
WarningBP.CollectionChanged += WarningCollectionChanged;
...
}
private void WarningCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
for (var i = 0; i < e.NewItems.Count; i++)
((Warning)e.NewItems[i]).SORT_ORDER = e.NewStartingIndex + i + 1;
}
If you allow user also to delete warnings and want SORT_ORDER be recalculated on deletion you can write handler as:
private void WarningCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
for (var i = 0; i < ((ICollection)sender).Count; i++)
((Warning)((IList)sender)[i]).SORT_ORDER = i + 1;
}
You could for example create a custom ObservableCollection<T> that sets the sequence number of added items. Something like this:
public class SequencedObservableCollection : ObservableCollection<WarningModel>
{
private int _sequenceCounter = 0;
protected override void InsertItem(int index, WarningModel item)
{
item.SORT_ORDER = ++_sequenceCounter;
base.InsertItem(index, item);
}
}
public abstract class WarningModel
{
public int SORT_ORDER { get; set; }
}
Don't forget to change the type of your source properties in the view model:
public SequencedObservableCollection WarningBP { get; set; }
public SequencedObservableCollection WarningPP { get; set; }
public SequencedObservableCollection WarningPB { get; set; }

How can I update the value of a field in every row of a list?

I have this class:
public class Test
{
public string Id { get; set; }
public int Number { get; set; }
public int DoubleNumber { get; set; }
}
and a list
List<Test> myTestList;
How can I make the value of the field DoubleNumber in myTestList equal to twice the value of Number? Note that I am okay to create another list if that's needed.
If I understand your question correctly:
foreach(Test item in myList) {
item.DoubleNumber = 2*item.Number;
}
Or, if it's ok, just remove the setter and modify the getter to return 2x Number:
public class Test
{
public string Id { get; set; }
public int Number { get; set; }
public int DoubleNumber { get { return 2* this.Number; } } //completely remove setter
}
Or, if you still want to be able to modify DoubleNumber:
public class Test {
private int m_num;
private int m_doubleNum;
public string Id {
get;
set;
}
public int Number {
get {
return this.m_num;
}
set {
this.m_num = value;
this.m_doubleNum = 2 * value; //when Number is set, update m_doubleNum too
}
}
public int DoubleNumber {
get {
return this.m_doubleNum;
}
set {
this.m_doubleNum = value; //allow manual setting of DoubleNumber
//or maybe also modify Number here?
//this.m_num = value / 2;
}
}
}
One way it could be using a foreach statement:
foreach(var item in myTestList)
{
item.DoubleNumber = 2*item.Number;
}
Another way it could be to use LINQ.
var result = myTestList.Select(test => new Test
{
test.Id,
test.Number,
DoubleNumber = 2*test.Number;
})
.ToList();
Among the two ways I would prefer the first one, since it's more clear what you are trying to do and more performant (in the second approach you have to create a new object for each object in myTestList).

Getting keyvaluepair values dynamically

Basic question here, but I'm new to c#. I have code that basically says: if condition A, then execute a code block on property X. If condition B, then execute the same code block on property Y, and so on. Instead of having to duplicate my code blocks just to change one single property name - a.Value.ValueX to a.Value.ValueY - is there a way to call ValueX or ValueY as variables, such as a.Value.{$propertyName} ?
public static class Conditions
{
public static bool A { get; set; }
public static bool B { get; set; }
}
public class MyObjects
{
public int ValueX { get; set; }
public int ValueY { get; set; }
}
public class MyCollection
{
public Dictionary<int, MyObjects> listOfObjects = new Dictionary<int, MyObjects>();
public static void DoConditions()
{
foreach( var a in listOfObjects)
{
if(Conditions.A)
{
// do code using value x
if (a.Value.ValueX > 0)
continue;
}
else if(Conditions.B)
{
// do the exact same code using value Y
if (a.Value.ValueY > 0)
continue;
}
}
}
}
You can do this:
int val = 0;
if(Conditions.A)
val = a.Value.ValueX;
else if(Conditions.B)
val = a.Value.ValueY;
// Your code block here using "val".
Create a variable and populate it with the appropriate property value:
foreach( var a in listOfObjects)
{
int value;
if(Conditions.A)
value = a.Value.ValueX;
else
value = a.Value.ValueY;
if(value > 0)
continue;
//other code using `value`
}

ProtoBuf - Migration to new version

I recently migrated to new version of protobuf-net, and i started getting this error message after
Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a subclass
Call Stack Trace
protobuf-net.dll!ProtoBuf.Meta.MetaType.AddSubType(int fieldNumber = 1, System.Type derivedType = {Name = "InfoColumn`1" FullName = "Om.Common.InfoSet.InfoColumn`1[[System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}) Line 83 C#
protobuf-net.dll!ProtoBuf.Meta.MetaType.ApplyDefaultBehaviour() Line 431 + 0x32 bytes C#
Any help in this regard is appreciated. I am planning to rollback my code to previous version of protobuf-net
Below is the class info.
[DataContract]
[ProtoInclude(1, typeof(InfoColumn<Double>))]
[ProtoInclude(2, typeof(InfoColumn<String>))]
[ProtoInclude(3, typeof(InfoColumn<DateTime>))]
[ProtoInclude(4, typeof(InfoColumn<Boolean>))]
public abstract class IInfoColumnBase
{
[DataMember(Order = 101)]
public abstract bool IsSingleValue { get; set; }
[DataMember(Order = 102)]
public abstract string Name { get; set; }
[DataMember(Order = 103)]
public abstract InfoColumnDataType DataType { get; set; }
public abstract long Insert();
public abstract void Insert(long index);
public abstract void SetValue(long index, object val);
public abstract void CopyValues(long start, long end, IInfoColumnBase destCol, long index);
public abstract long GetIndex(object val);
public abstract void Remove(long index);
public abstract object GetValue(long index);
public abstract object GetInternalArrayValue(long index);
public abstract void Clear();
public abstract long Count { get; }
public abstract long ArrayCount { get; }
}
public interface IInfoColumn<T> : IEnumerable<T>
{
T this[double index] { get; set; }
InfoTable Table { get; set; }
double Add(T item);
}
[DataContract(Name = "InfoColumn{0}")]
[KnownType(typeof(InfoColumn<double>))]
[KnownType(typeof(InfoColumn<String>))]
[KnownType(typeof(InfoColumn<bool>))]
[KnownType(typeof(InfoColumn<DateTime>))]
public class InfoColumn<T> : IInfoColumnBase, IInfoColumn<T>
{
long counter = 0;
[DataMember(Order = 1)]
public IList<T> Values { get; set; }
//[DataMember(Order = 2)]
bool isSingleVal = false;
//[DataMember(Order=3)]
public override string Name { get; set; }
//[DataMember(Order=4)]
public override InfoColumnDataType DataType { get; set; }
public InfoTable Table { get; set; }
public override long Count
{
get
{
return this.Table.Count;
}
}
public override long ArrayCount
{
get { return this.Values.Count; }
}
public InfoColumn()
{
}
public InfoColumn(string name,InfoTable table)
{
this.Values = new List<T>();
this.Name = name;
this.Table = table;
}
public override void Clear()
{
this.Values = new List<T>();
}
public override void Remove(long index)
{
int newindex = (int)index;
this.Values.RemoveAt(newindex);
}
public override void CopyValues(long start, long end, IInfoColumnBase destCol, long startIndex)
{
InfoColumn<T> typeCol = destCol as InfoColumn<T>;
for (long ctr = start; ctr <= end; ctr++)
{
typeCol.SetValue(startIndex, this.Values[(int)ctr]);
startIndex++;
}
}
public override void Insert(long rows)
{
if (this.IsSingleValue == true) return;
for (int ctr = 0; ctr < rows; ctr++)
{
this.Values.Add(default(T));
}
}
public T this[double a]
{
get
{
if (a >= this.Count) throw new IndexOutOfRangeException();
long index = (long)a;
if (this.Table.IsFreezed == false)
index = this.Table.CheckData(a);
if (this.isSingleVal == true)
return this.Values[0];
else
return this.Values[(int)index];
}
set
{
if (a >= this.Count) throw new IndexOutOfRangeException();
long index = (long)a;
if (this.Table.IsFreezed == false)
index = this.Table.CheckData(a);
if (this.isSingleVal == true)
this.Values[0] = value;
else
this.Values[(int)index] = value;
}
}
public override long GetIndex(object val)
{
T item = (T)val;
return this.Values.IndexOf(item);
}
public override void SetValue(long index, object val)
{
if (val is InfoSetLink)
this.Values[(int)index] = (T)val;
else
this.Values[(int)index] = (T)Convert.ChangeType(val, typeof(T));
}
public override object GetValue(long index)
{
return this[index];
}
public override object GetInternalArrayValue(long index)
{
return this.Values[(int)index];
}
//[DataMember(Order=5)]
public override bool IsSingleValue
{
get { return isSingleVal; }
set
{
if (isSingleVal == true)
{
this.Values = new List<T>(1);
}
}
}
public override long Insert()
{
if (this.IsSingleValue == true) return -1;
this.Values.Add(default(T));
return this.Values.Count - 1;
}
public double Add(T item)
{
this.Values.Add(item);
return this.Values.Count - 1;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return new InfoColumnEnumerator<T>(this);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new InfoColumnEnumerator<T>(this);
}
#endregion
}
InfoColumn<T> has a public Add(T) and implements IEnumerable<T> (via IInfoColumn<T>).
There is wider support for list-like types in v2, and it may be that it is trying to interpret the above as a list. Which indeed, it does look a lot like! I will try to take a look to see if this general scenario can be detected and avoided, but it is an edge case (since it is indeed very list-esque).
There is an existing IgnoreListBehaviour switch, however when validating this for the model shown above, it seems that for this specific scenario the "you can't do that" fires before the code that disables list handling; I have changed this in the source, and this will be included in the next release. Basically, you can address this by adding:
[ProtoContract(IgnoreListHandling = true)]
to the impacted type (InfoColumn<T>), with the next build. Which will be shortly, as soon as I've completed validation etc.

Categories