0

I'm trying to modify a property of a grid depending on whether or not there are any items in the ObservableCollection<> the ItemsSource is bound to. I currently have an event for OnItemsSourceChanged on the grid, but the problem is that ItemsSource is an object, and without casting it, I don't have access to the CollectionChanged event of the ObservableCollection<>. The generic type of ObservableCollection<> is undetermined until runtime and can be many things.

I've tried casting it with ItemsSource as ObservableCollection<object>, but that returns null. No luck with ItemsSource as ObservableCollection<dynamic>, either. Doing ItemsSource as IEnumerable<object> works surprisingly enough, but that still does not allow me to access the CollectionChanged event.

How can I cast my object to the underlying ObservableCollection<>? I'd prefer to avoid using reflection, but I'm not going to be picky here.

Lunyx
  • 3,164
  • 6
  • 30
  • 46

2 Answers2

1

What about casting it to INotifyCollectionChanged?

EDIT:

What you probably should be doing is something like this:

<TextBlock Visibility="{Binding MyObservableCollection.Count, Converter={StaticResource NonZeroToVisibleConverter}}">

Where NonZeroToVisibleConverter is something like:

public class ColorConverter : IValueConverter
{
  public object Convert(object value, Type targetType, 
      object parameter, CultureInfo culture)
  {   
    return (int)value > 0 ? Visibility.Visible : Visibility.Collapsed
  }

  public object ConvertBack(object value, Type targetType, 
      object parameter, CultureInfo culture)
  {
    throw new NotImplmentedException();
  }
}

OR probably even better

<Grid Name="MyGrid" ItemsSource="{Binding MyObservableCollection" />

<TextBlock Visibility="{Binding HasItems, ElementName=MyGrid, Converter={StaticResource BoolToVisibilityConverter}}" />

Last Resort For Future Reference

Your last resort would of been better to expose a property ShouldBe/IsVisible in you ViewModel and and bind to that in your View.

Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
  • Well, that just completely slipped over my head. That cast does work. I explained my motivation already, but in any case, it's because I want to change a property of the grid depending on whether or not there are items in my ItemsSource, which is bound to an ObservableCollection<> defined in the ViewModel. I'm not aware of a better way to make this check, but I'd be interested if you know of one. – Lunyx Feb 10 '15 at 21:15
  • See Edit for some ideas – Michal Ciechan Feb 10 '15 at 21:41
  • I'm currently doing my implementation of this feature, along with many others, in an extended (custom) datagrid control so that it can be reused. I was not able to find a way to bind to the number of items from the xaml, and I'm not sure if binding to ItemsSource and using a converter for that would trigger changes raised by the ObservableCollection changing and the ItemsSource changing. – Lunyx Feb 10 '15 at 22:30
  • Binding to ItemsSource.Count, would make WPF add change listeners to ItemsSource therefore it would be updated. – Michal Ciechan Feb 12 '15 at 23:43
1

Consider the following code:

// where 
ObservableCollection<Foo> foo = new();
element.ItemsSource = foo;
// then negotiate to non-generic types
var bar = element.ItemsSource as INotifyCollectionChanged;
bar.CollectionChanged += (NotifyCollectionChangedEventHandler)(delegate(object sender, NotifyCollectionChangedEventArgs e) 
{
  var collection = bar as ICollection;
  // TODO: handle based on collection.Count
});

This way you are able to handle the event regardless of the generic types applied to ObservableCollection<T>.

Shaun Wilson
  • 8,727
  • 3
  • 50
  • 48
  • This is what I ended up going with, but someone else answered it first. I'm still curious as to why casting it to IEnumerable works, however. – Lunyx Feb 10 '15 at 21:20
  • Sorry if I suggested a cast to IEnumerable somewhere, I've been more explicit on using ICollection to avoid consumer code needing to understand contained types (e.g. ). To answer your question on the cast, though, I believe it's because of covariance/contravariance rules on IEnumerable, see also: http://stackoverflow.com/questions/6508529/c-sharp-covariant-generic-parameter – Shaun Wilson Feb 10 '15 at 21:23
  • @Lunyx I think `IEnumerable` is covariant, while `ObservableCollection` is not. That means you can cast it to a more generic version. There are some logistical problems when implementing covariance for a modifiable collection, so that is not supported. – HugoRune Feb 10 '15 at 21:28
  • 1
    The reason why IEnumerable is covariant is because it is Read Only, i'e you will not be putting any objects into it, only reading. The reason ObservableCollection is not covariant is because it has Insert/Add methods, therefore if you was to cast it to object, you could then do SomeOTher class as Object, and iinsert it. Therefore not all objects would be of type T anymore – Michal Ciechan Feb 10 '15 at 21:43
  • Thanks, that explains a lot. – Lunyx Feb 10 '15 at 22:01