为 ComboBox 中的 ItemSource 值添加前缀
Add prefix to ItemSource values in ComboBox
这是我的 app.xaml 文件(删减):
<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>
这是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; }
}
}
这是标记:
<ComboBox x:Name="comboSongOpen" ItemsSource="{StaticResource SongTitleString}"
DisplayMemberPath="ValueString"
SelectedValuePath="ValueString"
SelectedValue="{Binding SongTitle}">
<ComboBox.MaxWidth>
<Binding Path="ActualWidth"
ElementName="textWeeklyBibleReading"/>
</ComboBox.MaxWidth>
</ComboBox>
一切顺利。这个问题是,当歌曲标题显示在组合上时,我想在歌曲标题前加上一个 3 位数字。例如:
001 - Song 1
002 - Song 2
003 - Song 3
最后,在应用程序的其他地方,我会想使用组合框选择的索引从资源中提取正确的歌曲标题(没有前缀)。
这可能吗?
更新:
如果我创建 XML:
<?xml version="1.0" encoding="utf-8" ?>
<Settings>
<SongTitles>
<SongTitle Number="1" Title = "Jehovah's Attributes"/>
</SongTitles>
</Settings>
并将其作为资源添加到 WPF:
<Window.Resources>
<XmlDataProvider x:Key="XmlData" Source="OCLM_Resources.xml" XPath="Settings/SongTitles" />
</Window.Resources>
然后使用:
<ComboBox x:Name="comboSongOpen"
ItemsSource="{Binding Source={StaticResource XmlData}, XPath=./SongTitle}" DisplayMemberPath="@Title">
<ComboBox.MaxWidth>
<Binding Path="ActualWidth"
ElementName="textWeeklyBibleReading"/>
</ComboBox.MaxWidth>
</ComboBox>
在drop中显示Title。但是我无法完全理解使用 ItemTemplate
and/or stiching 属性(用于格式化 0.000)在一起。
根据我对 OP 的评论,我想这更像是一个建议:
如果将歌曲列表卸载到文件中,则可以在初始化应用程序以创建歌曲列表时读取该文件。文件的实际结构由您决定。通常使用 CSV 文件,因为这是一种分隔值的简单方法(用逗号),但您可以使用任何您喜欢的分隔符。
在这种情况下,鉴于您可以使用 C# 读取文件的一些简单方法,我什至建议您用新行分隔您的歌曲名称。如果您这样做,编制您的歌曲列表将非常简单:
string[] songList = System.IO.File.ReadAllLines(@"songlist.txt");
for (int i = 0; i < songList.Length; i++) {
string newItem = ((i+1).ToString()) + " " + songList[i];
combo1.Items.Add(newItem);
}
或
List<string> songs = new List<string>();
for (int i = 0; i < songList.Length; i++) {
songs.Add((i + 1).ToString() + " " + songList[i]);
}
combo1.ItemsSource = songs;
因此在这些示例中,组合框的内容将是
1 Song 1
2 Song 2
编辑:根据您 post
的最后一部分
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.
在您的示例中,格式为 001 - Song 1
如果您只想要没有前缀的歌曲标题,selectedIndex
,您有几个选择。
1) 您可以在应用程序初始化时创建第二个列表,该列表不包含前缀。在编制列表时,制作第二个列表,并添加不带前缀的歌曲名称。然后,您可以使用 ComboBox
.
的 selectedIndex
属性 引用此列表
2) 您可以使用 selectedIndex
属性 然后使用字符串操作的一些变体来提取歌曲名称。
string[] selectedSong = combo1.Items[0].ToString().Split('-');
在此示例中,没有前缀的歌曲名称将在 selectedSong[1]
内。
按照 EdPlunkett 的建议,使用 ComboBox 的 ItemTemplate 参考以下示例。
<Window x:Class="ChkList_Learning.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ChkList_Learning"
mc:Ignorable="d"
Title="Window2" Height="300" Width="300">
<Grid>
<StackPanel>
<ComboBox ItemsSource="{Binding SongList}" x:Name="songsCombo">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding SongName}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID"></TextBlock>
<TextBox Width="100" Text="{Binding ElementName=songsCombo,Path=SelectedItem.Title}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Song Name"></TextBlock>
<TextBox Width="100" Text="{Binding ElementName=songsCombo,Path=SelectedItem.SongName}"></TextBox>
</StackPanel>
</StackPanel>
</Grid>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ChkList_Learning
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
this.DataContext = new SongViewModel();
}
}
class SongViewModel
{
public ObservableCollection<Song> SongList { get; set; }
public SongViewModel()
{
SongList = new ObservableCollection<Song>();
for (int i = 0; i < 10; i++)
{
Song song = new Song();
song.Title = (i + 1).ToString();
song.SongName = "Song Name" + (i + 1).ToString();
song.AlbumName = "My Album";
SongList.Add(song);
}
}
}
class Song
{
public string Title { get; set; }
public string SongName { get; set; }
public string AlbumName { get; set; }
}
}
这是一个非常简单的纯 xaml 解决方案。您不需要更改任何其他内容。
//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>
编辑:需要这个命名空间
xmlns:s="clr-namespace:System;assembly=mscorlib"
编辑:回应更新的问题
将 DataTemplate 更改为(在这种情况下您不需要 AlternationConverter)。
<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>
但是有一个很大的问题。你列出的是正确的。但是如果你 select 一个项目,ComboBox 也会显示“001 - Jehovah's Attributes”。要让它只显示"Jehovah's Attributes",你需要修改控件模板。
只需添加一个带有前缀
的 DisplayMemberPath
<Application.Resources>
<x:Array x:Key="SongTitleString" Type="local:ComboBoxItemString">
<local:ComboBoxItemString ValueString = "Song 1" ValueStringLong = "# 1 Song 1"/>
<local:ComboBoxItemString ValueString = "Song 2" ValueStringLong = "# 2 Song 1"/>
<local:ComboBoxItemString ValueString = "Song 3" ValueStringLong = "# 3 Song 1"/>
</x:Array>
</Application.Resources>
DisplayMemberPath="ValueStringLong"
这是我的 app.xaml 文件(删减):
<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>
这是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; }
}
}
这是标记:
<ComboBox x:Name="comboSongOpen" ItemsSource="{StaticResource SongTitleString}"
DisplayMemberPath="ValueString"
SelectedValuePath="ValueString"
SelectedValue="{Binding SongTitle}">
<ComboBox.MaxWidth>
<Binding Path="ActualWidth"
ElementName="textWeeklyBibleReading"/>
</ComboBox.MaxWidth>
</ComboBox>
一切顺利。这个问题是,当歌曲标题显示在组合上时,我想在歌曲标题前加上一个 3 位数字。例如:
001 - Song 1 002 - Song 2 003 - Song 3
最后,在应用程序的其他地方,我会想使用组合框选择的索引从资源中提取正确的歌曲标题(没有前缀)。
这可能吗?
更新:
如果我创建 XML:
<?xml version="1.0" encoding="utf-8" ?>
<Settings>
<SongTitles>
<SongTitle Number="1" Title = "Jehovah's Attributes"/>
</SongTitles>
</Settings>
并将其作为资源添加到 WPF:
<Window.Resources>
<XmlDataProvider x:Key="XmlData" Source="OCLM_Resources.xml" XPath="Settings/SongTitles" />
</Window.Resources>
然后使用:
<ComboBox x:Name="comboSongOpen"
ItemsSource="{Binding Source={StaticResource XmlData}, XPath=./SongTitle}" DisplayMemberPath="@Title">
<ComboBox.MaxWidth>
<Binding Path="ActualWidth"
ElementName="textWeeklyBibleReading"/>
</ComboBox.MaxWidth>
</ComboBox>
在drop中显示Title。但是我无法完全理解使用 ItemTemplate
and/or stiching 属性(用于格式化 0.000)在一起。
根据我对 OP 的评论,我想这更像是一个建议:
如果将歌曲列表卸载到文件中,则可以在初始化应用程序以创建歌曲列表时读取该文件。文件的实际结构由您决定。通常使用 CSV 文件,因为这是一种分隔值的简单方法(用逗号),但您可以使用任何您喜欢的分隔符。
在这种情况下,鉴于您可以使用 C# 读取文件的一些简单方法,我什至建议您用新行分隔您的歌曲名称。如果您这样做,编制您的歌曲列表将非常简单:
string[] songList = System.IO.File.ReadAllLines(@"songlist.txt");
for (int i = 0; i < songList.Length; i++) {
string newItem = ((i+1).ToString()) + " " + songList[i];
combo1.Items.Add(newItem);
}
或
List<string> songs = new List<string>();
for (int i = 0; i < songList.Length; i++) {
songs.Add((i + 1).ToString() + " " + songList[i]);
}
combo1.ItemsSource = songs;
因此在这些示例中,组合框的内容将是
1 Song 1
2 Song 2
编辑:根据您 post
的最后一部分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.
在您的示例中,格式为 001 - Song 1
如果您只想要没有前缀的歌曲标题,selectedIndex
,您有几个选择。
1) 您可以在应用程序初始化时创建第二个列表,该列表不包含前缀。在编制列表时,制作第二个列表,并添加不带前缀的歌曲名称。然后,您可以使用 ComboBox
.
selectedIndex
属性 引用此列表
2) 您可以使用 selectedIndex
属性 然后使用字符串操作的一些变体来提取歌曲名称。
string[] selectedSong = combo1.Items[0].ToString().Split('-');
在此示例中,没有前缀的歌曲名称将在 selectedSong[1]
内。
按照 EdPlunkett 的建议,使用 ComboBox 的 ItemTemplate 参考以下示例。
<Window x:Class="ChkList_Learning.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ChkList_Learning"
mc:Ignorable="d"
Title="Window2" Height="300" Width="300">
<Grid>
<StackPanel>
<ComboBox ItemsSource="{Binding SongList}" x:Name="songsCombo">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding SongName}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID"></TextBlock>
<TextBox Width="100" Text="{Binding ElementName=songsCombo,Path=SelectedItem.Title}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Song Name"></TextBlock>
<TextBox Width="100" Text="{Binding ElementName=songsCombo,Path=SelectedItem.SongName}"></TextBox>
</StackPanel>
</StackPanel>
</Grid>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ChkList_Learning
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
this.DataContext = new SongViewModel();
}
}
class SongViewModel
{
public ObservableCollection<Song> SongList { get; set; }
public SongViewModel()
{
SongList = new ObservableCollection<Song>();
for (int i = 0; i < 10; i++)
{
Song song = new Song();
song.Title = (i + 1).ToString();
song.SongName = "Song Name" + (i + 1).ToString();
song.AlbumName = "My Album";
SongList.Add(song);
}
}
}
class Song
{
public string Title { get; set; }
public string SongName { get; set; }
public string AlbumName { get; set; }
}
}
这是一个非常简单的纯 xaml 解决方案。您不需要更改任何其他内容。
//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>
编辑:需要这个命名空间
xmlns:s="clr-namespace:System;assembly=mscorlib"
编辑:回应更新的问题
将 DataTemplate 更改为(在这种情况下您不需要 AlternationConverter)。
<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>
但是有一个很大的问题。你列出的是正确的。但是如果你 select 一个项目,ComboBox 也会显示“001 - Jehovah's Attributes”。要让它只显示"Jehovah's Attributes",你需要修改控件模板。
只需添加一个带有前缀
的 DisplayMemberPath<Application.Resources>
<x:Array x:Key="SongTitleString" Type="local:ComboBoxItemString">
<local:ComboBoxItemString ValueString = "Song 1" ValueStringLong = "# 1 Song 1"/>
<local:ComboBoxItemString ValueString = "Song 2" ValueStringLong = "# 2 Song 1"/>
<local:ComboBoxItemString ValueString = "Song 3" ValueStringLong = "# 3 Song 1"/>
</x:Array>
</Application.Resources>
DisplayMemberPath="ValueStringLong"