简体   繁体   English

如何添加不同的ListBox项WPF

[英]How to add different ListBox items WPF

I am implementing a chat application like this image: 我正在实现一个像这个图像的聊天应用程序: 在此输入图像描述

I started by creating a ListBox and setting a ListBox.ItemTemplate, but i can't figure out how can i control the ListBox to add an item with the layout as a message received or as send (just like Whatsapp). 我开始创建一个ListBox并设置一个ListBox.ItemTemplate,但我无法弄清楚我如何控制ListBox添加一个项目的布局作为收到的消息或发送(就像Whatsapp)。

Here is my code: 这是我的代码:

<ListBox Name="ChatListBox" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="#00FFFFFF" BorderBrush="{x:Null}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
                            <ListBox.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Background="#00FFFFFF"/>
                                </ItemsPanelTemplate>
                            </ListBox.ItemsPanel>
                            <ListBox.ItemContainerStyle>
                                <Style TargetType="{x:Type ListBoxItem}">
                                    <Setter Property="Focusable" Value="False"/>
                                </Style>
                            </ListBox.ItemContainerStyle>
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <!--If the user sends a msg-->
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="9*"/>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>
                                        <Border Grid.Column="0" Margin="0" BorderThickness="1" BorderBrush="#9f9f9f" Background="#c4df9b" CornerRadius="10">
                                            <TextBlock Name="MsgText" Background="#c4df9b" Foreground="Black" TextAlignment="Center" TextWrapping="Wrap" Margin="5" Text="{Binding text}" FontSize="14"/>
                                        </Border>
                                        <Image Grid.Column="1" Source="Images/user.png" Margin="5" Height="{Binding ElementName=MsgText, Path=ActualHeight}"/>
                                        <!--
                                        If the user receives a msg
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*"/>
                                            <ColumnDefinition Width="9*"/>
                                        </Grid.ColumnDefinitions>
                                        <Image Grid.Column="0" Source="Images/user.png" Margin="5" Height="{Binding ElementName=MsgText, Path=ActualHeight}"/>
                                        <Border Grid.Column="1" Margin="0" BorderThickness="1" BorderBrush="#9f9f9f" Background="#c4df9b" CornerRadius="10">
                                            <TextBlock Name="MsgText" Background="#c4df9b" Foreground="Black" TextAlignment="Center" TextWrapping="Wrap" Margin="5" Text="{Binding text}" FontSize="14"/>
                                        </Border>-->
                                    </Grid>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>

Here is my c# code: 这是我的c#代码:

List<ChatItem> chatItem = new List<ChatItem>();
chatItem.Add(new ChatItem() { text = "Hello...", isFromUser = false });
        chatItem.Add(new ChatItem() { text = "hi!", isFromUser = true });
        chatItem.Add(new ChatItem() { text = "this is a test, this is a test, this is a test, this is a test, this is a test, this is a test, this is a test, this is a test, this is a test, this is a test", isFromUser = false });
        ChatListBox.ItemsSource = chatItem;

This is how the ListBox is redered: 这就是ListBox的处理方式:

在此输入图像描述

Is there any way of adding an IF statement at the ListBox with WPF? 有没有办法在带有WPF的ListBox中添加IF语句? or how can i control which ListBox.ItemTemplate to add. 或者我如何控制要添加的ListBox.ItemTemplate。

If your messages are of the same class you could use itemsTemplateSelector as in http://codingbandit.com/blog/?p=8 如果您的消息属于同一类,则可以使用itemsTemplateSelector,如http://codingbandit.com/blog/?p=8

if there are different classes you should just use the datatemplate datatype property as in Conditional List itemtemplate or datatemplate in WPF 如果有不同的类,你应该使用datatemplate数据类型属性,如在条件列表itemtemplate或datatemplate在WPF中

Maybe this will give you an idea 也许这会给你一个想法

Xaml XAML

<Window x:Class="Q1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Q1"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate x:Key="intDataTemplate">
        <TextBox Text="{Binding Path=.}" Width="80"/>
    </DataTemplate>
    <DataTemplate x:Key="stringDataTemplate">
        <TextBlock Text="{Binding Path=.}"/>
    </DataTemplate>
    <local:MyDataTemplateSelector IntDataTemplate="{StaticResource intDataTemplate}"
                                  StringDataTemplate="{StaticResource stringDataTemplate}"
                                  x:Key="myDataTemplateSelector"/>
</Window.Resources>
<Grid>
    <ListBox x:Name="myListBox" ItemsSource="{Binding}"
             ItemTemplateSelector="{StaticResource myDataTemplateSelector}">

    </ListBox>
</Grid>

Code-Behind 代码隐藏

using System;
using System.Collections.Generic;
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.Navigation;
using System.Windows.Shapes;

namespace Q1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            List<System.Object> myList = new List<object>();
            myList.Add(1);
            myList.Add("Alpha");
            myList.Add(2);
            myList.Add("Beta");
            myList.Add(3);
            myList.Add("Gamma");
            myListBox.DataContext = myList;
        }
    }

}

DataTemplateSelector DataTemplateSelector

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Q1
{
    public class MyDataTemplateSelector : System.Windows.Controls.DataTemplateSelector
    {
        public System.Windows.DataTemplate IntDataTemplate { get; set; }
        public System.Windows.DataTemplate StringDataTemplate { get; set; }
        public MyDataTemplateSelector()
        {
            IntDataTemplate = new System.Windows.DataTemplate();
            StringDataTemplate = new System.Windows.DataTemplate();
        }
        public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            if (item is Int32)
            {
                return IntDataTemplate;
            }
            else
            {
                return StringDataTemplate;
            }
        }
    }
}

Another option is to simply use a data trigger: 另一种选择是简单地使用数据触发器:

<DataTemplate x:Key="ToTemplate">
    ... etc ...
</DataTemplate>

<DataTemplate x:Key="FromTemplate">
    ... etc ...
</DataTemplate>

<Style TargetType="ListBoxItem">
    <Style.Triggers>
        <DataTrigger Binding="{Binding isFromUser}" Value="false">
            <Setter Property="Template" Value="{StaticResource ToTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding isFromUser}" Value="true">
            <Setter Property="Template" Value="{StaticResource FromTemplate}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

You could use DataTemplateSelector to select the Template according with your data, here's an example. 您可以使用DataTemplateSelector根据您的数据选择模板,这是一个示例。

For this example, I'll use messages, a client message and a server message: 对于此示例,我将使用消息,客户端消息和服务器消息:

public abstract class Message
{
    public string Content { get; set; }

    public override string ToString()
    {
        return Content;
    }
}

public class ServerMessage : Message
{

}

public class ClientMessage : Message
{

}

In this way, I can check the type of the object and apply certain template. 通过这种方式,我可以检查对象的类型并应用某些模板。

Let's define our templates and selector: 让我们定义我们的模板和选择器:

XAML: XAML:

    <DataTemplate x:Key="clientTemplate" >
        <TextBlock Text="{Binding Content}"
                   Foreground="Red"/>
    </DataTemplate>

    <DataTemplate x:Key="serverClient">
        <TextBlock Text="{Binding Content}"
                   Foreground="Green"/>
    </DataTemplate>

    <local:MessageTemplateSelector x:Key="messageSelector" 
                                   ServerTemplate="{StaticResource serverClient}"
                                   ClientTemplate="{StaticResource clientTemplate}"/>

DataTemplateSelector DataTemplateSelector

public class MessageTemplateSelector : DataTemplateSelector
{
    public DataTemplate ClientTemplate { get; set; }

    public DataTemplate ServerTemplate { get; set; }

    public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        if (item.GetType() == typeof(ClientMessage))
            return ClientTemplate;

        return ServerTemplate;
    }
}

First of all, as you can see I create two DataTemplate properties on my Selector, which I set it by XAML, doing this is easy to retrieve the DataTemplate, and finally I just compare the item received by parameter, which is the binded object(Message), and check its type and return the template. 首先,正如您所看到的,我在Selector上创建了两个DataTemplate属性,我通过XAML设置它,这样做很容易检索DataTemplate,最后我只是比较参数接收的项目,即绑定对象(消息),并检查其类型并返回模板。

And that is it, it works like expected. 就是这样,它就像预期的那样工作。

在此输入图像描述

UPDATE: Let's supposed that you want Template your items based on their types, just like my example, you could this the same thing above without using TemplateSelector, you could define the DataType of your Template: 更新:假设您希望模板根据其类型进行项目,就像我的示例一样,您可以在不使用TemplateSelector的情况下使用相同的内容,您可以定义模板的DataType:

<Window.Resources>
    <DataTemplate DataType="{x:Type local:ClientMessage}">
        <TextBlock Text="{Binding Content}"
                   Foreground="Red"/>
    </DataTemplate>

    <DataTemplate DataType="{x:Type  local:ServerMessage}">
        <TextBlock Text="{Binding Content}"
                   Foreground="Green"/>
    </DataTemplate>
</Window.Resources>

Doing so, the Template will be selected automatically according the type of the object. 这样,将根据对象的类型自动选择模板。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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