Casting Issue | I am trying to return a similar type

Viewed 72

I have an ObservableCollection UrlsList. I also have a checkbox in my WPF application which amends items in a list using the IsChecked property. The problem I am having is when I write some lambda expressions to filter the data I want I can't assign it back to UrlsList. It just sets my list to null which then crashes the application. Thanks for your help.

ViewModel

public ObservableCollection<URLModel> UrlsList { get; set; } = new ObservableCollection<URLModel>();

public void CheckBoxOnClick()
{
    if (URLModel.IsChecked)
        UrlsList = UrlsList.Select(url => url.ExistsInDb) as ObservableCollection<URLModel>;
    else
        UrlsList = UrlsList.Select(n => n.ExistsInDb == false) as ObservableCollection<URLModel>;
}

Warning Msg

2 Answers

Your problem is you are seemingly using Select instead of Where

The following produces a list an IEnumerable<bool>

UrlsList.Select(url => url.ExistsInDb)

What it seems you want is actually Where, Which filters the list

UrlsList.Where(url => url.ExistsInDb);

Enumerable.Select Method

Projects each element of a sequence into a new form.

Enumerable.Where Method

Filters a sequence of values based on a predicate.


You are trying to cast an IEnumerable<T> back to an ObservableCollection<T>. That is bound to fail, because the value returned by a LINQ yuery is not an observable collection.

There are a couple of issues with your code:

  1. You are using Enumerable.Select when you should use Enumerable.Where. Select projects a list to a list with different values (which is not what you want), while Where will filter your list, (which is what you want).
  2. A LINQ query has to be materialized, for example with Enumerable.ToList or Enumerable.ToArray. The result of Where is a non-materialized IEnumerable, whose execution is deferred. Your ObservableCollection is a full collection.
  3. You can not materialize your LINQ query with a type cast. You have to create the collection yourself.

Considering all of those issues, your code would be:

public void CheckBoxOnClick()
{
    if (URLModel.IsChecked)
        UrlsList = new ObservableCollection<URLModel>(UrlsList.Where(url => url.ExistsInDb));
    else
        UrlsList = new ObservableCollection<URLModel>(UrlsList.Where(url => !url.ExistsInDb));
}

A more subtle bug in this code is however that when you check and then uncheck your check box, your URL list will be empty, because you first select only the checked items into the list and then select the non-checked items from that list. Since you have filtered the list already for the checked items, your second select will yield no result. You will have to store the original list somewhere else and select from there:

public void CheckBoxOnClick()
{
    if (URLModel.IsChecked)
        UrlsList = new ObservableCollection<URLModel>(allUrls.Where(url => url.ExistsInDb));
    else
        UrlsList = new ObservableCollection<URLModel>(allUrls.Where(url => !url.ExistsInDb));
}

Another issue is, that unless you modify the list somewhere else, you won't need an ObservableCollection, since you are reassigning the list when the check box changes. You could either use a simple List<T> or array, or - and that is what databinding is actually intended for - you change the content of the databound ObservableCollection and then the UI would automatically change with it.

Related