When binding a ListBox
to a CollectionViewSource
(for view filtering), when re-binding to the Source
property, the ListBox
automatically selects the first item (causing the SelectedItem
binding to also fire).
I do not want the first item in my list to automatically select, nor do I want the setter for SelectedItem
to fire at all (as I have other logic that gets called when an item is selected).
Here is a self-contained example, drop this inside a blank, new WPF application to demonstrate:
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
namespace TestBinding
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var template = new DataTemplate();
FrameworkElementFactory tbFactory = new FrameworkElementFactory(typeof(TextBlock));
tbFactory.SetBinding(TextBlock.TextProperty, new Binding("Name"));
template.VisualTree = tbFactory;
var itemsA = new List<Item>
{
new Item { Name = "A Item 1" },
new Item { Name = "A Item 2" },
new Item { Name = "A Item 3" },
new Item { Name = "A Item 4" },
new Item { Name = "A Item 5" },
new Item { Name = "A Item 6" },
};
var itemsB = new List<Item>
{
new Item { Name = "B Item 1" },
new Item { Name = "B Item 2" },
new Item { Name = "B Item 3" },
new Item { Name = "B Item 4" },
};
var itemsC = new List<Item>
{
new Item { Name = "C Item 1" },
new Item { Name = "C Item 2" },
new Item { Name = "C Item 3" },
new Item { Name = "C Item 4" },
new Item { Name = "C Item 5" },
new Item { Name = "C Item 6" },
new Item { Name = "C Item 7" },
new Item { Name = "C Item 8" },
};
var sp = new StackPanel();
var b1 = new Button
{
Content = "Bind A"
};
b1.Click += (_, __) =>
{
Items = itemsA;
};
var b2 = new Button
{
Content = "Bind B"
};
b2.Click += (_, __) =>
{
Items = itemsB;
};
var b3 = new Button
{
Content = "Bind C"
};
b3.Click += (_, __) =>
{
Items = itemsC;
};
var list = new ListBox
{
ItemTemplate = template
};
list.SetBinding(ItemsControl.ItemsSourceProperty, new Binding("ItemFilter.View")
{
Source = this
});
list.SetBinding(Selector.SelectedItemProperty, new Binding("SelectedItem")
{
Source = this
});
sp.Children.Add(b1);
sp.Children.Add(b2);
sp.Children.Add(b3);
sp.Children.Add(list);
(Content as Grid).Children.Add(sp);
ItemFilter = new CollectionViewSource();
DataContext = this;
}
private List<Item> _Items;
public List<Item> Items
{
get { return _Items; }
set
{
_Items = value;
ItemFilter.Source = _Items;
}
}
#region Dependency Properties
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(Item), typeof(MainWindow), new PropertyMetadata(default(Item)));
public Item SelectedItem
{
get { return (Item)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty ItemFilterProperty = DependencyProperty.Register("ItemFilter", typeof(CollectionViewSource), typeof(MainWindow), new PropertyMetadata(default(CollectionViewSource)));
public CollectionViewSource ItemFilter
{
get { return (CollectionViewSource)GetValue(ItemFilterProperty); }
set { SetValue(ItemFilterProperty, value); }
}
#endregion
}
public class Item : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set { NotifyPropertyChanged(ref _Name, value, "Name"); }
}
#region INPC
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged<T>(ref T item, T value, string name)
{
item = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
}