Search code examples
c#wpfcomboboxitemsource

Add prefix to ItemSource values in ComboBox


This is in my app.xaml file (cut down):

<Application.Resources>
    <x:Array x:Key="SongTitleString" Type="local:ComboBoxItemString">
        <local:ComboBoxItemString ValueString = "Song 1"/>
        <local:ComboBoxItemString ValueString = "Song 2"/>
        <local:ComboBoxItemString ValueString = "Song 3"/>
    </x:Array>
</Application.Resources>

This is the class:

namespace OCLMEditor
{
    /// This class provides us with an object to fill a ComboBox with
    /// that can be bound to string fields in the binding object.
    public class ComboBoxItemString
    {
        public string ValueString { get; set; }
    }
}

This is the markup:

<ComboBox x:Name="comboSongOpen" ItemsSource="{StaticResource SongTitleString}" 
    DisplayMemberPath="ValueString" 
    SelectedValuePath="ValueString" 
    SelectedValue="{Binding SongTitle}">
    <ComboBox.MaxWidth>
        <Binding Path="ActualWidth" 
            ElementName="textWeeklyBibleReading"/>
    </ComboBox.MaxWidth>
</ComboBox>

All good. This issue is that, when the song titles are displayed on the combo, I want to prefix the song titles with a 3 digit number. Eg:

001 - Song 1
002 - Song 2
003 - Song 3

Eventually, elsewhere in the application, I will want to use the combo box selected index to pull out the right song title (with no prefix) from the resources.

Is this possible?

Update:

If I create a XML:

<?xml version="1.0" encoding="utf-8" ?>
<Settings>
  <SongTitles>
    <SongTitle Number="1" Title = "Jehovah's Attributes"/>
  </SongTitles>
</Settings>

And add it as a resource to the WPF:

<Window.Resources>
    <XmlDataProvider x:Key="XmlData" Source="OCLM_Resources.xml" XPath="Settings/SongTitles" />
</Window.Resources>

Then use:

<ComboBox x:Name="comboSongOpen"
           ItemsSource="{Binding Source={StaticResource XmlData}, XPath=./SongTitle}" DisplayMemberPath="@Title">
    <ComboBox.MaxWidth>
        <Binding Path="ActualWidth" 
            ElementName="textWeeklyBibleReading"/>
    </ComboBox.MaxWidth>
</ComboBox>

That display the Title in the drop. But I can't quite get my head around using ItemTemplate and/or stiching to attributes (for formatting 0.000) together.


Solution

  • Here's a very simple pure xaml solution. You don't need to change anything else you have.

                //add this to your resource.  Obviously you should put this in code if you have lots of items
                <AlternationConverter x:Key="AlternateForegroundConverter">
                    <s:Int16>1</s:Int16>
                    <s:Int16>2</s:Int16>
                    <s:Int16>3</s:Int16>
                    <s:Int16>4</s:Int16>
                    <s:Int16>5</s:Int16>
                    //more if necessary
                </AlternationConverter>
    
            <ComboBox x:Name="comboSongOpen" ItemsSource="{StaticResource SongTitleString}" SelectedValuePath="ValueString" SelectedValue="{Binding SongTitle}" AlternationCount="99999" >
                <ComboBox.ItemTemplate>
                    <DataTemplate DataType="local:ComboBoxItemString">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding (ItemsControl.AlternationIndex), RelativeSource={RelativeSource AncestorType=ListBoxItem}, StringFormat='000 - ', Converter={StaticResource AlternateForegroundConverter}}" />
                            <TextBlock Text="{Binding ValueString}" />
                        </StackPanel>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
                <ComboBox.MaxWidth>
                    <Binding Path="ActualWidth" ElementName="textWeeklyBibleReading"/>
                </ComboBox.MaxWidth>
    

    edit: this namespace is needed

    xmlns:s="clr-namespace:System;assembly=mscorlib"
    

    Edit: In response to updated question

    Change the DataTemplate to (you won't need the AlternationConverter in this case).

            <ComboBox.ItemTemplate>
                <DataTemplate DataType="local:ComboBoxItemString">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Number, StringFormat='000 - '}" />
                        <TextBlock Text="{Binding ValueString}" />
                    </StackPanel>
                </DataTemplate>
    
    //add Number property to you class
    public class ComboBoxItemString
    {
        public string ValueString { get; set; }
        public int Number { get; set; }
    }
    
    //add the array
    <x:Array x:Key="SongTitleString" Type="local:ComboBoxItemString">
        <local:ComboBoxItemString ValueString = "Song 1" Number = "1" />
        <local:ComboBoxItemString ValueString = "Song 2" Number = "2" />
        <local:ComboBoxItemString ValueString = "Song 3" Number = "3" />
    </x:Array>
    

    There is a big HOWEVER. You list is correct. But if you select an item, the ComboBox will display "001 - Jehovah's Attributes" as well. To get it to display "Jehovah's Attributes" only, you need to modify the control template.