Virtualization and SelectionChanged event - c#

I am using SelectionChanged event of ListBox, but it "doesn't work".
Here is repro:
public partial class MainWindow : Window
{
readonly List<Item> _items = new List<Item>
{
new Item(),
... // add 20 more, to have selected item outside of visible region
new Item(),
new Item { IsSelected = true },
new Item(),
};
void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
Debug.WriteLine($"Changed {e.AddedItems.Count}/{e.RemovedItems.Count}");
void button_Click(object sender, RoutedEventArgs e) =>
listBox.ItemsSource = listBox.ItemsSource == null ? _items : null;
}
public class Item
{
public bool IsSelected { get; set; }
}
and xaml:
<Grid>
<ListBox x:Name="listBox" SelectionChanged="listBox_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button Content="Test" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="10" Click="button_Click" />
</Grid>
1. Disable virtualization
Add to list:
VirtualizingPanel.IsVirtualizing="False"
Clicking button will produce the output. Cool.
2. VirtualizationMode="Standard"
Remove that line, by default ListBox will use "Standard" virtualization:
Event is not triggered. I need to scroll to selected item to have event triggered.
3. VirtualizationMode="Recycling"
Change virtualization to:
VirtualizingPanel.VirtualizationMode="Recycling"
WTF? Even scrolling doesn't trigger event.
Question: How to get SelectionChanged event to work properly in the most performant mode without need to scroll as in "Standard" mode?

With virtualization, if an item doesn't have a container (ListBoxItem) associated with it, then there's no container to which that ItemContainerStyle is applied. That means your IsSelected binding won't be applied until the item is scrolled into view. Until that property is set, no selection change occurs, and SelectionChanged is not raised.
How to get SelectionChanged event to work properly in the most performant mode without need to scroll as in "Standard" mode?
It arguably *is* working properly. If you approach this from an MVVM angle, then you need not rely on events from UI elements. Track the item selection yourself, in your model. You could use a utility class like this:
public interface ISelectable
{
bool IsSelected { get; set; }
}
public class ItemEventArgs<T> : EventArgs
{
public T Item { get; }
public ItemEventArgs(T item) => this.Item = item;
}
public class SelectionTracker<T> where T : ISelectable
{
private readonly ObservableCollection<T> _items;
private readonly ObservableCollection<T> _selectedItems;
private readonly ReadOnlyObservableCollection<T> _selectedItemsView;
private readonly HashSet<T> _trackedItems;
private readonly HashSet<T> _fastSelectedItems;
public SelectionTracker(ObservableCollection<T> items)
{
_items = items;
_selectedItems = new ObservableCollection<T>();
_selectedItemsView = new ReadOnlyObservableCollection<T>(_selectedItems);
_trackedItems = new HashSet<T>();
_fastSelectedItems = new HashSet<T>();
_items.CollectionChanged += OnCollectionChanged;
}
public event EventHandler<ItemEventArgs<T>> ItemSelected;
public event EventHandler<ItemEventArgs<T>> ItemUnselected;
public ReadOnlyObservableCollection<T> SelectedItems => _selectedItemsView;
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewItems == null)
goto default;
AddItems(e.NewItems.OfType<T>());
break;
case NotifyCollectionChangedAction.Remove:
if (e.OldItems == null)
goto default;
RemoveItems(e.OldItems.OfType<T>());
break;
case NotifyCollectionChangedAction.Replace:
if (e.OldItems == null || e.NewItems == null)
goto default;
RemoveItems(e.OldItems.OfType<T>());
AddItems(e.NewItems.OfType<T>());
break;
case NotifyCollectionChangedAction.Move:
break;
default:
Refresh();
break;
}
}
public void Refresh()
{
RemoveItems(_trackedItems);
AddItems(_items);
}
private void AddItems(IEnumerable<T> items)
{
foreach (var item in items)
{
var observableItem = item as INotifyPropertyChanged;
if (observableItem != null)
observableItem.PropertyChanged += OnItemPropertyChanged;
_trackedItems.Add(item);
UpdateItem(item);
}
}
private void RemoveItems(IEnumerable<T> items)
{
foreach (var item in items)
{
var observableItem = item as INotifyPropertyChanged;
if (observableItem != null)
observableItem.PropertyChanged -= OnItemPropertyChanged;
_trackedItems.Remove(item);
UpdateItem(item);
}
}
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender is T item)
UpdateItem(item);
}
private void UpdateItem(T item)
{
if (item?.IsSelected == true && _trackedItems.Contains(item))
{
if (_fastSelectedItems.Add(item))
{
_selectedItems.Add(item);
this.ItemSelected?.Invoke(this, new ItemEventArgs<T>(item));
}
}
else
{
if (_fastSelectedItems.Remove(item))
{
_selectedItems.Remove(item);
this.ItemUnselected?.Invoke(this, new ItemEventArgs<T>(item));
}
}
}
}
When you create your ObservableCollection of items, instantiate a SelectionTracker for that collection. Then subscribe to ItemSelected and ItemUnselected to handle individual selection changes, or alternatively subscribe to SelectedItems.CollectionChanged. If you don't care about being able to access SelectedItems as a collection, then you can get rid of _selectedItems and _selectedItemsView and avoid some list removal overhead.
[With VirtualizationMode="Recycling"] WTF? Even scrolling doesn't trigger event.
Well, that's a strange one. I see no reason why that should not work in this case, but I can perhaps see why it might not always work. In theory, as soon as the container is 'recycled' and its DataContext is assigned a new item, the IsSelected binding should update. If the container's previously assigned item had also been selected, that might not trigger a property change, and thus the event might not fire. But that doesn't seem to be the case in your example. Possibly a bug or unintended consequence of how recycling is implemented.
Don’t use IsSelected to manage selection.
I think the big takeaway here is that using ListBoxItem.IsSelected to *set* the selection is unreliable; it should only be trusted to reflect whether a given container is selected. It’s really intended for style and template triggers, so that they may know whether to render a container as selected or not. It was never meant to manage selection, and it’s a mistake to use it that way, because it represents the selection state of the container and not its associated data item. Thus, it only works in the most naïve and least performant scenario where every item is always associated with its own container (no virtualization).

Related

How can I properly reset a value associated with a ComboBox, from within a PropertyChanged event?

I have a ComboBox that is bound to a property on my ViewModel (from hear on "VM".) When a user makes a selection on the ComboBox it properly updates the bound property in my VM. Within my UI code, I have subscribed to the PropertyChanged event on my VM.
As it should behave, when the user makes a selection within the ComboBox, my PropertyChanged event is correctly executing in my UI back-end code. When the UI code catches the change of this property, under certain selection conditions I need to halt the process and request the user for additional information. From the UI, I send them a dialog. If they cancel the dialog, I reset the value in the VM that is associated with the ComboBox controls SelectedValue.
This is what I've observed. When the operation is cancelled by the user, my VM property is being set to the new value. However, the ComboBox is still showing the text value of the original entry that has now changed. How can I force the ComboBox to update itself from within my PropertyChanged event? In this case, I think it's just a textual issue or numeric index change that's referencing the text data from the bound collection. The data is correct in the VM but the display value for the ComboBox is wrong.
EXAMPLE
ComboBox Details
<ComboBox
ItemsSource="{Binding ListOfComboBoxDisplayObjects}"
SelectedValue="{Binding MySelectionIsAnEnumeration}"
DisplayMemberPath="Text"
SelectedValuePath="EnumerationValue"
Height="27" />
Sorry for the wordy properties on the VM, but that's to explain what's happening. My ListOfComboBoxDisplayObjects collection represents a set of enumerator values that are stored in the path within SelectedValuePath. The descriptive text for each value is pulled from the ListOfComboBoxDisplayObjects which is a special list strictly created for the UI. This basically pairs an enumeration value with a meaningful description.
ListOfComboBoxDisplayObjects Definition (from within VM)
Edit #1 - Added this definition to my example
private ObservableCollection<BindableEnumerationItem<Values>> _listOfComboBoxDisplayObjects;
public ObservableCollection<BindableEnumerationItem<Values>> ListOfComboBoxDisplayObjects
{
get { return _listOfComboBoxDisplayObjects; }
private set
{
if (value != _listOfComboBoxDisplayObjects)
{
_listOfComboBoxDisplayObjects= value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ListOfComboBoxDisplayObjects)));
}
}
}
MySelectionIsAnEnumeration Definition (From within VM)
*Edit #1: Adding this code definition.
private Values_mySelectionIsAnEnumeration ;
public Values MySelectionIsAnEnumeration
{
get { return _mySelectionIsAnEnumeration; }
set
{
//Double-checked this-- value is different on the second-call to change this value, once the UI cancels the operation.
if (value != _mySelectionIsAnEnumeration)
{
_mySelectionIsAnEnumeration= value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(MySelectionIsAnEnumeration )));
}
}
}
Pertinent Values Associated with ListOfComboBoxDisplayObjects
These values are generated in the ctor of the VM. They are fixed throughout the application.
Item #1
Text: "This is a Foo!"
Value: Values.Foo
Item #2:
Text: "Hi, I'm Bar."
Value: Values.Bar
Item #3:
Text: "This is Baz. I need to ask a question before I can be used."
Value: Values.Baz
PropertyChanged Event - From the UI Back-End
private void VM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "MySelectionIsAnEnumeration":
if (VM.MySelectionIsAnEnumeration == Values.Baz)
{
//Prompt the user and get DialogResult.
bool answerGiven = AskAQuestionAndGetAResult();
if(!answerGiven)
VM.MySelectionIsAnEnumeration = Values.Foo;
}
break;
}
}
After executing the above code, what I'm observing is that the VM.MySelectionIsAnEnumeration value is indeed changing to the proper value of Value.Foo when a user cancels the operation within AskAQuestionAndGetAResult(). However, after it's finished the ComboBox still reads "This is Baz. I need to ask a question before I can be used.", which is obviously the display value associated with Value.Baz.
How can I update both the underlying VM property AND the display text on the CombobBox to correctly show the valued that is now stored in VM.MySelectionIsAnEnumeration?
Edit #2
Below is the code efor my BindableEnumerationItem that I use within my Observable Collections for comboxes and list boxes. This is used throughout my application in simpler cases and has caused no issue. Please note, this is my actual, unaltered code. I've not renamed anything. My comboboxes can bind to each Item property for a type-safe property and DisplayText is the descriptor text.
public class BindableEnumerationItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private T _item;
public BindableEnumerationItem(T item, string displayText)
{
_item = item;
_displayText = displayText;
}
private string _displayText;
public string DisplayText
{
get { return _displayText; }
set
{
if (value != _displayText)
{
_displayText = value;
PropertyChanged(this, new PropertyChangedEventArgs("DisplayText"));
}
}
}
public T Item
{
get { return _item; }
set
{
_item = value;
PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
}
create an extension that will wire up the command from your viewmodel in xaml to the selector, which in this case is the combobox.
public partial class Extensions
{
public static readonly DependencyProperty SelectionChangedCommandProperty = DependencyProperty.RegisterAttached("SelectionChangedCommand", typeof(ICommand), typeof(Extensions), new UIPropertyMetadata((s, e) =>
{
var element = s as Selector;
if (element != null)
{
element.SelectionChanged -= OnSelectionChanged;
if (e.NewValue != null)
{
element.SelectionChanged += OnSelectionChanged;
}
}
}));
public static ICommand GetSelectionChangedCommand(UIElement element)
{
return (ICommand)element.GetValue(SelectionChangedCommandProperty);
}
public static void SetSelectionChangedCommand(UIElement element, ICommand value)
{
element.SetValue(SelectionChangedCommandProperty, value);
}
private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var element = sender as Selector;
var command = element.GetValue(SelectionChangedCommandProperty) as ICommand;
if (command != null && command.CanExecute(element.SelectedItem))
{
command.Execute(element.SelectedItem);
e.Handled = true;
}
}
}
Create the command in the viewmodel that handles the value changed event.
public ICommand EnumerationValueChangedCommand
{
get
{
return new Command(
() =>
{
if (VM.MySelectionIsAnEnumeration == Values.Baz)
{
//Prompt the user and get DialogResult.
bool answerGiven = AskAQuestionAndGetAResult();
if (!answerGiven)
VM.MySelectionIsAnEnumeration = Values.Foo;
}
});
}
}
And then bind using that extension. ext is the namespace for your extensions.
<ComboBox
ItemsSource="{Binding ListOfComboBoxDisplayObjects}"
SelectedValue="{Binding MySelectionIsAnEnumeration}"
DisplayMemberPath="Text"
SelectedValuePath="EnumerationValue"
ext:Extensions.SelectionChangedCommand="{Binding EnumerationValueChangedCommand}"
Height="27" />

ObservableCollection<> CollectionChanged Not Firing

I have a datagrid in a WPF app that is bound to an ObservableCollection like so
<DataGrid ItemsSource="{Binding Spring.SpringData, Mode=OneWay}" />
My data displays fine, and I can edit the data in my grid, but it does not fire the PublishSpringChange event when I manually edit the data in the grid. The underlying data changes, but the event does not fire, what am I missing?
With a model of Spring that has the following
public class Spring : INotifyPropertyChanged
{
private ObservableCollection<SpringData> _SpringData;
public ObservableCollection<SpringData> SpringData
{
get { return _SpringData; }
}
public Spring()
{
....
_SpringData = new ObservableCollection<SpringData>();
SpringData.CollectionChanged += PublishSpringChange;
...
}
private void PublishSpringChange(object sender, NotifyCollectionChangedEventArgs e)
{
// Code that does not run!
}
}
with a SpringData class of
public class SpringData: BindableBase
{
private double _force;
private double _displacement;
public SpringData(double displacement, double force)
{
Displacement = displacement;
Force = force;
}
public double Displacement
{
get { return _displacement; }
set { SetProperty(ref _displacement, value); }
}
public double Force
{
get { return _force; }
set { SetProperty(ref _force, value); }
}
}
INotifyCollectionChanged only fires when you actually modify the collection. This is when you Add, Remove, Move, Replace or Reset items in the collection. It will not fire when one of the properties in a SpringData object is changed.
In order to listen to changes for a SpringData object, assuming it implements INotifyPropertyChanged, you will need to hook up listeners to the PropertyChanged event of each of the items.
Its quite useful to have a single handler for all properties changing sometimes. Here's how you can do it.
Handle CollectionChanged as you are above:
_SpringData = new ObservableCollection<SpringData>();
SpringData.CollectionChanged += PublishSpringChange;
Now for all added and removed objects to the collection add a handler to PropertyChanged:
private void PublishSpringChange(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (INotifyPropertyChanged added in e.NewItems)
{
added.PropertyChanged += SpringDataOnPropertyChanged;
}
foreach (INotifyPropertyChanged removed in e.OldItems)
{
removed.PropertyChanged -= SpringDataOnPropertyChanged;
}
}
private SpringDataOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
//your code here
}

How to notify parent's view model when child's view model property is changed?

I have a windows say
PhotoAlbums (Parent window)
PhotoAlbumProperty (Child)
On Photo Albums window I have combo box of list of photo albums, NewAlbum button, Save button, property button.
I want to enable save button only when my PhotoAlbum is in edit mode.
PhotoAlbum will go in edit mode when I add new photos in a album OR if I change properties by clicking property button.
I have properties,
IsPhotoAlbumUpdated in PhotoAlbumVM
IsPhotoAlbumPropertyUpdated in PhotoAlbumPropertyVM
IsSaveEnabled
{
get return this.IsPhotoAlbumUpdated || this.SelectedAlbum.IsPhotoAlbumPropertyUpdated;
}
in PhotoAlbumVM
<Button Name="BtnSave" Command="{Binding Save}"
ToolTip="{x:Static resx:Resource.ToolTipSave}" Focusable="True"
IsEnabled="{Binding IsSaveEnabled}">
Now when this.SelectedAlbum.IsPhotoAlbumPropertyUpdated gets changed then how will my parent view model i.e. PhotoAlbumVM know this?
I was thinking to use prism events, but for doing such smaller thing I don't want to use prism events.
Please suggest me alternate logic.
You need to listen for the child item's OnPropertyChanged event. Each time SelectedAlbum is changed, in set remove the handler from the old album unless it's null using album.PropertyChanged -= MyPropertyChanged and assign the handler to the new value using value.PropertyChanged += MyPropertyChanged. In MyPropertyChanged force update new value of IsSaveEnabled.
You can subscribe to PropertyChanged events to collection items on CollectionChanged. Providing your UI is bound correctly to the items in the ObservableCollection, you shouldn't need to tell the UI to update when a property on an item in the collection changes. You can do the collection of specific object or use the below implementation to do it across application if you have to do make consistent behavior.
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;
namespace VJCollections
{
/// <summary>
/// This class adds the ability to refresh the list when any property of
/// the objects changes in the list which implements the INotifyPropertyChanged.
/// </summary>
/// <typeparam name="T">
public class ItemsChangeObservableCollection<T> :
ObservableCollection<T> where T : INotifyPropertyChanged
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
RegisterPropertyChanged(e.NewItems);
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
UnRegisterPropertyChanged(e.OldItems);
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
UnRegisterPropertyChanged(e.OldItems);
RegisterPropertyChanged(e.NewItems);
}
base.OnCollectionChanged(e);
}
protected override void ClearItems()
{
UnRegisterPropertyChanged(this);
base.ClearItems();
}
private void RegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void UnRegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}

In WPF/MVVM, how to implement the "Select All" function in a perfect way

In View, i.e. the XAML, I have binded the SelectAll bool value to the Checkbox control
<CheckBox Grid.Row="5" Content="Select All" HorizontalAlignment="Left" Margin="0,5, 0, 0" IsChecked="{Binding Path=SelectAll, Mode=TwoWay}" Width="206"></CheckBox>
and fullfill the SelectAll as
public bool SelectAll
{
get { return this.Get<bool>("SelectAll"); }
set
{
this.Set<bool>("SelectAll", value);
if (this.SelectAllCommand.CanExecute(null))
this.SelectAllCommand.Execute(value);
}
}
Yes, it looks good, but I have an issue...
When all the checkboxes are selected manually, the selectall checkbox should be selected automatically... at that time, we don't want the SelectAllCommand command to be executed...
How can I do it..... I know maybe it is an easy job, but how to do it perfectly....
Thanks for giving me some advices in advance
Try
public bool? SelectedAll
{
get { return this.Get<bool?>("SelectedAll"); }
set
{
if(Equals(SelectedAll, value) == true)
return;
this.Set<bool?>("SelectedAll", value);
OnSelectedAllChanged(value);
}
}
private void SelectedAllChanged(bool? input)
{
//Stuff
}
You'll need to uses the PropertyChanged event of the item to reevaluate the SelectAll value anytime an items is selected.
For example,
// Setup PropertyChange notification for each item in collection
foreach(var item in MyCollection)
{
item.PropertyChanged += Item_PropertyChanged;
}
private bool _isSelectAllExecuting = false;
// If the IsSelected property of an item changes, raise a property change
// notification for SelectAll
void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!_isSelectAllExecuting && e.PropertyName == "IsSelected")
ReevaluateSelectAll();
}
void ReevaluateSelectAll()
{
// Will evaluate true if no items are found that are not Selected
_selectAll = MyCollection.FirstOrDefault(p => !p.IsSelected) == null;
// Since we're setting private field instead of public one to avoid
// executing command in setter, we need to manually raise the
// PropertyChanged event to notify the UI it's been changed
RaisePropertyChanged("SelectAll");
}
void SelectAll()
{
_isSelectAllExecuting = true;
foreach(var item in MyCollection)
item.IsSelected = true;
_isSelectAllExecuting = false;
}

Caliburn.Micro and DataGrid: Reliable approach for detecting individual cell changes?

I have an MVVM-based WPF application that relies on Caliburn.Micro.
In one view, I am displaying a DataGrid and a Button. The DataGrid displays a collection of items, where the item class derives from PropertyChangedBase.
The button should be enabled or disabled based on the contents in the editable DataGrid cells. What is the most reliable approach to achieve this with Caliburn.Micro?
Schematically, this is what my code looks right now:
public class ItemViewModel : PropertyChangedBase { }
...
public class ItemsViewModel : PropertyChangedBase
{
private IObservableCollection<ItemViewModel> _items;
// This is the DataGrid in ItemsView
public IObservableCollection<ItemViewModel> Items
{
get { return _items; }
set
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
// This is the button in ItemsView
public void DoWork() { }
// This is the button enable "switch" in ItemsView
public bool CanDoWork
{
get { return Items.All(item => item.NotifiableProperty == some_state); }
}
}
As the code stands, there is no notification to ItemsViewModel.CanDoWork when one NotifiableProperty is changed, for example when the user edits one cell in the ItemsView´s DataGrid. Hence, the DoWork button enable state will never be changed.
One possible workaround is to add an (anonymous) event handler to every item in the Items collection:
foreach (var item in Items)
item.PropertyChanged +=
(sender, args) => NotifyOfPropertyChange(() => CanDoWork);
but then I also need to keep track of when (if) items are added or removed from the Items collection, or if the Items collection is re-initialized altogether.
Is there a more elegant and reliable solution to this problem? I am sure there is, but so far I have not been able to find it.
I think this is a case where INPC works well; to simplify registering/deregistering adds and deletes just add a CollectionChanged handler to your Items collection:
Items.CollectionChanged += OnItemsCollectionChanged;
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
if (e.NewItems != null && e.NewItems.Count != 0) {
foreach (ItemViewModel vm in e.NewItems)
vm.PropertyChanged += OnDetailVmChanged;
}
if (e.OldItems != null && e.OldItems.Count != 0) {
foreach (ItemViewModel vm in e.OldItems) {
vm.PropertyChanged -= OnDetailVmChanged;
}
}
}
Josh Smith wrote a PropertyObserver class here that I find more elegant than shotgun INPC tracking, but in a master-detail scenario like yours you would still have to track the adds and deletes.
EDIT by Anders Gustafsson
Note that for the above code to work in the general case requires that Items has been initialized with the default constructor before the event handler is attached. To ensure that OnDetailVmChanged event handlers are correctly added and removed, the Items property setter need to be extended to something like this:
public IObservableCollection<ItemViewModel> Items
{
get { return _items; }
set
{
// If required, initialize empty _items collection and attach
// event handler
if (_items == null) {
_items = new BindableCollection<ItemViewModel>();
_items.CollectionChanged += OnItemsCollectionChanged;
}
// Clear old contents in _items
_items.Clear();
// Add value item:s one by one to _items
if (value != null) foreach (var item in value) _items.Add(item);
NotifyOfPropertyChange(() => Items);
}
}
(And of course, with the above Items setter in place, the topmost Items.CollectionChanged event handler attachment should not be included in the code.)
Ideally, I would have used if (value != null) _items.AddRange(value);, but when the AddRange method triggers the OnItemsCollectionChanged event handler, e.NewItems appear to be empty (or null). I have not explicitly verified that e.OldItems is non-null when the Clear() method is invoked; otherwise Clear() would also need to be replaced with one-by-one removal of the item:s in _items.
Whenever a property changes fire RaiseCanExecuteChanged for the button cummand command?
For Example :
public DelegateCommand<object> MyDeleteCommand { get; set; }
string _mySelectedItem;
public string MySelectedItem
{
get { return _mySelectedItem; }
set
{
_mySelectedItem = value;
OnPropertyChanged("MySelectedItem");
MyDeleteCommand.RaiseCanExecuteChanged();
}
}

Categories

Resources