1

I'm working on a few ComboBoxes that need a "select" property as the top option in WPF (c#)

At the moment I have the combobox's named and then populated in the code behind from a array string.

<ComboBox Width="150" x:Name="cmbTitle" Margin="3" SelectedIndex="0" />

.

cmbTitle.Items.Add("Select");
foreach (var title in Constants.Title)
            cmbTitle.Items.Add(title);

My Issue is that the selectd Index will always be off by 1 of the index in the string.

After doing my research I see that this is a very prehistoric way of populating a combo box (WinFrom background). Constants seem to be stored in Enums in every example I have looked at so would like to move away from multiple string[]s.

What is my best way of binding an enum to a combobox while accommodating for a "select" option in WPF? I've looked at half a dozen options today and I'm not too sure what other code examples to list.

It's quite a open question, but I'm quite lost.

Thanks in advance, Oli

animuson
  • 53,861
  • 28
  • 137
  • 147
Oli
  • 428
  • 1
  • 5
  • 20
  • 1
    Should the "Select" option be available for picking, or it's just a prompt for users? If latter, there's [a better way of doing that](http://stackoverflow.com/questions/2901536/can-a-wpf-combobox-display-alternative-text-when-its-selection-is-null). – skink Jan 19 '12 at 15:37
  • @Joulukuusi It should just be a prompt for the user. Thanks, this looks very useful. – Oli Jan 19 '12 at 15:57

2 Answers2

1

I think the best way to populate your ComboBox will be using IDictionary.

As an example, your code-behind:

public YourEnum SelectedOption { get; set; }

public IDictionary<string, YourEnum> Options = new Dictionary<string, YourEnum?>();

Options.Add("Select", null);
Options.Add("Option 1", YourEnum.Option1);
...
Options.Add("Option N", YourEnum.OptionN);

Your xaml file:

<ComboBox ItemsSource="{Binding Options, ...}" SelectedValue="{Binding SelectedOption, ...}" DisplayMemberPath="Key" SelectedValuePath="Value" />
Maksim Gladkov
  • 3,051
  • 1
  • 14
  • 16
1
  1. Values of an enumeration can be retrieved from Enum.GetValues(), and binding to a method is typically done using ObjectDataProvider. Here's an example of getting all BindingMode values:

    <ObjectDataProvider x:Key="BindingModes" ObjectType="{x:Type sys:Enum}" MethodName="GetValues">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="BindingMode" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    

    Now, we can bind ItemsSource of our ComboBox:

    <ComboBox ItemsSource="{Binding Source={StaticResource BindingModes}}" />
    
  2. Our control needs a new property for the prompt:

    public class ExtendedComboBox : ComboBox
    {
        public static readonly DependencyProperty PromptProperty =
            DependencyProperty.Register("Prompt", typeof(string), typeof(ExtendedComboBox), new PropertyMetadata(string.Empty));
    
        public string Prompt
        {
            get { return (string)GetValue(PromptTextProperty); }
            set { SetValue(PromptTextProperty, value); }
        }
    }
    

    We can cheat a bit and place a TextBlock with the prompt inside our control, and hide it when there's an item selected. For this we rewrite ControlTemplate of the control with a new one containing the TextBlock. I modified template from there:

    <Style x:Key="PromptTextBlock" TargetType="{x:Type TextBlock}" >
        <Setter Property="Visibility" Value="Hidden" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}}" Value="{x:Null}">
                <Setter Property="Visibility" Value="Visible" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
    
    <Style x:Key="PromptedComboBox" TargetType="{x:Type local:ExtendedComboBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ExtendedComboBox}">
                    <Grid>
                        <ToggleButton x:Name="DropDownToggle"
                                      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"  
                                      Margin="-1" HorizontalContentAlignment="Right"
                                      IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
                            <Path x:Name="BtnArrow" Height="4" Width="8" 
                                  Stretch="Uniform" Margin="0,0,4,0"  Fill="Black"
                                  Data="F1 M 300,-190L 310,-190L 305,-183L 301,-190 Z " />
                        </ToggleButton>
                        <ContentPresenter x:Name="ContentPresenter" Margin="6,2,25,2"
                                          Content="{TemplateBinding SelectionBoxItem}"
                                          ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                                          ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}">
                        </ContentPresenter>
                        <TextBox x:Name="PART_EditableTextBox"
                                 Style="{x:Null}"
                                 Focusable="False"
                                 Background="{TemplateBinding Background}"
                                 HorizontalAlignment="Left" 
                                 VerticalAlignment="Center" 
                                 Margin="3,3,23,3"
                                 Visibility="Hidden"
                                 IsReadOnly="{TemplateBinding IsReadOnly}"/>
                            <Popup x:Name="PART_Popup" IsOpen="{TemplateBinding IsDropDownOpen}">
                                <Border x:Name="PopupBorder" 
                                        HorizontalAlignment="Stretch" Height="Auto" 
                                        MinWidth="{TemplateBinding ActualWidth}"
                                        MaxHeight="{TemplateBinding MaxDropDownHeight}"
                                        BorderThickness="{TemplateBinding BorderThickness}" 
                                        BorderBrush="Black" Background="White" CornerRadius="3">
                                    <ScrollViewer x:Name="ScrollViewer" BorderThickness="0" Padding="1">
                                        <ItemsPresenter/>
                                    </ScrollViewer>
                                </Border>
                            </Popup>
                        <TextBlock Margin="4,3,20,3" Text="{Binding PromptText, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource PromptTextBlock}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
         </Setter>
     </Style>
    
  3. Combining, we have:

    <local:ExtendedComboBox Style="{StaticResource PromptedComboBox}" Prompt="Select an item" ItemsSource="{Binding Source={StaticResource BindingModes}}" />
    
Community
  • 1
  • 1
skink
  • 5,133
  • 6
  • 37
  • 58