Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to an ObservableCollection to show the first X number of items c# WPF

Background Explanation

Okay so I'm currently binding a ContextMenu ItemsSource to an ObservableCollection of lets say TypeA

The following code is within the singleton class DataStore

private ObservableCollection<TypeA> TypeACollection = new ObservableCollection<TypeA>();

public ObservableCollection<TypeA> GetTypeACollection
{
    get { return TypeACollection; }
}
public ObservableCollection<TypeA> GetFirstFiveTypeACollection
{
    get 
    {
        return TypeACollection.Take(5);
    }
}

Now I've already successfully bound an ItemsControl to GetTypeACollection with the following XAML code:

The following code is within the MainWindow.xaml

<ItemsControl x:Name="TypeAList" ItemsSource="{Binding GetTypeACollection, Source={StaticResource DataStore}, UpdateSourceTrigger=PropertyChanged}" >
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <VirtualizingStackPanel />
    </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <User_Controls:TypeAUserControl Type="{Binding }"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

This works as expected, showing the TypeAUserControl, correctly formatted as intended with the data from TypeA

Now when i try to repeat this on a ContextMenu MenuItem by binding the ItemsSource to GetFirstFiveTypeACollection i initially see the expected results however upon deleting a TypeA object the MainWindow ItemsControl is updated where the ContextMenu is not.

I believe this is due to the fact that the binding itself is between the ContextMenu and a 'new' ObservableCollection<TypeA> (as seen in GetFirstFiveTypeAColletion ).

I have also attempted this through use of an IValueConverter

The following code is within the class ValueConverters

public class ObservableCollectionTypeAResizeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ObservableCollection<TypeA> TypeACollection = value as ObservableCollection<TypeA>;

        return TypeACollection.Take(System.Convert.ToInt32(parameter));
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

The XAML code I have tried.

Using IValueConverter

<MenuItem Header="First 5 Type A" Name="MI_FirstFiveTypeA" 
    ItemsSource="{Binding DATA_STORE.GetTypeACollection, ConverterParameter=5,
            Converter={StaticResource ObservableCollectionTypeAResizeConverter}, 
            Source={StaticResource DataStore}}" 
/>

Using GetFirstFiveTypeACollection

<MenuItem Header="First 5 Type A" Name="MI_FirstFiveTypeA" 
    ItemsSource="{Binding DATA_STORE.RecentTimers, Source={StaticResource DataStore}, 
            UpdateSourceTrigger=PropertyChanged}"
/>

I've no idea what to try and do next or how i should be doing this, Any help would be greatly appreciated!

Edit

Okay so I have changed the following

DataStore.cs

private ObservableCollection<TimerType> TimerTypes = new ObservableCollection<TimerType>();

public ObservableCollection<TimerType> getTimerTypes
{
    get 
    { 
        return new ObservableCollection<TimerType>(TimerTypes.OrderByDescending(t => t.LastUsed)); 
    }
}
public ObservableCollection<TimerType> RecentTimers
{
    get 
    { 
        return new ObservableCollection<TimerType>(TimerTypes.OrderByDescending(t => t.LastUsed).Take(5)); 
    }
}

public event PropertyChangedEventHandler PropertyChanged;

// Create the OnPropertyChanged method to raise the event
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
//THIS IS IN THE ADD METHOD
TimerTypes.Add(timer);
NotifyPropertyChanged("getTimerTypes");
NotifyPropertyChanged("RecentTimers");
NotifyPropertyChanged("TimerTypes");

//THIS IS IN THE REMOVE METHOD
TimerTypes.Remove(Timer);
NotifyPropertyChanged("getTimerTypes");
NotifyPropertyChanged("RecentTimers");
NotifyPropertyChanged("TimerTypes");

MainWindow.xaml

<ItemsControl x:Name="TimersList" ItemsSource="{Binding Path=getTimerTypes, UpdateSourceTrigger=PropertyChanged}" >

MainWindow.xaml.cs

//Lists are populated in DataStore.cs before this.
DataContext = DataStore.DATA_STORE;
InitializeComponent();

NotifyIcon.xaml

<MenuItem Header="Recent Timers" Name="MIRecent" ItemsSource="{Binding RecentTimers, UpdateSourceTrigger=PropertyChanged}"/>

NotifyIcon.xaml.cs

DataContext = DataStore.DATA_STORE;
InitializeComponent();

So everything binds correctly at start but when a TimerType is deleted the PropertyChangedEventHandler is always NULL. I thought this was a DataContext issue but I'm pretty sure I have the DataContext and all of the Bindings correct now?

Singleton Instance Creation

private static readonly DataStore Instance = new DataStore();

private DataStore() { }

public static DataStore DATA_STORE
{
    get { return Instance; }
    set { }
}
like image 792
Chris Walters Avatar asked Oct 18 '22 23:10

Chris Walters


1 Answers

Unfortunately, by using Take (or any of the LINQ methods) you get a straight IEnumerable back. When you have bound to it, there is no INotifyCollectionChanged so changes to the collection will not cause the UI to update.

There's no real workaround due to the lack of INotifyCollectionChanged. Your best bet is to raise PropertyChanged against your "subset" property when you add/remove items to force the UI to renumerate the IEnumerable.

like image 68
BradleyDotNET Avatar answered Oct 29 '22 22:10

BradleyDotNET