Reputation: 35627
Is it possible to do bind a Command to the ListView's ItemSelectionChanged? How? If not possible, is there another way to call a command on when the list view's selection changes?
Upvotes: 0
Views: 1654
Reputation: 22435
at first, i use in my project a command where the selectedItems a passed as parameter. downside of this approach the user has to klick the command button to start the action, but this fulfill my requirements in most cases.
on way to achieve what you want is something like erash posted - to use a kind of EventToCommand behavior like mvvm-light has.
another way is to create a attached behavior to bind to the selecteditems in your viewmodel. you have to adept the following code to ListView or create something more general :)
<DataGrid AttachedProperties:DatagridService.SelectedItemsSource="{Binding SelectedItems, Mode=OneWay}" />
ViewModel:
public ObservableCollection<object> SelectedItems//init collection just ONCE!
{
get
{
if (this.selectedItems == null)
{
this.selectedItems = new ObservableCollection<object>();
this.selectedItems.CollectionChanged +=
new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SelectedItemsCollectionChanged);
}
return this.selectedItems;
}
}
private void SelectedItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//this is called when ever the selecteditems changed
}
Attached Behavior:
public static class DatagridService
{
#region SelectedItemSource
#region Attached Property Declaration
/// <summary>
/// Identifies the ListBoxExtension.SelectedItemsSource attached property.
/// </summary>
public static readonly DependencyProperty SelectedItemsSourceProperty =
DependencyProperty.RegisterAttached(
"SelectedItemsSource",
typeof(IList),
typeof(DatagridService),
new PropertyMetadata(
null,
new PropertyChangedCallback(OnSelectedItemsSourceChanged)));
#endregion
#region Attached Property Accessors
/// <summary>
/// Gets the IList that contains the values that should be selected.
/// </summary>
/// <param name="element">The ListBox to check.</param>
public static IList GetSelectedItemsSource(DependencyObject element)
{
if (element == null)
throw new ArgumentNullException("element");
return (IList)element.GetValue(SelectedItemsSourceProperty);
}
/// <summary>
/// Sets the IList that contains the values that should be selected.
/// </summary>
/// <param name="element">The ListBox being set.</param>
/// <param name="value">The value of the property.</param>
public static void SetSelectedItemsSource(DependencyObject element, IList value)
{
if (element == null)
throw new ArgumentNullException("element");
element.SetValue(SelectedItemsSourceProperty, value);
}
#endregion
#region IsResynchingProperty
// Used to set a flag on the ListBox to avoid reentry of SelectionChanged due to
// a full syncronisation pass
private static readonly DependencyPropertyKey IsResynchingPropertyKey =
DependencyProperty.RegisterAttachedReadOnly(
"IsResynching",
typeof(bool),
typeof(DatagridService),
new PropertyMetadata(false));
#endregion
#region Private Static Methods
private static void OnSelectedItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid grd = d as DataGrid;
if (grd == null)
throw new InvalidOperationException("The DataGridExtension.SelectedItemsSource attached " +
"property can only be applied to DataGrid controls.");
grd.SelectionChanged -= new SelectionChangedEventHandler(OnDataGridSelectionChanged);
if (e.NewValue != null)
{
ListenForChanges(grd);
}
BindingExpression bexp = grd.GetBindingExpression(SelectedItemsSourceProperty);
if (bexp != null)
bexp.UpdateSource();
}
private static void ListenForChanges(DataGrid grd)
{
// Wait until the element is initialised
if (!grd.IsInitialized)
{
EventHandler callback = null;
callback = delegate
{
grd.Initialized -= callback;
ListenForChanges(grd);
};
grd.Initialized += callback;
return;
}
grd.SelectionChanged += new SelectionChangedEventHandler(OnDataGridSelectionChanged);
ResynchList(grd);
}
private static void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid grd = sender as DataGrid;
if (grd != null)
{
bool isResynching = (bool)grd.GetValue(IsResynchingPropertyKey.DependencyProperty);
if (isResynching)
return;
IList list = GetSelectedItemsSource(grd);
if (list != null)
{
foreach (object obj in e.RemovedItems)
{
if (list.Contains(obj))
list.Remove(obj);
}
foreach (object obj in e.AddedItems)
{
if (!list.Contains(obj))
list.Add(obj);
}
}
}
}
private static void ResynchList(DataGrid grd)
{
if (grd != null)
{
grd.SetValue(IsResynchingPropertyKey, true);
IList list = GetSelectedItemsSource(grd);
if (grd.SelectionMode == DataGridSelectionMode.Single)
{
grd.SelectedItem = null;
if (list != null)
{
if (list.Count > 1)
{
// There is more than one item selected, but the listbox is in Single selection mode
throw new InvalidOperationException("DataGrid is in Single selection mode, but was given more than one selected value.");
}
if (list.Count == 1)
grd.SelectedItem = list[0];
}
}
else
{
grd.SelectedItems.Clear();
if (list != null)
{
foreach (object obj in grd.Items)
if (list.Contains(obj))
grd.SelectedItems.Add(obj);
}
}
grd.SetValue(IsResynchingPropertyKey, false);
}
}
#endregion
#endregion
}
Upvotes: 0
Reputation: 8503
Yes -- the MVVM-light framework has support to databind Commands to Routed Events. See http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx
<Rectangle Fill="White"
Stroke="Black"
Width="200"
Height="100">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<cmd:EventToCommand Command="{Binding TestCommand,
Mode=OneWay}"
CommandParameter="{Binding Text,
ElementName=MyTextBox,
Mode=OneWay}"
MustToggleIsEnabledValue="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
Upvotes: 1