We’re trying to create a ListBox which has a few items inside that the user cannot select. Think the group headings in a grouped listbox, except these are independent items (i.e. no indented children.) We just want to display a message in the list.
Here’s a mock-up of what we currently display if there are ‘Foo’ items…
Foo 1
Foo 2
Foo 3
-------------- <-- This is of type 'Separator' so it's styled as not-selectable by default.
Add new Foo...
Manage Foos...
…and if there aren’t any Foo items, we display this…
[No Foo Items] <-- This one should not be selectable, the same as a separator
-------------- <-- This is of type 'Separator' so it's styled as not-selectable by default.
Add new Foo...
Manage Foos...
Now again, we already have the code that properly handles what’s displayed (see below). We’re wondering what’s the proper way to disable that entry from being able to be selected.
What we don’t know is how to style that ListBoxItem so the selection skips right over it, nor can the user click on it.
Code
Someone asked me to see my code, so here it is. Not really relevant to the question, but should show people how I did this.
Note: We template the string.Empty in the XAML to show the localized ‘No xxx Items’ message. string.Empty is just a placeholder that we can target.
Note 2: FauxData is a simple library of items and collections used for testing so I don’t have to constantly create them from scratch. SimpleItemCollection for instance creates ten SimpleItem objects in the constructor, pre-populated with things like Name, Description, IsSelected, etc. all fully supporting INotifyPropertyChanged and collection change notifications. I have the same thing with HierarchicalItemsCollection which adds Parent, Children and IsExpanded, etc. It saves a LOT of work when testing code and controls!
Finally, this is from a simple test, or ‘playground’ app. As such, it’s not supposed to represent the best way to do things, just to show a concept. Yes, it can be cleaned up a lot, but you get the general idea.
Anyway, on to the code…
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using FauxData;
namespace Playground.ListTest
{
[LaunchEntry("List Test")]
public partial class Main : Window
{
SimpleItemCollection itemsCollection = new SimpleItemCollection(); // Default constructor creates 10 items
public Main()
{
InitializeComponent();
MainListBox.ItemsSource = CreateCompositeCollection();
}
private CompositeCollection CreateCompositeCollection()
{
var EmptyHolder = new ObservableCollection<object>();
itemsCollection.CollectionChanged += (s,e) => {
if(itemsCollection.Count != 0)
EmptyHolder.Clear();
else if(EmptyHolder.Count == 0)
EmptyHolder.Add(string.Empty);
};
var cc = new CompositeCollection();
cc.Add(new CollectionContainer(){ Collection = itemsCollection });
cc.Add(new CollectionContainer(){ Collection = EmptyHolder });
cc.Add(new Separator());
cc.Add(ApplicationCommands.New); // <-- Pops a dialog to enter a new item
cc.Add(ApplicationCommands.Open); // <-- Shows an item management window
return cc;
}
private void Test_Click(object sender, RoutedEventArgs e)
{
if(itemsCollection.Count != 0)
itemsCollection.Clear();
else
itemsCollection.Add(new SimpleItem(){Name = "New item" });
}
}
}
As
Focusable="false"does not cover all options (e.g. click and drag select) i would recommend settingIsEnabledtofalseinstead.(The greyed out text is a matter of the
ListBoxItemstyle/template, so if you do not like it you will need to override it)