是否可以在 ListView 中显示带有文本 + 图标的行?
Is it possible to display lines with text + icons in a ListView?
我想在 ListView
中显示不同行的通知。
我试过 StackPanel
,但我不确定我是否走在正确的道路上。
<StackPanel Margin="5" Orientation="Horizontal" Background="DarkBlue" Height="28">
<Image Source="../Resources/crown.png" Height="18"/>
<TextBlock Text="Hello, I am a text block!" Margin="3 0 0 0"/>
</StackPanel>
不总是相同的行,也可以只是文本。
有谁知道我如何实现这样的东西(也许也在 code-behind 中)?
如果您希望图像实际位于文本中(如表情符号),那么您将不得不做一些工作。这听起来像是我真正想要用户控件的几次之一,其重点是扫描文本以查找表情符号值并动态构建数据模板。
请记住,您在 XAML 中可以做的任何事情都可以在代码中完成,因此我正在考虑的代码将遵循以下总体思路:
- 扫描文本以获取表情符号值并创建数据元素值列表。
- 创建一个 DockPanel。
- 对于列表中的每个元素,添加一个 TextBlock 或一个图像(基于值)。
- 将 this.Content 设置为 DockPanel。
我认为这正是您要找的东西,但如果您只想要一个图像,那么 ValueConverter 建议会起作用。
您没有任何数据结构,所以让我们在下面创建它们。这种方法并不完整,只是 太多选项 无法实现此方案。您可以随时调整、改进和扩展示例以满足您的要求。
我将使用 ItemsControl
,因为这似乎是一个只读通知控件。如果您希望这些项目是可选的,您可以轻松地将其替换为 ListBox
或 ListView
。
思路很简单,有一个Line
模型,里面包含了所有要显示的信息,它是一个属性类型的DateTime
时间戳和一个可枚举的LineFragment
,表示一行中显示的所有不同数据类型。
public class Line
{
public Line(DateTime dateTime, IEnumerable<LineFragment> fragments)
{
DateTime = dateTime;
Fragments = fragments;
}
public DateTime DateTime { get; }
public IEnumerable<LineFragment> Fragments { get; }
}
LineFragment
是许多在一行中表示文本和其他数据的派生类型的基本类型。
public abstract class LineFragment
{
}
一个简单的TextFragment
表示一行中的纯文本。
public class TextFragment : LineFragment
{
public TextFragment(string text)
{
Text = text;
}
public string Text { get; }
}
代表玩家的玩家片段,例如Knochenbrecher Messerkämpferin.
public class PlayerFragment : LineFragment
{
public PlayerFragment(string name)
{
Name = name;
}
public string Name { get; }
}
代表头骨和戴牙套的皇冠的玩家状态片段。
public class PlayerStatFragment : LineFragment
{
public PlayerStatFragment(int skulls, int crowns)
{
Skulls = skulls;
Crowns = crowns;
}
public int Skulls { get; }
public int Crowns { get; }
}
一个属性统计片段,它具有代表 Glory、Experience 或 等属性的衍生物银色.
public abstract class AttributeStatFragment : LineFragment
{
protected AttributeStatFragment(string name, AttributeStatOperator statOperator, int value)
{
Name = name;
Operator = statOperator;
Value = value;
}
public string Name { get; }
public AttributeStatOperator Operator { get; }
public int Value { get; }
}
public class GloryStatFragment : AttributeStatFragment
{
public GloryStatFragment(string name, AttributeStatOperator statOperator, int value) : base(name, statOperator, value)
{
}
}
public class ExperienceStatFragment : AttributeStatFragment
{
public ExperienceStatFragment(string name, AttributeStatOperator statOperator, int value) : base(name, statOperator, value)
{
}
}
public class SilverStatFragment : AttributeStatFragment
{
public SilverStatFragment(string name, AttributeStatOperator statOperator, int value) : base(name, statOperator, value)
{
}
}
public enum AttributeStatOperator
{
Plus,
Minus
}
代表税收的税收片段和衍生品,例如 Guild Tax。
public abstract class TaxFragment : LineFragment
{
public TaxFragment(string name, int value)
{
Name = name;
Value = value;
}
public string Name { get; }
public int Value { get; }
}
public class GuildTaxFragment : TaxFragment
{
public GuildTaxFragment(string name, int tax) : base(name, tax)
{
}
}
现在您可以在视图模型或代码隐藏中公开 Line
的集合。如果您在运行时添加项目,则必须使用 ObervableCollection<T>
,否则添加、删除或插入项目等更改将不会反映在用户界面中。
public ObservableCollection<Line> Lines { get; }
不要忘记初始化集合 属性。之后你可以像这样添加你的项目:
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du hast"),
new GloryStatFragment(GloryStatName, AttributeStatOperator.Plus, 820),
new PlayerStatFragment(390, 273),
new TextFragment("erhalten")
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du erhältst"),
new ExperienceStatFragment(ExperienceStatName, AttributeStatOperator.Plus, 42)
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new PlayerFragment("Knochenbrecher Messerkämpferin"),
new TextFragment("hat"),
new TextFragment("für dich regeneriert")
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du hast"),
new SilverStatFragment(SilverStatName, AttributeStatOperator.Plus, 184),
new PlayerStatFragment(75, 61),
new TextFragment("erhalten")
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du hast"),
new GuildTaxFragment(GuildTaxName, 46),
new TextFragment("bezahlt")
}));
为了显示片段,我们必须为 XAML 中的项目创建 DataTemplate
s。请注意,出于演示目的,我在 Resources
文件夹中创建了虚拟图像。
<Style x:Key="LineFragmentImageStyle"
TargetType="{x:Type Image}">
<Setter Property="Width"
Value="10" />
<Setter Property="Height"
Value="10" />
<Setter Property="Margin"
Value="2" />
</Style>
<Style x:Key="OperatorTextBlockStyle"
TargetType="{x:Type TextBlock}">
<Setter Property="Text"
Value="+ " />
<Style.Triggers>
<DataTrigger Binding="{Binding}"
Value="{x:Static local:AttributeStatOperator.Minus}">
<Setter Property="Text"
Value="- " />
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type local:TextFragment}">
<TextBlock Text="{Binding Text, StringFormat='{}{0} '}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:PlayerFragment}">
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:PlayerStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="( " />
<Image Source="Resources/skull.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Skulls, StringFormat='{} {0} '}" />
<Image Source="Resources/crown.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Crowns, StringFormat='{} {0} '}" />
<TextBlock Text=" ) " />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ExperienceStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding Operator}"
Style="{StaticResource OperatorTextBlockStyle}" />
<Image Source="Resources/key.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:GloryStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding Operator}"
Style="{StaticResource OperatorTextBlockStyle}" />
<Image Source="Resources/badge.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SilverStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding Operator}"
Style="{StaticResource OperatorTextBlockStyle}" />
<Image Source="Resources/silver.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:GuildTaxFragment}">
<StackPanel Orientation="Horizontal">
<Image Source="Resources/silver.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
<TextBlock Text="(" />
<Image Source="Resources/armor.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text=")" />
</StackPanel>
</DataTemplate>
然后我们为 Line
本身创建一个数据模板。每行显示一个时间戳,在 ItemsControl
旁边有一个 TextBlock
,使用上面定义的数据模板水平显示所有片段。 WrapPanel
如果超出视口,将自动换行。
<DataTemplate DataType="{x:Type local:Line}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Margin="3"
Text="{Binding DateTime, StringFormat=[hh:mm:ss]}" />
<ItemsControl Grid.Column="1"
Margin="3"
ItemsSource="{Binding Fragments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</DataTemplate>
最后但同样重要的是,要显示 Lines
集合,我们必须向使用 Line
数据模板的用户界面 XAML 添加一个 ItemsControl
显示线条。
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Lines}" Style="{StaticResource LinesItemsControlStyle}" />
</ScrollViewer>
就是这样。您可以轻松地使用其他项目扩展它。这是它的样子:
我想在 ListView
中显示不同行的通知。
我试过 StackPanel
,但我不确定我是否走在正确的道路上。
<StackPanel Margin="5" Orientation="Horizontal" Background="DarkBlue" Height="28">
<Image Source="../Resources/crown.png" Height="18"/>
<TextBlock Text="Hello, I am a text block!" Margin="3 0 0 0"/>
</StackPanel>
不总是相同的行,也可以只是文本。
有谁知道我如何实现这样的东西(也许也在 code-behind 中)?
如果您希望图像实际位于文本中(如表情符号),那么您将不得不做一些工作。这听起来像是我真正想要用户控件的几次之一,其重点是扫描文本以查找表情符号值并动态构建数据模板。
请记住,您在 XAML 中可以做的任何事情都可以在代码中完成,因此我正在考虑的代码将遵循以下总体思路:
- 扫描文本以获取表情符号值并创建数据元素值列表。
- 创建一个 DockPanel。
- 对于列表中的每个元素,添加一个 TextBlock 或一个图像(基于值)。
- 将 this.Content 设置为 DockPanel。
我认为这正是您要找的东西,但如果您只想要一个图像,那么 ValueConverter 建议会起作用。
您没有任何数据结构,所以让我们在下面创建它们。这种方法并不完整,只是 太多选项 无法实现此方案。您可以随时调整、改进和扩展示例以满足您的要求。
我将使用 ItemsControl
,因为这似乎是一个只读通知控件。如果您希望这些项目是可选的,您可以轻松地将其替换为 ListBox
或 ListView
。
思路很简单,有一个Line
模型,里面包含了所有要显示的信息,它是一个属性类型的DateTime
时间戳和一个可枚举的LineFragment
,表示一行中显示的所有不同数据类型。
public class Line
{
public Line(DateTime dateTime, IEnumerable<LineFragment> fragments)
{
DateTime = dateTime;
Fragments = fragments;
}
public DateTime DateTime { get; }
public IEnumerable<LineFragment> Fragments { get; }
}
LineFragment
是许多在一行中表示文本和其他数据的派生类型的基本类型。
public abstract class LineFragment
{
}
一个简单的
TextFragment
表示一行中的纯文本。public class TextFragment : LineFragment { public TextFragment(string text) { Text = text; } public string Text { get; } }
代表玩家的玩家片段,例如Knochenbrecher Messerkämpferin.
public class PlayerFragment : LineFragment { public PlayerFragment(string name) { Name = name; } public string Name { get; } }
代表头骨和戴牙套的皇冠的玩家状态片段。
public class PlayerStatFragment : LineFragment { public PlayerStatFragment(int skulls, int crowns) { Skulls = skulls; Crowns = crowns; } public int Skulls { get; } public int Crowns { get; } }
一个属性统计片段,它具有代表 Glory、Experience 或 等属性的衍生物银色.
public abstract class AttributeStatFragment : LineFragment { protected AttributeStatFragment(string name, AttributeStatOperator statOperator, int value) { Name = name; Operator = statOperator; Value = value; } public string Name { get; } public AttributeStatOperator Operator { get; } public int Value { get; } } public class GloryStatFragment : AttributeStatFragment { public GloryStatFragment(string name, AttributeStatOperator statOperator, int value) : base(name, statOperator, value) { } } public class ExperienceStatFragment : AttributeStatFragment { public ExperienceStatFragment(string name, AttributeStatOperator statOperator, int value) : base(name, statOperator, value) { } } public class SilverStatFragment : AttributeStatFragment { public SilverStatFragment(string name, AttributeStatOperator statOperator, int value) : base(name, statOperator, value) { } } public enum AttributeStatOperator { Plus, Minus }
代表税收的税收片段和衍生品,例如 Guild Tax。
public abstract class TaxFragment : LineFragment { public TaxFragment(string name, int value) { Name = name; Value = value; } public string Name { get; } public int Value { get; } } public class GuildTaxFragment : TaxFragment { public GuildTaxFragment(string name, int tax) : base(name, tax) { } }
现在您可以在视图模型或代码隐藏中公开 Line
的集合。如果您在运行时添加项目,则必须使用 ObervableCollection<T>
,否则添加、删除或插入项目等更改将不会反映在用户界面中。
public ObservableCollection<Line> Lines { get; }
不要忘记初始化集合 属性。之后你可以像这样添加你的项目:
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du hast"),
new GloryStatFragment(GloryStatName, AttributeStatOperator.Plus, 820),
new PlayerStatFragment(390, 273),
new TextFragment("erhalten")
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du erhältst"),
new ExperienceStatFragment(ExperienceStatName, AttributeStatOperator.Plus, 42)
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new PlayerFragment("Knochenbrecher Messerkämpferin"),
new TextFragment("hat"),
new TextFragment("für dich regeneriert")
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du hast"),
new SilverStatFragment(SilverStatName, AttributeStatOperator.Plus, 184),
new PlayerStatFragment(75, 61),
new TextFragment("erhalten")
}));
Lines.Add(new Line(DateTime.Now, new List<LineFragment>
{
new TextFragment("Du hast"),
new GuildTaxFragment(GuildTaxName, 46),
new TextFragment("bezahlt")
}));
为了显示片段,我们必须为 XAML 中的项目创建 DataTemplate
s。请注意,出于演示目的,我在 Resources
文件夹中创建了虚拟图像。
<Style x:Key="LineFragmentImageStyle"
TargetType="{x:Type Image}">
<Setter Property="Width"
Value="10" />
<Setter Property="Height"
Value="10" />
<Setter Property="Margin"
Value="2" />
</Style>
<Style x:Key="OperatorTextBlockStyle"
TargetType="{x:Type TextBlock}">
<Setter Property="Text"
Value="+ " />
<Style.Triggers>
<DataTrigger Binding="{Binding}"
Value="{x:Static local:AttributeStatOperator.Minus}">
<Setter Property="Text"
Value="- " />
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type local:TextFragment}">
<TextBlock Text="{Binding Text, StringFormat='{}{0} '}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:PlayerFragment}">
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:PlayerStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="( " />
<Image Source="Resources/skull.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Skulls, StringFormat='{} {0} '}" />
<Image Source="Resources/crown.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Crowns, StringFormat='{} {0} '}" />
<TextBlock Text=" ) " />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ExperienceStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding Operator}"
Style="{StaticResource OperatorTextBlockStyle}" />
<Image Source="Resources/key.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:GloryStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding Operator}"
Style="{StaticResource OperatorTextBlockStyle}" />
<Image Source="Resources/badge.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SilverStatFragment}">
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding Operator}"
Style="{StaticResource OperatorTextBlockStyle}" />
<Image Source="Resources/silver.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:GuildTaxFragment}">
<StackPanel Orientation="Horizontal">
<Image Source="Resources/silver.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text="{Binding Value, StringFormat='{} {0} '}" />
<TextBlock Text="{Binding Name, StringFormat='{}{0} '}" />
<TextBlock Text="(" />
<Image Source="Resources/armor.png"
Style="{StaticResource LineFragmentImageStyle}" />
<TextBlock Text=")" />
</StackPanel>
</DataTemplate>
然后我们为 Line
本身创建一个数据模板。每行显示一个时间戳,在 ItemsControl
旁边有一个 TextBlock
,使用上面定义的数据模板水平显示所有片段。 WrapPanel
如果超出视口,将自动换行。
<DataTemplate DataType="{x:Type local:Line}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Margin="3"
Text="{Binding DateTime, StringFormat=[hh:mm:ss]}" />
<ItemsControl Grid.Column="1"
Margin="3"
ItemsSource="{Binding Fragments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</DataTemplate>
最后但同样重要的是,要显示 Lines
集合,我们必须向使用 Line
数据模板的用户界面 XAML 添加一个 ItemsControl
显示线条。
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Lines}" Style="{StaticResource LinesItemsControlStyle}" />
</ScrollViewer>
就是这样。您可以轻松地使用其他项目扩展它。这是它的样子: