How to convert the following foreach loop to linq format? - c#

Code:
foreach (var item in items)
testItems.Add(new TestItem { Header = item.TestItemTypeName, Content = item });
testItems is of type ObservableCollection<TabItem>
How to convert the above foreach loop to linq format? Thanks.

How about
items.ForEach(i => testItems.Add(
new TestItem
{
Header = i.TestItemTypeName,
Content = i
}));
Given that items is ObservableCollection<TabItem>
you could try
items.ToList().ForEach(i => testItems.Add(
new TestItem
{
Header = i.TestItemTypeName,
Content = i
}));

Providing that testItems has AddRange function, i.e. inherits from List:
testItems.AddRange(items.Select(item => new TestItem{ Header = item.TestItemTypeName, Content = item }));
As you have mentioned,this is an ObservableCollection, so the method is not available. In that case, the way you provided is the simplest possible. However, if you need to use this functionality more often, consider checking the following link:
ObservableCollection Doesn't support AddRange method, so I get notified for each item added, besides what about INotifyCollectionChanging?

Or perhaps:
var testItems = items
.Select(item =>
new TestItem {
Header = item.TestItemTypeName,
Content = item
})
.ToList();

Related

How to use linq instead of foreach loop in c#

I'm trying to find solution for the next situation:
I have array with Item ids
var arrayIds = new long []{1076,2840,4839,3920,..., N};
I have method which returns one Item
public Item getItem(long id) {
return new Item{Id = id, Name = "name"};
}
Here trying to get all Items
var itemList = new List<Item>();
foreach(var id in arrayIds) {
itemList.Add(getItem(id));
}
Is it possible to use Linq here instead of foreach?
I've tried to write something like that
itemList = arrayIds.ForEach(x => getItem(x));
so I have the next error here:
There is no argument given that corresponds to the required formal parameter 'action' of 'Array.ForEach<T>(T[], Action<T>)'
So I don't know how to use Linq correctly.
I've tried to write something like that
itemList = arrayIds.ForEach(x => getItem(x));
ForEach() works on List<T>:
arrayIds.ToList().ForEach(x => getItem(x));
But what you want is:
var itemList = arrayIds.Select(getItem).ToList();
Or if you only want to enumerate the items:
var items = arrayIds.Select(getItem);
Use Select
var itemList = arrayIds.Select(x => getItem(x))

Group, Sort, and then Merge to a Observable Collection?

I have a List of Items that have a "DisplayOrder" property. This can either have NULL or int value. If it has int value, it has priority and should be in the 1st group of the Observable Collection. Items in the 1st group are also sorted by DisplayOrder.
If it is NULL, then it belongs to the 2nd group, sorted alphabetically.
The 1st group and 2nd group are then combined for a Main Items Collection Observable Collection which I bind to a ListView.
This is my current code though I am worried if there is a much optimal way of doing it.
var MainItemCollection = new ObservableCollection<MainItemViewModel>();
var ItemsWithProperty = new ObservableCollection<MainItemViewModel>();
var ItemsWithNullProperty = new ObservableCollection<MainItemViewModel>();
foreach (var item in DataObject.MainItems)
{
if (item.DisplayOrder == null)
ItemsWithNullProperty.Add(new MainItemViewModel(item));
else
ItemsWithProperty.Add(new MainItemViewModel(item));
}
ItemsWithProperty = new ObservableCollection<MainItemViewModel>(ItemsWithProperty.OrderBy(c => c.DisplayOrder));
ItemsWithNullProperty = new ObservableCollection<MainItemViewModel>(ItemsWithNullProperty.OrderBy(c => c.Title));
//Add those with priorities first sorted by DisplayOrder 1,2,3,4
foreach (var c in ItemsWithProperty)
{
MainItemCollection.Add(c);
}
//Add those without priority sorted Alphabetically
foreach (var c in ItemsWithNullProperty)
{
MainItemCollection.Add(c);
}
Thank you!
Get the items with DisplayOrder=null & order them by Title:
ItemsWithNullProperty=DataObject.MainItems.Where(x=>x.DisplayOrder==null).OrderBy(o=>o.Title).ToList();
Get the items with DisplayOrder(all items except the above query) & order them by DisplayOrder:
ItemsWithProperty= DataObject.MainItems.Except(ItemsWithNullProperty).OrderBy(o=>o.DisplayOrder).ToList();
Fill the data in MainCollection:
var allItems = MainItemCollection.Concat(ItemsWithProperty)
.Concat(ItemsWithNullProperty)
.ToList();
When doing things like this, you don't need all those intermediate ObservableCollections - you can use the appropriate data structures like array, list, dictionary, hash set etc. or Linq queries. In this particular case, the whole procedure can be reduced to something like this
var MainItemCollection = new ObservableCollection<MainItemViewModel>(DataObject.MainItems
.OrderBy(item => item.DisplayOrder ?? int.MaxValue)
.ThenBy(item => item.DisplayOrder == null ? item.Title : string.Empty)
);
I feel that this is a pretty common scenario.
There is a sorted ObservableCollection bound to some XAML UI, and once more data is available UI needs to be updated without full refresh.
Whenever new ObservableCollection is created like in suggestions above, all items will be rebound and therefore UI fully updated.
I'm surprised that there are no library methods to achieve this. Here is the solution I've came up with. Hope someone might find it useful.
public static class ObservableCollectionExtensions
{
public static void MergeSortedListIntoSortedObservableCollection<T>(this ObservableCollection<T> destination, IList<T> list, Func<T, T, int> compareFunc)
{
int dest_index = 0;
int list_index = 0;
while (list_index < list.Count)
{
if (dest_index >= destination.Count)
{
destination.Add(list[list_index]);
list_index++;
dest_index++;
}
else
{
if (compareFunc(destination[dest_index], list[list_index]) > 0)
{
destination.Insert(dest_index, list[list_index]);
list_index++;
dest_index++;
}
else
{
dest_index++;
}
}
}
}
}

How to use ListBox.Items as Itemsource of a List<String>

I have a ListBox. I also have a list in code behind. I want to get all the items from the listbox as the source of my list.
I tried this way:
<listBox x:Name="MyList" .../>
list<string> feed = new list<string>();
//to get the source
var feed = MyList.Items.Cast<string>().ToList();
but It's throwing an Exception: System.InvalidCastException
what to do?
You are getting the error because your list isn't made of strings! Try to get the string representation instead.
List<string> feed = MyList.Items.Cast<object>()
.Select(o => o.ToString()).ToList();
list<T> feed = new list<T>();
var feed = MyList.Items.Cast<T>().ToList();
This is the correct way. But, see the type of items you have in your ListBox. They may not be string type That's why you are getting this error. You may not have items with the type string. Replace T with that type in your ListBox. Or fix your ListBox to have items with the type string.
I think Sam's answer is correct. If the ItemsSource of your ListBox is string collection, then casting them to string should not be a problem.
MyList.ItemsSource = new List<string>
{
"one","two","three"
};
List<string> feed = MyList.Items.Cast<string>().ToList();
But if the ItemsSource of your ListBox is other than string collection, suppose MyClass collection
class MyClass
{
public string MyProperty { get; set; }
public override string ToString()
{
return this.MyProperty;
}
}
Then you have to cast it in appropriate type and select desired property.
MyList.ItemsSource = new List<MyClass>
{
new MyClass {MyProperty ="one"},
new MyClass {MyProperty ="two"},
new MyClass {MyProperty ="three"},
};
List<string> feed = MyList.Items.Cast<MyClass>().Select(c => c.MyProperty).ToList();
But to answer your question correctly we need to know about the ItemsSource of your ListBox.
You know in wpf you have Listbox has ListboxItem and you have to cash you item to ListboxItem first.
And after that. You push the listboxItem.Content to your
List<string> list = new List<string>();
foreach (var item in this.MyList.Items)
{
ListBoxItem castItem = item as ListBoxItem;
list.Add(castItem.Content.ToString());
}
Here you can try

How to add to a list multiple selected values of a ListBox ? C#, ASP.NET web site

I'm working on one of my first projects. I have a listbox where I select multiple values and I would like to add each selection (selectedItem.Text) to a list of strings.
so far what I was working on is something like ..
selectedItem = new List<string>();
var value = lstpdfList.SelectedItem.Text;
for (int i = 0; i < lstpdfList.SelectedValue.Count(); i++)
{
selectedItem.Add(value);
}
I would really appreciate any advice.
Iterate each item from ListBox.Items collection
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected)
{
selectedItem.Add(item.Text); // selectedImte.Add(item.Value);
}
}
There is SelectedItems property of ListBox, try to iterate throught it. For example, if there is strings in your ListBox, then your code might look like this:
selectedItem = new List<string>();
foreach (string value in lstpdfList.SelectedValues)
selectedItem.Add(value);
You can just cast them to strings:
var selectedItems = listBox1.SelectedItems
.Cast<string>()
.ToList();
If you have populated your ListBox with something other than just strings, just cast to whichever type you need, like so:
var selectedItems = listBox1.SelectedItems
.Cast<WhateverYourTypeIs>()
.Select(item => item.ToString())
.ToList();

Is there a way to get a range from an ObservableCollection?

I would like to get a range from an ObservableCollection for the purpose of looping through it and changing a property on those items. Is there an easy built-in way to do this with the ObservableCollection class?
You can use Skip and Take.
System.Collections.ObjectModel.ObservableCollection<int> coll =
new System.Collections.ObjectModel.ObservableCollection<int>()
{ 1, 2, 3, 4, 5 };
foreach (var i in coll.Skip(2).Take(2))
{
Console.WriteLine(i);
}
For looping, ObservableCollection<T> derives from Collection<T>, and implements IList<T>. This lets you loop by index:
for (int i=5;i<10;++i)
{
DoSomething(observableCollection[i]);
}
If you want to do queries on the collection via LINQ, you have a couple of options. You can use Skip() and Take(), or build a new range and access by index:
var range = Enumerable.Range(5, 5);
var results = range.Select(i => DoMappingOperation(observableCollection[i]));
Or:
var results = observableCollection.Skip(5).Take(5).Select(c => DoMappingOperation(c));
ObservableCollection<T> implements Colletion<T> which implements IEnumerable so you should be able to do (if you're looking for a range that matches a given criteria):
foreach(var item in observerableCollection.Where(i => i.prop == someVal))
{
item.PropertyToChange = newValue;
}
Or an arbitrary range (in this case it takes items 10 - 40):
foreach(var item in observableCollection.Skip(10).Take(30))
{
item.PropertyToChange = newValue;
}
foreach(var item in MyObservableProperty.Skip(10).Take(20))
{
item.Value = "Second ten";
}
Skip and Take are linq extension methods used for paging a collection.

Categories