WPF DataGrid MultiSelect - c#

I have read several posts on this topic but many are from a previous versions of VS or framework. What I am trying to do is selected multiple rows from a dataGrid and return those rows into a bound observable collection.
I have tried creating a property(of type) and adding it to an observable collection and it works with single records but the code never fires with multiple records.
Is there a clean way to do this in VS2013 using an MVVM patern?
Any thoughts would be appreciated.
<DataGrid x:Name="MainDataGrid" Height="390" Width="720"
VerticalAlignment="Center" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False"
ItemsSource="{Binding Path=DisplayInDataGrid}"
SelectedItem="{Binding Path=DataGridItemSelected}"
SelectionMode="Extended"
private ObservableCollection<ScannedItem> _dataGridItemsSelected;
public ObservableCollection<ScannedItem> DataGridItemsSelected
{
get { return _dataGridItemsSelected; }
set
{
_dataGridItemsSelected = value;
OnPropertyChanged("DataGridItemsSelected");
}
}
private ScannedItem _dataGridItemSelected;
public ScannedItem DataGridItemSelected
{
get { return _dataGridItemSelected;}
set
{
_dataGridItemSelected = value;
OnPropertyChanged("DataGridItemSelected");
EnableButtons();
LoadSelectedCollection(DataGridItemSelected);
}
}
void LoadSelectedCollection(ScannedItem si)
{
if (DataGridItemsSelected == null)
{
DataGridItemsSelected = new ObservableCollection<ScannedItem>();
}
DataGridItemsSelected.Add(si);
}

I have implemented a two-way data binding for MultiSelector.SelectedItems property using attached behavior pattern.
The following image shows how it works:
There are two DataGrids bound to the same model and they share selected items. Left DataGrid is active so selected items are blue and right DataGrid is inactive so selected items are gray.
Following is the sample code how to use it:
MainWindow.xaml
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding People}" local:MultiSelectorExtension.SelectedItems="{Binding SelectedPeople}" CanUserAddRows="True"/>
<DataGrid Grid.Column="1" ItemsSource="{Binding People}" local:MultiSelectorExtension.SelectedItems="{Binding SelectedPeople}" CanUserAddRows="True"/>
<StackPanel Grid.Row="1" Grid.ColumnSpan="2">
<Button DockPanel.Dock="Top" Content="Select All" Command="{Binding SelectAllCommand}"/>
<Button DockPanel.Dock="Top" Content="Unselect All" Command="{Binding UnselectAllCommand}"/>
<Button DockPanel.Dock="Top" Content="Select Next Range" Command="{Binding SelectNextRangeCommand}"/>
</StackPanel>
</Grid>
</Window>
Model.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Input;
namespace WpfApplication
{
abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void Set<T>(ref T field, T value, string propertyName)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
}
sealed class DelegateCommand : ICommand
{
private readonly Action action;
public DelegateCommand(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
this.action = action;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute()
{
this.action();
}
bool ICommand.CanExecute(object parameter)
{
return true;
}
void ICommand.Execute(object parameter)
{
this.Execute();
}
}
class Person : ObservableObject
{
private string name, surname;
public Person()
{
}
public Person(string name, string surname)
{
this.name = name;
this.surname = surname;
}
public string Name
{
get { return this.name; }
set { this.Set(ref this.name, value, "Name"); }
}
public string Surname
{
get { return this.surname; }
set { this.Set(ref this.surname, value, "Surname"); }
}
public override string ToString()
{
return this.name + ' ' + this.surname;
}
}
class MainWindowModel : ObservableObject
{
public ObservableCollection<Person> People { get; private set; }
public SelectedItemCollection<Person> SelectedPeople { get; private set; }
public DelegateCommand SelectAllCommand { get; private set; }
public DelegateCommand UnselectAllCommand { get; private set; }
public DelegateCommand SelectNextRangeCommand { get; private set; }
public MainWindowModel()
{
this.People = new ObservableCollection<Person>(Enumerable.Range(1, 1000).Select(i => new Person("Name " + i, "Surname " + i)));
this.SelectedPeople = new SelectedItemCollection<Person>();
for (int i = 0; i < this.People.Count; i += 2)
this.SelectedPeople.Add(this.People[i]);
this.SelectAllCommand = new DelegateCommand(() => this.SelectedPeople.Reset(this.People));
this.UnselectAllCommand = new DelegateCommand(() => this.SelectedPeople.Clear());
this.SelectNextRangeCommand = new DelegateCommand(() =>
{
var index = this.SelectedPeople.Count > 0 ? this.People.IndexOf(this.SelectedPeople[this.SelectedPeople.Count - 1]) + 1 : 0;
int count = 10;
this.SelectedPeople.Reset(Enumerable.Range(index, count).Where(i => i < this.People.Count).Select(i => this.People[i]));
});
this.SelectedPeople.CollectionChanged += this.OnSelectedPeopleCollectionChanged;
}
private void OnSelectedPeopleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Debug.WriteLine("Action = {0}, NewItems.Count = {1}, NewStartingIndex = {2}, OldItems.Count = {3}, OldStartingIndex = {4}, Total.Count = {5}", e.Action, e.NewItems != null ? e.NewItems.Count : 0, e.NewStartingIndex, e.OldItems != null ? e.OldItems.Count : 0, e.OldStartingIndex, this.SelectedPeople.Count);
}
}
class SelectedItemCollection<T> : ObservableCollection<T>
{
public void Reset(IEnumerable<T> items)
{
int oldCount = this.Count;
this.Items.Clear();
foreach (var item in items)
this.Items.Add(item);
if (!(oldCount == 0 && this.Count == 0))
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
if (this.Count != oldCount)
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
}
}
}
}
Following is the implementation, which unfortunately is not documented. Implementation uses various tricks (via reflection) to reduce the number of changes to underlying collections as much as possible (to suspend excessive collection changed notifications).
It is worth noting that if selected items collection in model has Select(IEnumerable) or Select(IEnumerable) method, that method will be used for performing bulk updates (updates which affect more than one item) which offers better performance if, for example, all items in the DataGrid are selected or un-selected.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Markup;
namespace WpfApplication
{
static class MultiSelectorExtension
{
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(MultiSelectorExtension), new PropertyMetadata(new PropertyChangedCallback(OnSelectedItemsChanged)));
private static readonly DependencyProperty SelectedItemsBinderProperty = DependencyProperty.RegisterAttached("SelectedItemsBinder", typeof(SelectedItemsBinder), typeof(MultiSelectorExtension));
[AttachedPropertyBrowsableForType(typeof(MultiSelector))]
[DependsOn("ItemsSource")]
public static IList GetSelectedItems(this MultiSelector multiSelector)
{
if (multiSelector == null)
throw new ArgumentNullException("multiSelector");
return (IList)multiSelector.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(this MultiSelector multiSelector, IList selectedItems)
{
if (multiSelector == null)
throw new ArgumentNullException("multiSelector");
multiSelector.SetValue(SelectedItemsProperty, selectedItems);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var multiSelector = d as MultiSelector;
if (multiSelector == null)
return;
var binder = (SelectedItemsBinder)multiSelector.GetValue(SelectedItemsBinderProperty);
var selectedItems = e.NewValue as IList;
if (selectedItems != null)
{
if (binder == null)
binder = new SelectedItemsBinder(multiSelector);
binder.SelectedItems = selectedItems;
}
else if (binder != null)
binder.Dispose();
}
private sealed class SelectedItemsBinder : IDisposable
{
private static readonly IList emptyList = new object[0];
private static readonly Action<MultiSelector> multiSelectorBeginUpdateSelectedItems, multiSelectorEndUpdateSelectedItems;
private readonly MultiSelector multiSelector;
private IList selectedItems;
private IResetter selectedItemsResetter;
private bool suspendMultiSelectorUpdate, suspendSelectedItemsUpdate;
static SelectedItemsBinder()
{
GetMultiSelectorBeginEndUpdateSelectedItems(out multiSelectorBeginUpdateSelectedItems, out multiSelectorEndUpdateSelectedItems);
}
public SelectedItemsBinder(MultiSelector multiSelector)
{
this.multiSelector = multiSelector;
this.multiSelector.SelectionChanged += this.OnMultiSelectorSelectionChanged;
this.multiSelector.Unloaded += this.OnMultiSelectorUnloaded;
this.multiSelector.SetValue(SelectedItemsBinderProperty, this);
}
public IList SelectedItems
{
get { return this.selectedItems; }
set
{
this.SetSelectedItemsChangedHandler(false);
this.selectedItems = value;
this.selectedItemsResetter = GetResetter(this.selectedItems.GetType());
this.SetSelectedItemsChangedHandler(true);
if (this.multiSelector.IsLoaded)
this.OnSelectedItemsCollectionChanged(this.selectedItems, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
else
{
RoutedEventHandler multiSelectorLoadedHandler = null;
this.multiSelector.Loaded += multiSelectorLoadedHandler = new RoutedEventHandler((sender, e) =>
{
this.OnSelectedItemsCollectionChanged(this.selectedItems, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
this.multiSelector.Loaded -= multiSelectorLoadedHandler;
});
}
}
}
private int ItemsSourceCount
{
get
{
var collection = this.multiSelector.ItemsSource as ICollection;
return collection != null ? collection.Count : -1;
}
}
public void Dispose()
{
this.multiSelector.ClearValue(SelectedItemsBinderProperty);
this.multiSelector.Unloaded -= this.OnMultiSelectorUnloaded;
this.multiSelector.SelectionChanged -= this.OnMultiSelectorSelectionChanged;
this.SetSelectedItemsChangedHandler(false);
}
private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (this.suspendMultiSelectorUpdate || e.Action == NotifyCollectionChangedAction.Move)
return;
this.suspendSelectedItemsUpdate = true;
if (this.selectedItems.Count == 0)
this.multiSelector.UnselectAll();
else if (this.selectedItems.Count == this.ItemsSourceCount)
this.multiSelector.SelectAll();
else if (e.Action != NotifyCollectionChangedAction.Reset && (e.NewItems == null || e.NewItems.Count <= 1) && (e.OldItems == null || e.OldItems.Count <= 1))
UpdateList(this.multiSelector.SelectedItems, e.NewItems ?? emptyList, e.OldItems ?? emptyList);
else
{
if (multiSelectorBeginUpdateSelectedItems != null)
{
multiSelectorBeginUpdateSelectedItems(this.multiSelector);
this.multiSelector.SelectedItems.Clear();
UpdateList(this.multiSelector.SelectedItems, this.selectedItems, emptyList);
multiSelectorEndUpdateSelectedItems(this.multiSelector);
}
else
{
this.multiSelector.UnselectAll();
UpdateList(this.multiSelector.SelectedItems, this.selectedItems, emptyList);
}
}
this.suspendSelectedItemsUpdate = false;
}
private void OnMultiSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.suspendSelectedItemsUpdate)
return;
this.suspendMultiSelectorUpdate = true;
if (e.AddedItems.Count <= 1 && e.RemovedItems.Count <= 1)
UpdateList(this.selectedItems, e.AddedItems, e.RemovedItems);
else
{
if (this.selectedItemsResetter != null)
this.selectedItemsResetter.Reset(this.selectedItems, this.multiSelector.SelectedItems.Cast<object>().Where(item => item != CollectionView.NewItemPlaceholder));
else
UpdateList(this.selectedItems, e.AddedItems, e.RemovedItems);
}
this.suspendMultiSelectorUpdate = false;
}
private void OnMultiSelectorUnloaded(object sender, RoutedEventArgs e)
{
this.Dispose();
}
private void SetSelectedItemsChangedHandler(bool add)
{
var notifyCollectionChanged = this.selectedItems as INotifyCollectionChanged;
if (notifyCollectionChanged != null)
{
if (add)
notifyCollectionChanged.CollectionChanged += this.OnSelectedItemsCollectionChanged;
else
notifyCollectionChanged.CollectionChanged -= this.OnSelectedItemsCollectionChanged;
}
}
private static void UpdateList(IList list, IList newItems, IList oldItems)
{
int addedCount = 0;
for (int i = 0; i < oldItems.Count; ++i)
{
var index = list.IndexOf(oldItems[i]);
if (index >= 0)
{
object newItem;
if (i < newItems.Count && (newItem = newItems[i]) != CollectionView.NewItemPlaceholder)
{
list[index] = newItem;
++addedCount;
}
else
list.RemoveAt(index);
}
}
for (int i = addedCount; i < newItems.Count; ++i)
{
var newItem = newItems[i];
if (newItem != CollectionView.NewItemPlaceholder)
list.Add(newItem);
}
}
private static void GetMultiSelectorBeginEndUpdateSelectedItems(out Action<MultiSelector> beginUpdateSelectedItems, out Action<MultiSelector> endUpdateSelectedItems)
{
try
{
beginUpdateSelectedItems = (Action<MultiSelector>)Delegate.CreateDelegate(typeof(Action<MultiSelector>), typeof(MultiSelector).GetMethod("BeginUpdateSelectedItems", BindingFlags.NonPublic | BindingFlags.Instance));
endUpdateSelectedItems = (Action<MultiSelector>)Delegate.CreateDelegate(typeof(Action<MultiSelector>), typeof(MultiSelector).GetMethod("EndUpdateSelectedItems", BindingFlags.NonPublic | BindingFlags.Instance));
}
catch
{
beginUpdateSelectedItems = endUpdateSelectedItems = null;
}
}
private static IResetter GetResetter(Type listType)
{
try
{
MethodInfo genericReset = null, nonGenericReset = null;
Type genericResetItemType = null;
foreach (var method in listType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if (method.Name != "Reset")
continue;
if (method.ReturnType != typeof(void))
continue;
var parameters = method.GetParameters();
if (parameters.Length != 1)
continue;
var parameterType = parameters[0].ParameterType;
if (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
genericResetItemType = parameterType.GetGenericArguments()[0];
genericReset = method;
break;
}
else if (parameterType == typeof(IEnumerable))
nonGenericReset = method;
}
if (genericReset != null)
return (IResetter)Activator.CreateInstance(typeof(GenericResetter<,>).MakeGenericType(genericReset.DeclaringType, genericResetItemType), genericReset);
else if (nonGenericReset != null)
return (IResetter)Activator.CreateInstance(typeof(NonGenericResetter<>).MakeGenericType(nonGenericReset.DeclaringType), nonGenericReset);
else
return null;
}
catch
{
return null;
}
}
private interface IResetter
{
void Reset(IList list, IEnumerable items);
}
private sealed class NonGenericResetter<TTarget> : IResetter
{
private readonly Action<TTarget, IEnumerable> reset;
public NonGenericResetter(MethodInfo method)
{
this.reset = (Action<TTarget, IEnumerable>)Delegate.CreateDelegate(typeof(Action<TTarget, IEnumerable>), method);
}
public void Reset(IList list, IEnumerable items)
{
this.reset((TTarget)list, items);
}
}
private sealed class GenericResetter<TTarget, T> : IResetter
{
private readonly Action<TTarget, IEnumerable<T>> reset;
public GenericResetter(MethodInfo method)
{
this.reset = (Action<TTarget, IEnumerable<T>>)Delegate.CreateDelegate(typeof(Action<TTarget, IEnumerable<T>>), method);
}
public void Reset(IList list, IEnumerable items)
{
this.reset((TTarget)list, items.Cast<T>());
}
}
}
}
}

Create a command that fires on the DataGrid's SelectionChanged event, passing in the DataGrid's SelectedItems.
In your ViewModel, have a List of selected objects.
Your SelectionChangedCommand execution method would then update that collection of selected objects.
For example:
In my XAML:
<DataGrid ItemsSource="{Binding Datasets, NotifyOnTargetUpdated=True}" Name="dsDatagrid" SelectionMode="Extended" MouseDoubleClick="ViewDataset">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding ElementName=dsDatagrid, Path=SelectedItems}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
In my ViewModel:
private List<ObservableDataset> selectedDatasets;
private void SelectionChangedExecuted(object datasets)
{
this.selectedDatasets = new List<ObservableDataset>((datasets as IList).Cast<ObservableDataset>());
}
EDIT: I'm using MVVMLight.

Related

WPF ComboBox Not updating on propertychanged event

I have a custom combobox in my WPF application and the combo box is not updating when my item source is updated after initial launch. The itemsSource is a custom observableDictionary. I've implemented INotifyPropertyChanged on my properties with my observableDictionary but it won't update. I've searched through every WPF propertychanged event not working on stack overflow and i'm looking for some assistance.
Custom Combobox Code:
<controls:MultiSelectComboBox Width="100" Height="30" Padding="0 10 0 0" Grid.Row="0" Grid.Column="1"
ItemsSource="{Binding Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
Path=DataContext.OptionTeamMembersDictionary,
BindsDirectlyToSource=True,
RelativeSource={RelativeSource AncestorType={x:Type UserControl},Mode=FindAncestor}}"
SelectedItems="{Binding SelectedOptionTeamMembersDictionary, Mode=TwoWay}"
x:Name="TeamMemberDisplay"
ToolTip="{Binding Path=Text, RelativeSource={RelativeSource Self}}"/>
Properties and private variables:
private ObservableDictionary<string, object> _optionTeamMembersDictionary;
private ObservableDictionary<string, object> _selectedOptionTeamMembersDictionary;
public ObservableDictionary<string, object> OptionTeamMembersDictionary
{
get
{
return _optionTeamMembersDictionary;
}
set
{
_optionTeamMembersDictionary = value;
OnPropertyChanged("OptionTeamMembersDictionary");
}
}
public ObservableDictionary<string, object> SelectedOptionTeamMembersDictionary
{
get
{
return _selectedOptionTeamMembersDictionary;
}
set
{
_selectedOptionTeamMembersDictionary = value;
OnPropertyChanged("SelectedOptionTeamMembersDictionary");
}
}
I use a button to trigger a database pull which leads each row into an object and then populates the optionTeamMemberDictionary when it returns more then one row of data.
All of the above works when the data is loaded in my constructor but when its loaded after launch my comboboxes do not show the new data in the collection.
EDIT:
Code for the Custom ComboBox being used:
https://www.codeproject.com/Articles/563862/Multi-Select-ComboBox-in-WPF is the URL this came from. Some edits were made to implement an obserableDirctionary instead of normal dictionary
public partial class MultiSelectComboBox : UserControl
{
private ObservableCollection<Node> _nodeList;
public MultiSelectComboBox()
{
InitializeComponent();
_nodeList = new ObservableCollection<Node>();
}
#region Dependency Properties
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(ObservableDictionary<string, object>), typeof(MultiSelectComboBox), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(MultiSelectComboBox.OnItemsSourceChanged)));
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(ObservableDictionary<string, object>), typeof(MultiSelectComboBox), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(MultiSelectComboBox.OnSelectedItemsChanged)));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MultiSelectComboBox), new UIPropertyMetadata(string.Empty));
public static readonly DependencyProperty DefaultTextProperty =
DependencyProperty.Register("DefaultText", typeof(string), typeof(MultiSelectComboBox), new UIPropertyMetadata(string.Empty));
public ObservableDictionary<string, object> ItemsSource
{
get { return (ObservableDictionary<string, object>)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
}
}
public ObservableDictionary<string, object> SelectedItems
{
get { return (ObservableDictionary<string, object>)GetValue(SelectedItemsProperty); }
set
{
SetValue(SelectedItemsProperty, value);
}
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string DefaultText
{
get { return (string)GetValue(DefaultTextProperty); }
set { SetValue(DefaultTextProperty, value); }
}
#endregion
#region Events
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MultiSelectComboBox control = (MultiSelectComboBox)d;
control.DisplayInControl();
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MultiSelectComboBox control = (MultiSelectComboBox)d;
control.SelectNodes();
control.SetText();
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox clickedBox = (CheckBox)sender;
if (clickedBox.Content == "All")
{
if (clickedBox.IsChecked.Value)
{
foreach (Node node in _nodeList)
{
node.IsSelected = true;
}
}
else
{
foreach (Node node in _nodeList)
{
node.IsSelected = false;
}
}
}
else
{
int _selectedCount = 0;
foreach (Node s in _nodeList)
{
if (s.IsSelected && s.Title != "All")
_selectedCount++;
}
if (_selectedCount == _nodeList.Count - 1)
_nodeList.FirstOrDefault(i => i.Title == "All").IsSelected = true;
else
_nodeList.FirstOrDefault(i => i.Title == "All").IsSelected = false;
}
SetSelectedItems();
SetText();
}
#endregion
#region Methods
private void SelectNodes()
{
foreach (KeyValuePair<string, object> keyValue in SelectedItems)
{
Node node = _nodeList.FirstOrDefault(i => i.Title == keyValue.Key);
if (node != null)
node.IsSelected = true;
}
}
private void SetSelectedItems()
{
if (SelectedItems == null)
SelectedItems = new ObservableDictionary<string, object>();
SelectedItems.Clear();
foreach (Node node in _nodeList)
{
if (node.IsSelected && node.Title != "All")
{
if (this.ItemsSource.Count > 0)
SelectedItems.Add(node.Title, this.ItemsSource[node.Title]);
}
}
}
private void DisplayInControl()
{
_nodeList.Clear();
if (this.ItemsSource.Count > 0)
_nodeList.Add(new Node("All"));
foreach (KeyValuePair<string, object> keyValue in this.ItemsSource)
{
Node node = new Node(keyValue.Key);
_nodeList.Add(node);
}
MultiSelectCombo.ItemsSource = _nodeList;
}
private void SetText()
{
if (this.SelectedItems != null)
{
StringBuilder displayText = new StringBuilder();
foreach (Node s in _nodeList)
{
if (s.IsSelected == true && s.Title == "All")
{
displayText = new StringBuilder();
displayText.Append("All");
break;
}
else if (s.IsSelected == true && s.Title != "All")
{
displayText.Append(s.Title);
displayText.Append(',');
}
}
this.Text = displayText.ToString().TrimEnd(new char[] { ',' });
}
// set DefaultText if nothing else selected
if (string.IsNullOrEmpty(this.Text))
{
this.Text = this.DefaultText;
}
}
#endregion
}
public class Node : INotifyPropertyChanged
{
private string _title;
private bool _isSelected;
#region ctor
public Node(string title)
{
Title = title;
}
#endregion
#region Properties
public string Title
{
get
{
return _title;
}
set
{
_title = value;
NotifyPropertyChanged("Title");
}
}
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
After much troubleshooting i was able to figure out an answer for this. onItemsSourceChanged was not firing after the control was instantiated so it never got updates made after initial launch of the application. I edited the OnItemsSourceChanged Function on the MultiFunctionComboBox to the below so it would fire on a changed event and it is working as expected now.
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MultiSelectComboBox control = (MultiSelectComboBox)d;
var action = new NotifyCollectionChangedEventHandler(
(o, args) =>
{
MultiSelectComboBox c = (MultiSelectComboBox)d;
c?.DisplayInControl();
});
if (e.OldValue != null)
{
var sourceCollection = (ObservableDictionary<string, object>)e.OldValue;
sourceCollection.CollectionChanged -= action;
}
if (e.NewValue != null)
{
var sourceCollection = (ObservableDictionary<string, object>)e.NewValue;
sourceCollection.CollectionChanged += action;
}
control.DisplayInControl();
}

Combobox SelectedItem Becomes Null

I'm attempting to make a ComboBox that switches between objects. The general gist is that an object has a Key that appears in the ComboBox and a Data component that can theoretically be anything. The Data component is complex while the Key is just a string. For the example below, Data is just a Uri, it actually turns out the type of the Data doesn't matter.
The basic intent is to bind the SelectedItem of the ComboBox to the Model so that the Data of the SelectedItem can be modified through other interactions.
The code is setup such that a couple items are added to the ComboBox, and then the SelectedItem is choose as the first element. That works just fine. When I then click the button, the SelectedItem is assigned null where I throw the exception.
Why does the SelectedItem get assigned null?
Here is complete working code; my target is .NET 4.0, but I'm guessing it doesn't matter too much. Xaml follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Sandbox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public Model Model { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Model = new Model();
this.Model.Items.Add(
new ObservableKeyValuePair<string, Uri>()
{
Key = "Apple",
Value = new Uri("http://apple.com")
});
this.Model.Items.Add(
new ObservableKeyValuePair<string, Uri>()
{
Key = "Banana",
Value = new Uri("http://Banana.net")
});
this.Model.SelectedItem = this.Model.Items.First();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Model.SelectedItem.Value = new Uri("http://cranberry.com");
}
}
public class TrulyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
public TrulyObservableCollection()
{
CollectionChanged += FullObservableCollectionCollectionChanged;
}
public TrulyObservableCollection(IEnumerable<T> pItems)
: this()
{
foreach (var item in pItems)
{
this.Add(item);
}
}
private void FullObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
public class ObservableKeyValuePair<TKey, TValue> :
INotifyPropertyChanged,
IEquatable<ObservableKeyValuePair<TKey, TValue>>
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
public override bool Equals(object rhs)
{
var obj = rhs as ObservableKeyValuePair<TKey, TValue>;
if (obj != null)
{
return this.Key.Equals(obj.Key);
}
return false;
}
public bool Equals(ObservableKeyValuePair<TKey, TValue> other)
{
return this.Key.Equals(other.Key);
}
public override int GetHashCode()
{
return this.Key.GetHashCode();
}
protected TKey _Key;
public TKey Key
{
get
{
return _Key;
}
set
{
if (value is INotifyPropertyChanged)
{
(value as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(KeyChanged);
}
_Key = value;
OnPropertyChanged("Key");
}
}
void KeyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("Key");
}
protected TValue _Value;
public TValue Value
{
get
{
return _Value;
}
set
{
if (value is INotifyPropertyChanged)
{
(value as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(ValueChanged);
}
_Value = value;
OnPropertyChanged("Value");
}
}
void ValueChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("Value");
}
}
public class Model : INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public Model()
{
Items = new TrulyObservableCollection<ObservableKeyValuePair<string, Uri>>();
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
public TrulyObservableCollection<ObservableKeyValuePair<string, Uri>> Items { get; set; }
public ObservableKeyValuePair<string, Uri> _SelectedItem = null;
public ObservableKeyValuePair<string, Uri> SelectedItem
{
get
{
return Items.FirstOrDefault(x => _SelectedItem != null && x.Key == _SelectedItem.Key);
}
set
{
if (value == null)
{
throw new Exception("This is the problem");
}
if (_SelectedItem != value)
{
_SelectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
}
}
Xaml:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Click="Button_Click" Content="Clsick Me"/>
<ComboBox IsEditable="False" ItemsSource="{Binding Model.Items}" SelectedItem="{Binding Model.SelectedItem, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}">
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Window>
I'm at a total lose trying to explain why "value" is null.
The underlying problem is in the implementation of Selector.OnItemsChanged. Towards the end of the method, the currently SelectedItem is nulled.
I worked around this by deriving a new ComboBox class that overrides OnItemsChanged, saves the current SelectedItem, calls base.OnItemsChanged and then resets the SelectedItem. This may require the propagation of an "InhibitEvents" flag into the model if the SelectedItem transition from valid=>null=>valid is not desired.
The value becomes null because you are trying to assign a selected item value which is not in the collection

Can not find child control on ItemContainerGenerator?

I'm using ItemsControl for StackPanel as below code:
File playBackControl.xaml - Begin
<ScrollViewer x:Name="scrollViewerChannelBtns">
<StackPanel x:Name="channelBtns" Orientation="Vertical" MouseWheel="ScrollViewer_MouseWheel">
<ItemsControl x:Name="channelBtnItems" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToogleButton x:Name="tgbChannelName"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="{Binding Path=ChannelNameBtnWidth}"
Height="{Binding Path=ChannelNameBtnHeight}"
Margin="{Binding Path=ChannelNameBtnMargin}"
IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"
ToolTip="{Binding Path=ToolTip}" Tag="{Binding Path=Index}"
IsEnabled="{Binding Path=IsEnabled}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
File playBackControl.xaml - End
File playBackControl.xaml.cs - Begin
public partial class PlayBackControl : UserControl
{
public static List<ChannelBtnItemData> listChannelBtnItemData = new List<ChannelBtnItemData>();
public PlayBackControl() //This will run first when the app start
{
InitializeComponent();
channelBtnItems.ItemContainerGenerator.StatusChanged += ChannelBtnItemsStatusChangedEventHandler;
System.Threading.Thread threadTimer = new System.Threading.Thread(TimerThreadThreadProc);
threadTimer.Start();
}
private void TimerThreadThreadProc()
{
while (true)
{
Thread.Sleep(60000); //Sleep 60s
this.Dispatcher.BeginInvoke(new Action(delegate()
{
//Re init listChannelBtnItemData, this list has about 64 items
channelBtnItems.ItemsSource = listChannelBtnItemData;
channelBtnItems.Items.Refresh();
}));
}
}
public class ChannelBtnItemData : INotifyPropertyChanged
{
private String _toolTip;
private int _index;
private int _channelID;
private bool _isChecked;
private bool _isEnabled;
private bool _lockToggle;
private double _channelNameBtnWidth;
private double _channelNameBtnHeight;
private Thickness _channelNameBtnMargin;
public String ToolTip
{
get { return _toolTip; }
set
{
_toolTip = value;
OnPropertyChanged("ToolTip");
}
}
public int Index
{
get { return _index; }
set
{
_index = value;
OnPropertyChanged("Index");
}
}
public int ChannelID
{
get { return _channelID; }
set
{
_channelID = value;
OnPropertyChanged("ChannelID");
}
}
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
OnPropertyChanged("IsEnabled");
}
}
public bool LockToggle
{
get { return _lockToggle; }
set
{
_lockToggle = value;
OnPropertyChanged("LockToggle");
}
}
public double ChannelNameBtnWidth
{
get { return _channelNameBtnWidth; }
set
{
_channelNameBtnWidth = value;
OnPropertyChanged("ChannelNameBtnWidth");
}
}
public double ChannelNameBtnHeight
{
get { return _channelNameBtnHeight; }
set
{
_channelNameBtnHeight = value;
OnPropertyChanged("ChannelNameBtnHeight");
}
}
public Thickness ChannelNameBtnMargin
{
get { return _channelNameBtnMargin; }
set
{
_channelNameBtnMargin = value;
OnPropertyChanged("ChannelNameBtnMargin");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public ChannelBtnItemData()
{
}
}
private void ChannelBtnItemsStatusChangedEventHandler(Object sender, EventArgs e)
{
if (channelBtnItems.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
if (channelBtnItems.HasItems)
{
var containers = channelBtnItems.Items.Cast<Object>().Select(
item => (FrameworkElement)channelBtnItems.ItemContainerGenerator.ContainerFromItem(item));
foreach (var container in containers)
{
if (container != null)
container.Loaded += ChannelBtnItemContainerLoaded;
}
}
}
}
private void ChannelBtnItemContainerLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
element.Loaded -= ChannelBtnItemContainerLoaded;
ToogleButton tgbChannelName = FindChild<ToogleButton>(element, "tgbChannelName");
if (tgbChannelName != null) //Sometimes It equal null
{
//Do something
}
}
public T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
else
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
}
File playBackControl.xaml.cs - End
In the ChannelBtnItemContainerLoaded() function, sometimes the tgbChannelName equal null,
I have searched and read more about this, but I don't know how to fix it.
I create a demo to test this but it worked for me. Loaded event is being fired when all is there available to access and so I never ran into the state where instance was null.
However I have a feeling that you haven't revealed us what you exactly up to there.
Are you changing the ItemsSource at runtime? At what point do you swap the ItemsSource?
Where is the following code of yours being called?
channelBtnItems.ItemContainerGenerator.StatusChanged += ChannelBtnItemsStatusChangedEventHandler;
channelBtnItems.ItemsSource = listChannelBtnItemData;
channelBtnItems.Items.Refresh();
However here is a trick how you can "postpone" an action.
Use the Dispatcher.BeginInvoke with DispatcherPrority.Background.
http://weblogs.asp.net/pawanmishra/archive/2010/06/06/understanding-dispatcher-in-wpf.aspx
private void ChannelBtnItemContainerLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
element.Loaded -= ChannelBtnItemContainerLoaded;
element.Dispatcher.BeginInvoke((Action)(() =>
{
ToggleButton tgbChannelName = FindChild<ToggleButton>(element, "tgbChannelName");
if (tgbChannelName != null) //Sometimes It equal null
{
//Do something
}
else
{
}
}), DispatcherPriority.Background);
}
Try it out. If this doesnt help please provide us with the complete code including data and viewmodel so can test on same code just like yours.

WPF Filter a ListBox

I have a ListBox with binding to a list of strings. I want to filter the list when I enter text in a TextBox. How can I do it?
public void ListLoad()
{
ElementList = new List<string>(); // creation a list of strings
ElementList.Add("1"); // add a item of string
ElementList.Add("2"); // add a item of string
DataContext = this; // set the data context
}
I'm binding it in XAML with:
ItemsSource="{Binding ElementList}"
CollectionViewSource class can help here. As far as I can tell it has many capabilities to filter, sort and group collections.
ICollectionView view = CollectionViewSource.GetDefaultView(ElementList);
view.Filter = (o) => {return o;}//here is the lambda with your conditions to filter
When you don't need any filter just set view.Filter to null.
Also check out this article on filtering
Here is an attached property for binding a filter:
using System;
using System.Windows;
using System.Windows.Controls;
public static class Filter
{
public static readonly DependencyProperty ByProperty = DependencyProperty.RegisterAttached(
"By",
typeof(Predicate<object>),
typeof(Filter),
new PropertyMetadata(default(Predicate<object>), OnByChanged));
public static void SetBy(ItemsControl element, Predicate<object> value)
{
element.SetValue(ByProperty, value);
}
public static Predicate<object> GetBy(ItemsControl element)
{
return (Predicate<object>)element.GetValue(ByProperty);
}
private static void OnByChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ItemsControl itemsControl &&
itemsControl.Items.CanFilter)
{
itemsControl.Items.Filter = (Predicate<object>)e.NewValue;
}
}
}
Used like this in xaml:
<DataGrid local:Filter.By="{Binding Filter}"
ItemsSource="{Binding Foos}">
...
And viewmodel:
public class ViewModel : INotifyPropertyChanged
{
private string filterText;
private Predicate<object> filter;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Foo> Foos { get; } = new ObservableCollection<Foo>();
public string FilterText
{
get { return this.filterText; }
set
{
if (value == this.filterText) return;
this.filterText = value;
this.OnPropertyChanged();
this.Filter = string.IsNullOrEmpty(this.filterText) ? (Predicate<object>)null : this.IsMatch;
}
}
public Predicate<object> Filter
{
get { return this.filter; }
private set
{
this.filter = value;
this.OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool IsMatch(object item)
{
return IsMatch((Foo)item, this.filterText);
}
private static bool IsMatch(Foo item, string filterText)
{
if (string.IsNullOrEmpty(filterText))
{
return true;
}
var name = item.Name;
if (string.IsNullOrEmpty(name))
{
return false;
}
if (filterText.Length == 1)
{
return name.StartsWith(filterText, StringComparison.OrdinalIgnoreCase);
}
return name.IndexOf(filterText, 0, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
If you set Dictionary as itemsource to listbox use the below code to sort,
private void tb_filter_textChanged(object sender, TextChangedEventArgs e)
{
Dictionary<string, string> dictObject = new Dictionary<string, string>();
ICollectionView view = CollectionViewSource.GetDefaultView(dictObject);
view.Filter = CustomerFilter;
listboxname.ItemsSource = view;
}
private bool CustomerFilter(object item)
{
KeyValuePair<string, string> Items = (KeyValuePair<string,string>) item;
return Items.Value.ToString().Contains("a");
}
The above code returns the items contaning "a".

MultiSelection not working in ListView

I have problem in ListView MultiSelection.
Here is my code :
private ICommand _excludeCurveCommand;
public ICommand ExcludeCurveCommand
{
get
{
if (_excludeCurveCommand == null)
_excludeCurveCommand = new DelegateCommand<object>(param => this.ExcludeCurveExecuted(param));
return _excludeCurveCommand;
}
}
/// <summary>
/// Executed event for ContextMenu --> Remove click
/// </summary>
/// <param name="param">param is FolderItemViewModel bound as CommandParameter</param>
private void ExcludeCurveExecuted(object param)
{
ICollection<object> curves = param as ICollection<object>;
int count = curves.Count;
}
< Button Content="Exclude" Command="{Binding ExcludeCurveCommand}" CommandParameter="{Binding SelectedItems,ElementName=lstView}" .../>
< ListView Name="lstView" ItemsSource="{Binding MyList}" SelectionMode="Extended" ..../>
Here i used shift and selected from first four items. But in ExcludeCurveExecuted method i getting some random counts always. What is the issue in this.
EDIT:
* I don't wantto go with IsSelected flag in ViewModel
To me it seems that you have have ListView UI virtualization enabled, as it enabled by default. That means, that ListView draws only items visible on UI.
Here is MSDN link
To be sure what is going on interface (like selection in your case) I'm afraid you don't have any other option then
or disable UI virtualization (not good)
or implement IsSelected flag on ModelView
Good luck.
You can use a Behavior to Sync the Selected Items to a Collection in Your View Model....
You a ve to add a reference to System.Windows.Interactivity to your project as well as an alias to it in your Xaml
Beahvior
public class MultiSelectionBehavior : Behavior<ListView>
{
protected override void OnAttached()
{
base.OnAttached();
if (SelectedItems != null)
{
AssociatedObject.SelectedItems.Clear();
foreach (var item in SelectedItems)
{
AssociatedObject.SelectedItems.Add(item);
}
}
}
public IList SelectedItems
{
get { return (IList)GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(IList), typeof(MultiSelectionBehavior), new UIPropertyMetadata(null, SelectedItemsChanged));
private static void SelectedItemsChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var behavior = o as MultiSelectionBehavior;
if (behavior == null)
return;
var oldValue = e.OldValue as INotifyCollectionChanged;
var newValue = e.NewValue as INotifyCollectionChanged;
if (oldValue != null)
{
oldValue.CollectionChanged -= behavior.SourceCollectionChanged;
behavior.AssociatedObject.SelectionChanged -= behavior.ListViewSelectionChanged;
}
if (newValue != null)
{
behavior.AssociatedObject.SelectedItems.Clear();
foreach (var item in (IEnumerable)newValue)
{
behavior.AssociatedObject.SelectedItems.Add(item);
}
behavior.AssociatedObject.SelectionChanged += behavior.ListViewSelectionChanged;
newValue.CollectionChanged += behavior.SourceCollectionChanged;
}
}
private bool _isUpdatingTarget;
private bool _isUpdatingSource;
void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_isUpdatingSource)
return;
try
{
_isUpdatingTarget = true;
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
AssociatedObject.SelectedItems.Remove(item);
}
}
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
AssociatedObject.SelectedItems.Add(item);
}
}
}
finally
{
_isUpdatingTarget = false;
}
}
private void ListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isUpdatingTarget)
return;
var selectedItems = this.SelectedItems;
if (selectedItems == null)
return;
try
{
_isUpdatingSource = true;
foreach (var item in e.RemovedItems)
{
selectedItems.Remove(item);
}
foreach (var item in e.AddedItems)
{
selectedItems.Add(item);
}
}
finally
{
_isUpdatingSource = false;
}
}
}
Xaml
<ListView ItemsSource="{Binding ItemsList}"
DisplayMemberPath="Name"
SelectionMode="Extended">
<i:Interaction.Behaviors>
<local:MultiSelectionBehavior SelectedItems="{Binding MySelectedItems}" />
</i:Interaction.Behaviors>
</ListView>
MySelectedItems is the collection in your ViewModel
Dont forget it initialize ... MySelectedItems in your ViewModel....
Your ViewModel
private void ExcludeCurveExecuted(object param)
{
int count = MySelectedItems.Count;
}

Categories