简体   繁体   中英

Is it possible to display lines with text + icons in a ListView?

I would like to display different lines with notifications in a ListView .

在此处输入图像描述

I've tried a StackPanel , but I'm not sure whether I'm on the right path.

<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>

It's not always the same lines, it can also be just text.

Does anyone have an idea how I can implement something like this (maybe also in code-behind)?

If you want the Images actually inside the text (like an emoticon), then you are going to have to do some work. This sounds like one of the few times I would actually want a User Control, the point of which would be one that scans the Text looking for emoticon values and building a Data Template on the fly.

Remember that anything you can do in XAML you can do in code, so the code I'm thinking of would follow this general idea:

  1. Scan text for emoticon values and create a list of values for data elements.
  2. Create a DockPanel.
  3. Foreach element in the List, add either a TextBlock or an Image (based on value).
  4. Set this.Content to the DockPanel.

I think something like this is actually what you are looking for, but if you want just an Image, then the ValueConverter suggestion would work.

You do not have any data structure, so let us create them in the following. This approach is not complete and there are just too much options to implement this scenario. You can adapt, improve and extend the examples to fit your requirements at any point.

I will use an ItemsControl , because this seems to be a read-only notification control. If you want the items to be selectable, you can easily replace it with a ListBox or a ListView .

The idea is easy, there is a Line model that contains all information to be displayed, which is a property of type DateTime for the timestamp and an enumerable of LineFragment , which represents all the distinct data types that are displayed in a line.

public class Line
{
   public Line(DateTime dateTime, IEnumerable<LineFragment> fragments)
   {
      DateTime = dateTime;
      Fragments = fragments;
   }

   public DateTime DateTime { get; }

   public IEnumerable<LineFragment> Fragments { get; }
}

LineFragment is the base type for a lot of derived types that represent text and other data in one line.

public abstract class LineFragment
{
}
  • A simple TextFragment represents plain text in a line.

     public class TextFragment: LineFragment { public TextFragment(string text) { Text = text; } public string Text { get; } }
  • A player fragment that represents a player, eg Knochenbrecher Messerkämpferin .

     public class PlayerFragment: LineFragment { public PlayerFragment(string name) { Name = name; } public string Name { get; } }
  • A player status fragement that represents the skulls and crowns in braces.

     public class PlayerStatFragment: LineFragment { public PlayerStatFragment(int skulls, int crowns) { Skulls = skulls; Crowns = crowns; } public int Skulls { get; } public int Crowns { get; } }
  • An attribute stat fragment that has derivatives that represent attributes like Glory , Experience or Silver .

     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 }
  • A tax fragment and derivatives that represent taxes like 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) { } }

Now you can expose a collection of Line s in your view model or code-behind. If you are adding items at runtime you have to use an ObervableCollection<T> , otherwise changes like adding, removing or inserting items will not be reflected in the user interface.

public ObservableCollection<Line> Lines { get; }

Do not forget to initialize the collection property. After that you can add your items like this:

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")
}));

In order to display the fragments, we have to create DataTemplate s for our items in XAML. Please note that I have created dummy images that are located in the Resources folder for demonstration purposes.

<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>

Then we create a data template for the Line itself. Each line displays a timestamp with a TextBlock next to an ItemsControl that shows all fragments horizontally using the data templates defined above. The WrapPanel will automatically wrap a line if it exceeds the viewport.

<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>

Last but not least, to show the Lines collection, we have to add an ItemsControl to our user interface XAML that uses the Line data template to display the lines.

<ScrollViewer>
   <ItemsControl ItemsSource="{Binding Lines}" Style="{StaticResource LinesItemsControlStyle}" />
</ScrollViewer>

And that is it. You can extend it with other items easily. This is how it looks:

应用程序的屏幕截图(左),带有片段包装(右)。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM