The following forum(s) have migrated to Microsoft Q&A (Preview): Developing Universal Windows apps!
Visit Microsoft Q&A (Preview) to post new questions.

Learn More

 locked
Listen to Property change of item in observable collection in PRISM MVVM app RRS feed

  • Question

  • Hello,

    I am more or less completely stucked with listening to propertychanged Events in items of an observable collection.

    My target ist that the collection is saved to a file as soon as any item inside has changed.

    It is a simple Itemtemplate, just one line ending with a Checkbox. When the Content(text) of the line has changed or the Checkbox was checked/unchecked, an Event shall be triggered and a handler shall execute the saving process.

    I tried several approaches - None worked, no unity Container, no sub-Observablecollection - nothing.

    Any ideas?

    Observable collection Sub class:

    public class ItemObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
        {
            #region Constructor
            public ItemObservableCollection()
            {
                CollectionChanged += ItemObservableCollection_CollectionChanged;
            }
            #endregion
    
            #region Eventhandler
            private void ItemObservableCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                if (e.NewItems != null)
                {
                    foreach (var item in e.NewItems)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged += ItemObservableCollection_PropertyChanged; 
                    }
                }
                if (e.OldItems != null)
                {
                    foreach (var item in e.OldItems)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged -= ItemObservableCollection_PropertyChanged;
                    }
                }
            }
    
            private void ItemObservableCollection_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
                OnCollectionChanged(args);
            }
            #endregion
        }

    Item class:

    public class CheckListItem : BindableBase
        {
            #region Fields
            string _item = default(string);
            bool _checked = default(bool);
            #endregion
    
            #region Properties
            public string Item { get { return _item; } set { SetProperty(ref _item, value);  } }
            public bool Checked { get { return _checked; } set { SetProperty(ref _checked, value); } }
            #endregion
        }

    ViewModel:

    public class CheckListPageViewModel : ViewModel, ICheckListPage
        {
            #region Fields
            ItemObservableCollection<CheckListItem> _defcon1;
            ItemObservableCollection<CheckListItem> _defcon2;
            ItemObservableCollection<CheckListItem> _defcon3;
            ItemObservableCollection<CheckListItem> _defcon4;
            ItemObservableCollection<CheckListItem> _defcon5;
            private DelegateCommand _addCommand;
            private DelegateCommand _saveCommand;
            private DelegateCommand<string> _visualstateToDefconStateCommand;
            private bool _isOpen;
            private bool _isChecked;
            private Visibility _addButtonVisibility;
            VisualState _state = default(VisualState);
            private int _defconState;
            string _itemText = default(string);
            #endregion
    
            #region Properties
            public ItemObservableCollection<CheckListItem> Defcon1 { get { return _defcon1; } set { SetProperty(ref _defcon1, value); } }
            public ItemObservableCollection<CheckListItem> Defcon2 { get { return _defcon2; } set { SetProperty(ref _defcon2, value); } }
            public ItemObservableCollection<CheckListItem> Defcon3 { get { return _defcon3; } set { SetProperty(ref _defcon3, value); } }
            public ItemObservableCollection<CheckListItem> Defcon4 { get { return _defcon4; } set { SetProperty(ref _defcon4, value); } }
            public ItemObservableCollection<CheckListItem> Defcon5 { get { return _defcon5; } set { SetProperty(ref _defcon5, value); } }
            public bool IsOpen { get { return _isOpen; } set { SetProperty(ref _isOpen, value); } }
            public bool IsChecked { get { return _isChecked; } set { SetProperty(ref _isChecked, value); } }
            public enum VisualState { Defcon1, Defcon2, Defcon3, Defcon4, Defcon5 }
            public VisualState State { get { return _state; } set { SetProperty(ref _state, value); } }
            public string ItemText { get { return _itemText; } set { SetProperty(ref _itemText, value); } }
            public Visibility AddButtonVisibility { get { return _addButtonVisibility; } set { SetProperty(ref _addButtonVisibility, value); } }
            #endregion
    
            #region Constructor
            public CheckListPageViewModel()
            {
                _defcon1 = new ItemObservableCollection<CheckListItem>();
                _defcon2 = new ItemObservableCollection<CheckListItem>();
                _defcon3 = new ItemObservableCollection<CheckListItem>();
                _defcon4 = new ItemObservableCollection<CheckListItem>();
                _defcon5 = new ItemObservableCollection<CheckListItem>();
                Defcon1.CollectionChanged += Defcon1Changed;
                Defcon2.CollectionChanged += Defcon2Changed;
                Defcon3.CollectionChanged += Defcon3Changed;
                Defcon4.CollectionChanged += Defcon4Changed;
                Defcon5.CollectionChanged += Defcon5Changed;
            }
            #endregion
    
            #region Methods
            public async override void OnNavigatedTo(object navigationParameter, Windows.UI.Xaml.Navigation.NavigationMode navigationMode, Dictionary<string, object> viewModelState)
            {
                await LoadDefconStatus();
                await LoadCollections();
                DefconStateToVisualState();
            }
    
            private async Task<object> LoadCollections()
            {
                if (await StorageHelper.FileExistsAsync("defcon5.json", StorageHelper.StorageStrategies.Local) & await StorageHelper.ReadFileAsync<ObservableCollection<CheckListItem>>("defcon5.json", StorageHelper.StorageStrategies.Local) != null)
                    Defcon5 = await StorageHelper.ReadFileAsync<ItemObservableCollection<CheckListItem>>("defcon5.json", StorageHelper.StorageStrategies.Local);
                if (await StorageHelper.FileExistsAsync("defcon4.json", StorageHelper.StorageStrategies.Local) & await StorageHelper.ReadFileAsync<ObservableCollection<CheckListItem>>("defcon4.json", StorageHelper.StorageStrategies.Local) != null)
                    Defcon4 = await StorageHelper.ReadFileAsync<ItemObservableCollection<CheckListItem>>("defcon4.json", StorageHelper.StorageStrategies.Local);
                if (await StorageHelper.FileExistsAsync("defcon3.json", StorageHelper.StorageStrategies.Local) & await StorageHelper.ReadFileAsync<ObservableCollection<CheckListItem>>("defcon3.json", StorageHelper.StorageStrategies.Local) != null)
                    Defcon3 = await StorageHelper.ReadFileAsync<ItemObservableCollection<CheckListItem>>("defcon3.json", StorageHelper.StorageStrategies.Local);
                if (await StorageHelper.FileExistsAsync("defcon2.json", StorageHelper.StorageStrategies.Local) & await StorageHelper.ReadFileAsync<ObservableCollection<CheckListItem>>("defcon2.json", StorageHelper.StorageStrategies.Local) != null)
                    Defcon2 = await StorageHelper.ReadFileAsync<ItemObservableCollection<CheckListItem>>("defcon2.json", StorageHelper.StorageStrategies.Local);
                if (await StorageHelper.FileExistsAsync("defcon1.json", StorageHelper.StorageStrategies.Local) & await StorageHelper.ReadFileAsync<ObservableCollection<CheckListItem>>("defcon1.json", StorageHelper.StorageStrategies.Local) != null)
                    Defcon1 = await StorageHelper.ReadFileAsync<ItemObservableCollection<CheckListItem>>("defcon1.json", StorageHelper.StorageStrategies.Local);
                return Task.FromResult<object>(null);
            }
    
            private async Task<object> LoadDefconStatus()
            {
                Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
                Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
                if (roamingSettings.Values.ContainsKey("defconStatus"))
                {
                    await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    _defconState = Convert.ToInt16(roamingSettings.Values["defconStatus"].ToString()));
                }
                else
                {
                    if (localSettings.Values.ContainsKey("defconStatus"))
                    {
                        _defconState = Convert.ToInt16(localSettings.Values["defconStatus"].ToString());
                    }
                    else _defconState = 5;
                }
                return Task.FromResult<object>(null);
            }
    
            private void DefconStateToVisualState()
            {
                switch (_defconState)
                {
                    case 1:
                        State = VisualState.Defcon1;
                        break;
                    case 2:
                        State = VisualState.Defcon2;
                        break;
                    case 3:
                        State = VisualState.Defcon3;
                        break;
                    case 4:
                        State = VisualState.Defcon4;
                        break;
                    case 5:
                        State = VisualState.Defcon5;
                        break;
                    default:
                        State = VisualState.Defcon5;
                        break;
                }
            }
    
            private async void Defcon5Changed(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                await StorageHelper.WriteFileAsync<ItemObservableCollection<CheckListItem>>("defcon5.json", Defcon5, StorageHelper.StorageStrategies.Local);
            }
    
            private async void Defcon4Changed(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                await StorageHelper.WriteFileAsync<ItemObservableCollection<CheckListItem>>("defcon4.json", Defcon4, StorageHelper.StorageStrategies.Local);
            }
    
            private async void Defcon3Changed(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                await StorageHelper.WriteFileAsync<ItemObservableCollection<CheckListItem>>("defcon3.json", Defcon3, StorageHelper.StorageStrategies.Local);
            }
    
            private async void Defcon2Changed(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                await StorageHelper.WriteFileAsync<ItemObservableCollection<CheckListItem>>("defcon2.json", Defcon2, StorageHelper.StorageStrategies.Local);
            }
    
            private async void Defcon1Changed(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                await StorageHelper.WriteFileAsync<ItemObservableCollection<CheckListItem>>("defcon1.json", Defcon1, StorageHelper.StorageStrategies.Local);
            }
            #endregion

    Sunday, July 26, 2015 5:45 PM

Answers

  • I have moved the Event Registration from the Constructor to the OnNavigatedTo method. I  don't know why but now it works as expected. I guess, I will have soon more questions regarding this Project. Especially about editing, deleting and moving entries, but first I try it myself.

    I have opened the whole Project here: https://onedrive.live.com/redir?resid=D80C77B9D3E3DE0F!374483&authkey=!AAIIo2MZu90bUNU&ithint=folder%2csln

    Although it doesn't look complex on the UI, it covers a lot of Topics like universal app, roaming, PRISM, VisualStates, Storyboard and more...

    public async override void OnNavigatedTo(object navigationParameter, Windows.UI.Xaml.Navigation.NavigationMode navigationMode, Dictionary<string, object> viewModelState)
            {
                await LoadDefconStatus();
                await LoadCollections();
                DefconStateToVisualState();
                Defcon1.CollectionChanged += Defcon1Changed;
                Defcon2.CollectionChanged += Defcon2Changed;
                Defcon3.CollectionChanged += Defcon3Changed;
                Defcon4.CollectionChanged += Defcon4Changed;
                Defcon5.CollectionChanged += Defcon5Changed;
            }

    • Marked as answer by Marcus Runge Tuesday, July 28, 2015 6:47 PM
    Tuesday, July 28, 2015 6:47 PM

All replies

  • There seems to be no issue with your ObservableCollection implementation. Make sure that your bindings works as expected and that the setter of the properties of the CheckListItem objects actually get called. Put breakpoints in the setters and inside each of the DefconXChanged event handlers. Does anyone of them get hit? If not, there is an issue with your binding(s).

    This should work:

     <ListBox ItemsSource="{Binding Defcon1}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" Content="{Binding Item}" FontSize="20" Foreground="Red"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

    Hope that helps. Please upload a reproducible sample of your issue to OneDrive and post the link to it here if you need any further help.

    Please also remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question.

    Sunday, July 26, 2015 9:19 PM
  • Out of interest, if you comment out the Load and just hard code a couple of entries in the collection, does it work?

    http://pauliom.wordpress.com

    Sunday, July 26, 2015 9:27 PM
  • I have moved the Event Registration from the Constructor to the OnNavigatedTo method. I  don't know why but now it works as expected. I guess, I will have soon more questions regarding this Project. Especially about editing, deleting and moving entries, but first I try it myself.

    I have opened the whole Project here: https://onedrive.live.com/redir?resid=D80C77B9D3E3DE0F!374483&authkey=!AAIIo2MZu90bUNU&ithint=folder%2csln

    Although it doesn't look complex on the UI, it covers a lot of Topics like universal app, roaming, PRISM, VisualStates, Storyboard and more...

    public async override void OnNavigatedTo(object navigationParameter, Windows.UI.Xaml.Navigation.NavigationMode navigationMode, Dictionary<string, object> viewModelState)
            {
                await LoadDefconStatus();
                await LoadCollections();
                DefconStateToVisualState();
                Defcon1.CollectionChanged += Defcon1Changed;
                Defcon2.CollectionChanged += Defcon2Changed;
                Defcon3.CollectionChanged += Defcon3Changed;
                Defcon4.CollectionChanged += Defcon4Changed;
                Defcon5.CollectionChanged += Defcon5Changed;
            }

    • Marked as answer by Marcus Runge Tuesday, July 28, 2015 6:47 PM
    Tuesday, July 28, 2015 6:47 PM