Showing non-selectable items in a WPF ComboBox

This is a demo about adding non-selectable items (like titles or category names) and hyperlinks to a WPF ComboBox.

One way of dealing with large comboboxes is filtering. Another technique is proposing only the most popular items in the default list, and provide a hyperlink to the full list:



If the hyperlink is clicked, the ComboBox is entirely populated, but by adding non-selectable items you can still make the distinction:



Here's the full code for the demo:

XAML

<Window x:Class="DockOfTheBay.SuggestionComboBoxSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DockOfTheBay"
        Title="Suggestion ComboBox" 
        Height="120" Width="300">
    <StackPanel Orientation="Horizontal" 
                VerticalAlignment="Top"
                Margin="10 10">
        <TextBlock Text="Select: " 
                   Padding="4 3"/>
        <ComboBox 
            x:Name="SuggestionComboBox1" 
            Padding="4 3" 
            MinWidth="200"/>
    </StackPanel>
</Window>


C#

namespace DockOfTheBay
{
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
 
    /// <summary>
    /// Demonstration of using non-selectable items and hyperlinks in a
    /// WPF ComboBox.
    /// </summary>
    public partial class SuggestionComboBoxSample : Window
    {
        /// <summary>
        /// The hyperlink displayed after the the ComboBox's Suggestions.
        /// </summary>
        private ComboBoxItem linkItem;
 
        /// <summary>
        /// Initializes a new instance of the SuggestionComboBoxSample class.
        /// </summary>
        public SuggestionComboBoxSample()
        {
            InitializeComponent();
 
            // Add an item that looks like a title
            ComboBoxItem item = new ComboBoxItem();
            item.Content = "Most Frequent";
            item.IsEnabled = false;
            item.Background = Brushes.Blue;
            item.Foreground = Brushes.White;
            SuggestionComboBox1.Items.Add(item);
 
            // Add suggestions
            foreach (var name in this.Suggestions())
            {
                SuggestionComboBox1.Items.Add(name);
            }
 
            // Don't add content directly, like the following:
            // SuggestionComboBox1.Items.Add(new Separator());
            // It will not display properly when databinding is used.
            item = new ComboBoxItem();
            item.Content = new Separator();
            SuggestionComboBox1.Items.Add(item);
            Hyperlink link = new Hyperlink();
 
            // 'RequestNavigate' is not fired, so use 'Click' instead
            link.Click += this.Link_Click;
 
            link.Inlines.Add(new Run("Show Everything"));
            item = new ComboBoxItem();
            item.Content = link;
            this.linkItem = item;
            SuggestionComboBox1.Items.Add(item);
        }
 
        /// <summary>
        /// Fetches the remaining items, and adds them to the ComboBox.
        /// </summary>
        /// <param name="sender">The HyperLink.</param>
        /// <param name="e">The Event Arg.</param>
        private void Link_Click(object sender, RoutedEventArgs e)
        {
            // Remove Hyperlink
            SuggestionComboBox1.Items.Remove(this.linkItem);
 
            // Add an item that looks like a title
            ComboBoxItem item = new ComboBoxItem();
            item.Content = "The Rest";
            item.IsEnabled = false;
            item.Background = Brushes.Blue;
            item.Foreground = Brushes.White;
            SuggestionComboBox1.Items.Add(item);
 
            // Add suggestions
            foreach (var name in this.Rest())
            {
                SuggestionComboBox1.Items.Add(name);
            }
        }
 
        /// <summary>
        /// The Suggested Items.
        /// </summary>
        /// <returns>A list of Suggestions.</returns>
        private List<string> Suggestions()
        {
            List<string> names = new List<string>();
            names.Add("WPF rocks");
            names.Add("WCF rocks");
            names.Add("XAML is fun");
 
            return names;
        }
 
        /// <summary>
        /// The non-Suggested Items.
        /// </summary>
        /// <returns>A list of non-Suggestions.</returns>
        private List<string> Rest()
        {
            List<string> names = new List<string>();
            names.Add("WPF rules");
            names.Add("WCF rules");
            names.Add("WinForms not");
 
            return names;
        }
    }
}


Here's how the control looks like in a real-life application for cancer registration. It presents a list of the most probable histologic diagnoses, based on key words in the tumor location:

1 comment:

  1. This is not the correct method of doing this. The CollectionViewSource has built in grouping functionality that should be used instead.

    ReplyDelete