Results 1 to 4 of 4

Thread: [WPF] Checkboxes in ComboBox - display number of checked items when closed

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    [WPF] Checkboxes in ComboBox - display number of checked items when closed

    Hi,

    I need to have a ComboBox that displays a list of CheckBoxes when dropped down so the user can check the relevant ones and then close it again. When closed, I want the ComboBox (the 'Text' area) to display the number of checked items (for example "Checked items: 3").

    I cannot seem to figure this out... I can get the CheckBoxes in the ComboBox, that's easy enough using a DataTemplate, but then it also uses the same template for the 'selected item' and thus shows the last clicked ("selected") CheckBox in the text area. Selecting an item is actually not really defined for this ComboBox, I just need the user to check some items and be done with it, he doesn't need to select any items.

    I've tried looking for solutions, and there seem to be a few different solutions to having a different template for the selected item as for the dropdown items, but none seem to work for me...

    The code-behind for all of my attempts is:
    vb.net Code:
    1. Imports System.Collections.ObjectModel
    2.  
    3. Class MainWindow
    4.  
    5.     Private items As ObservableCollection(Of ComboBoxItem)
    6.  
    7.     Public Sub New()
    8.  
    9.         ' This call is required by the designer.
    10.         InitializeComponent()
    11.  
    12.         ' Add any initialization after the InitializeComponent() call.
    13.  
    14.         items = New ObservableCollection(Of ComboBoxItem)
    15.         For i As Integer = 1 To 5
    16.             items.Add(New ComboBoxItem() With {.Text = "Item " & i})
    17.         Next
    18.  
    19.         comboBox.ItemsSource = items
    20.  
    21.     End Sub
    22.  
    23.     Private ReadOnly Property CheckedItems As Integer
    24.         Get
    25.             Return items.Where(Function(cbi) cbi.IsChecked).Count()
    26.         End Get
    27.     End Property
    28.  
    29. End Class
    30.  
    31. Public Class ComboBoxItem
    32.  
    33.     Private _Text As String
    34.     Public Property Text() As String
    35.         Get
    36.             Return _Text
    37.         End Get
    38.         Set(ByVal value As String)
    39.             _Text = value
    40.         End Set
    41.     End Property
    42.  
    43.     Private _IsChecked As Boolean
    44.     Public Property IsChecked() As Boolean
    45.         Get
    46.             Return _IsChecked
    47.         End Get
    48.         Set(ByVal value As Boolean)
    49.             _IsChecked = value
    50.         End Set
    51.     End Property
    52.  
    53. End Class

    Attempt 1:
    Source
    My XAML code:
    Code:
        <Window.Resources>   
            <ControlTemplate x:Key="X" TargetType="{x:Type ContentControl}">
                <CheckBox Content="{Binding Text}" IsChecked="{Binding IsChecked}" />
            </ControlTemplate>
        </Window.Resources>
        
        <Grid>
            <ComboBox Name="comboBox" VerticalAlignment="Top" Margin="10">                  
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=CheckedItems}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
                <ComboBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ComboBoxItem}">
                        <Setter Property="Margin" Value="5"/>
                        <Setter Property="Template" Value="{StaticResource X}" />
                    </Style>
                </ComboBox.ItemContainerStyle>
            </ComboBox>
        </Grid>
    I am trying to bind the TextBlock Text property to the 'CheckedItems' property in my Window, I hope I'm not screwing that up...

    Anyway, the result is a dropdown containing CheckBox items which I can check (correct), but after closing, the 'text area' remains empty. Even if I select an item (which isn't really defined for me) it displays nothing.



    (Text too long, see post 2...)

  2. #2

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: [WPF] Checkboxes in ComboBox - display number of checked items when closed

    Attempt 2:
    Source
    My XAML:
    Code:
        <Window.Resources>   
            <DataTemplate x:Key="ComplexTemplate">
                <CheckBox Content="{Binding Text}" IsChecked="{Binding IsChecked}" />
            </DataTemplate>
            <ControlTemplate x:Key="SimpleTemplate">
                <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=CheckedItems}" />
            </ControlTemplate>
        </Window.Resources>
        
        <Grid>
            <ComboBox Name="comboBox" VerticalAlignment="Top" Margin="10">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <ContentControl x:Name="content" Content="{Binding}" ContentTemplate="{StaticResource ComplexTemplate}"/>
                        </StackPanel>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBoxItem}}" Value="{x:Null}">
                                <Setter TargetName="content" Property="ContentTemplate" Value="{StaticResource SimpleTemplate}"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
        </Grid>
    This produces an error as soon as I start, saying it cannot convert a ControlTemplate to a DataTemplate...


    Attempt 3:
    Finally I simply tried to bind the Text property of the ComboBox to the property in my Window (same as before), but nothing... The text area stays empty.



    I'm kinda out of ideas... What else can I do? This can't be so hard?

  3. #3
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: [WPF] Checkboxes in ComboBox - display number of checked items when closed

    Sounds to me like you don't actually want a ComboBox, but rather an Expander. What you're describing is really strange behaviour for a ComboBox. The Expander control will give you that "hiding" behaviour that you're after, without the strangeness of the ComboBox implementation.

  4. #4
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: [WPF] Checkboxes in ComboBox - display number of checked items when closed

    To fix your issue with the Count of items.

    First off, I took your code and removed the code behind into its own class, MainWindowViewModel.

    In the constructor of the Window, I instantiated one of them and set it as the DataContext of the Window:

    vbnet Code:
    1. Class MainWindow
    2.  
    3.     Public Sub New()
    4.  
    5.         ' This call is required by the designer.
    6.         InitializeComponent()
    7.  
    8.         Me.DataContext = New MainWindowViewModel()
    9.  
    10.     End Sub
    11.  
    12. End Class

    Normally, you'd set the DataContext from outside the Window, but this'll do for now.

    Now, our XAML looks a bit like this:

    Code:
    <Window x:Class="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>
            <Expander>
                <ListView ItemsSource="{Binding Items}">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <CheckBox Content="{Binding Text}" IsChecked="{Binding IsChecked}" />
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </Expander>
            <TextBlock Text="{Binding CheckedCount}" />
        </StackPanel>
    </Window>
    (I've changed some of the property names slightly)

    Now, the problem with the Binding for CheckedCount is that it needs to know when the CheckedCount property changes. Which it can't at the moment. So, get that ViewModel class implementing INotifyPropertyChanged so it can raise the PropertyChanged event when the items are checked on/off. But we don't yet know that so let's add an event to the ComboBoxItem class that lets us know when it gets checked:

    vbnet Code:
    1. Public Class ComboBoxItem
    2.     ' ...
    3.     Private _IsChecked As Boolean
    4.     Public Property IsChecked() As Boolean
    5.         Get
    6.             Return _IsChecked
    7.         End Get
    8.         Set(ByVal value As Boolean)
    9.             _IsChecked = value
    10.             RaiseEvent IsCheckedChanged(Me, EventArgs.Empty)
    11.         End Set
    12.     End Property
    13.  
    14.     Public Event IsCheckedChanged(sender As Object, eventArgs As EventArgs)
    15.  
    16. End Class

    That lets us hook on to the event, and raise our own event in the ViewModel:

    vbnet Code:
    1. Imports System.Collections.ObjectModel
    2. Imports System.ComponentModel
    3.  
    4. Public Class MainWindowViewModel
    5.     Implements INotifyPropertyChanged
    6.  
    7.     Private m_items As ObservableCollection(Of ComboBoxItem)
    8.  
    9.     Public Sub New()
    10.  
    11.         m_items = New ObservableCollection(Of ComboBoxItem)
    12.         For i As Integer = 1 To 5
    13.             Dim comboBoxItem As New ComboBoxItem() With {.Text = "Item " & i}
    14.             AddHandler comboBoxItem.IsCheckedChanged, AddressOf Me.ComboBoxItemIsCheckedChanged
    15.  
    16.             m_items.Add(comboBoxItem)
    17.         Next
    18.  
    19.     End Sub
    20.  
    21.     Public ReadOnly Property Items As ObservableCollection(Of ComboBoxItem)
    22.         Get
    23.             Return m_items
    24.         End Get
    25.     End Property
    26.  
    27.     Public ReadOnly Property CheckedCount As Integer
    28.         Get
    29.             Return m_items.Where(Function(cbi) cbi.IsChecked).Count()
    30.         End Get
    31.     End Property
    32.  
    33.     Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    34.  
    35.     Private Sub ComboBoxItemIsCheckedChanged(ByVal sender As Object, ByVal eventArgs As EventArgs)
    36.         RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("CheckedCount"))
    37.  
    38.     End Sub
    39.  
    40. End Class

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width