C# 如何创建容器以在messenger应用程序中显示传入和传出消息?

C# 如何创建容器以在messenger应用程序中显示传入和传出消息?,c#,wpf,xaml,C#,Wpf,Xaml,我正在wpf中开发messenger应用程序 我想创建一个控件来在聊天室中显示消息 我想让它像windowsphone的消息应用程序或任何其他智能手机显示传入和传出消息的方式一样 左边是传入消息,右边是传出消息,并按时间戳排序 我查看了telerik control toolkit的示例。在那里,他们使用整个容器来显示所有按时间戳排序的消息,您可以为传入和传出的消息指定datatemplate来使用它。但问题是telerik不是免费的工具包。所以我必须自己做 这是我想要的例子 <UserC

我正在wpf中开发messenger应用程序

我想创建一个控件来在聊天室中显示消息

我想让它像windowsphone的消息应用程序或任何其他智能手机显示传入和传出消息的方式一样

左边是传入消息,右边是传出消息,并按时间戳排序

我查看了telerik control toolkit的示例。在那里,他们使用整个容器来显示所有按时间戳排序的消息,您可以为传入和传出的消息指定datatemplate来使用它。但问题是telerik不是免费的工具包。所以我必须自己做

这是我想要的例子

<UserControl.DataContext>
    <viewModels:MessagesViewModel/>
</UserControl.DataContext>
以下是传入和传出消息的模板

<UserControl.Resources>
    <DataTemplate x:Key="IncomingMessageTemplate">
        <Grid Margin="12,12">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <StackPanel>

                <telerikPrimitivesNamespace:RadPointerContentControl Background="{StaticResource PhoneAccentBrush}"
                                                                      PointerDirection="Top"
                                                                     TargetPoint="-141,-100">
                    <telerikPrimitivesNamespace:RadPointerContentControl.PointerTemplate>
                        <DataTemplate>
                            <Polygon Width="14"
                                     Height="28"
                                     Points="0,14 0,28 14,14"
                                     StrokeThickness="0"
                                     Fill="{StaticResource PhoneAccentBrush}"
                                     RenderTransformOrigin="0.5, 0.5">
                                <Polygon.RenderTransform>
                                    <TransformGroup>
                                        <ScaleTransform ScaleX="-1"/>
                                        <TranslateTransform Y="-2"/>
                                    </TransformGroup>
                                </Polygon.RenderTransform>
                            </Polygon>
                        </DataTemplate>
                    </telerikPrimitivesNamespace:RadPointerContentControl.PointerTemplate>
            <StackPanel>
            <TextBlock Text="{Binding Path=Text}" MinHeight="54"
                                       TextWrapping="Wrap" Foreground="White"
                                       Margin="8, 2, 8, 6"/>
                    <TextBlock Text="{Binding Path=FormattedTimeStamp}"
                                   Margin="8, 2, 8, 2"
                                   HorizontalAlignment="Right"

                                   Foreground="{StaticResource PhoneSubtleBrush}"/>

            </StackPanel>
            </telerikPrimitivesNamespace:RadPointerContentControl>
            </StackPanel>
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="OutgoingMessageTemplate">
        <Grid Margin="12,12">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="3*"/>
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="1">

                <telerikPrimitivesNamespace:RadPointerContentControl 
                     PointerDirection="bottom"
                                                             TargetPoint="145, -100">
                    <telerikPrimitivesNamespace:RadPointerContentControl.Background>
                        <SolidColorBrush Color="{StaticResource PhoneAccentColor}" Opacity="0.5"/>
                    </telerikPrimitivesNamespace:RadPointerContentControl.Background>
                    <telerikPrimitivesNamespace:RadPointerContentControl.PointerTemplate>
                        <DataTemplate>
                            <Polygon Width="14"
                                     Height="28"
                                     Points="0,14 0,28 14,14"
                                     StrokeThickness="0">
                                <Polygon.RenderTransform>
                                    <TransformGroup>
                                        <ScaleTransform ScaleX="-1"/>
                                        <TranslateTransform Y="0"/>
                                    </TransformGroup>
                                </Polygon.RenderTransform>
                                <Polygon.Fill>
                                    <SolidColorBrush Color="{StaticResource PhoneAccentColor}" Opacity="0.5"/>
                                </Polygon.Fill>
                            </Polygon>
                        </DataTemplate>
                    </telerikPrimitivesNamespace:RadPointerContentControl.PointerTemplate>

                    <StackPanel>
                        <TextBlock Text="{Binding Path=Text}" MinHeight="54"
                                           TextWrapping="Wrap" Foreground="White"
                                           Margin="8, 2, 8, 6"/>
                        <TextBlock Text="{Binding Path=FormattedTimeStamp}"
                                   Margin="8, 2, 8, 2"
                                   HorizontalAlignment="right"
                                   Foreground="{StaticResource PhoneSubtleBrush}"/>

                    </StackPanel>
                </telerikPrimitivesNamespace:RadPointerContentControl>
            </StackPanel>
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="TextBoxTemplate">
        <Grid Margin="0, 74, 0, 12">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="3*"/>
            </Grid.ColumnDefinitions>

            <telerikPrimitivesNamespace:RadPointerContentControl Grid.ColumnSpan="2"
                                            PointerDirection="bottom"         TargetPoint="199, -100">
                <telerikPrimitivesNamespace:RadPointerContentControl.PointerTemplate>
                    <DataTemplate>
                        <Polygon Width="14"
                                 Height="28"
                                 Points="0,14 0,28 14,14"
                                 StrokeThickness="0">
                            <Polygon.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform ScaleX="-1"/>
                                    <TranslateTransform Y="-32"/>
                                </TransformGroup>
                            </Polygon.RenderTransform>
                        </Polygon>
                    </DataTemplate>
                </telerikPrimitivesNamespace:RadPointerContentControl.PointerTemplate>

                <telerikPrimitives:RadTextBox x:Name="PART_TextBox"
                                                      Watermark="chat on Facebook"
                                                      Margin="0,-5,0,20"
                                                      AcceptsReturn="True"
                                                      ActionButtonVisibility="Visible">
                    <telerikPrimitives:RadTextBox.ActionButtonStyle>
                        <Style TargetType="telerikTextBox:TextBoxActionButton">
                            <Setter Property="ButtonType"
                                            Value="Custom"/>
                            <Setter Property="RestStateImageSource"
                                            Value="Images/SendIcon.png"/>
                        </Style>
                    </telerikPrimitives:RadTextBox.ActionButtonStyle>
                </telerikPrimitives:RadTextBox>
            </telerikPrimitivesNamespace:RadPointerContentControl>
        </Grid>
    </DataTemplate>
</UserControl.Resources>
下面是我想要的-控件根据传入和传出消息模板查看对话

<telerikData:RadConversationView Grid.Row="0" 
                                         ItemsSource="{Binding Messages}"
                                         x:Name="conversationView" TextBoxTemplate="{StaticResource TextBoxTemplate}"
                                         Margin="12, 0" IncomingMessageTemplate="{StaticResource IncomingMessageTemplate}" OutgoingMessageTemplate="{StaticResource OutgoingMessageTemplate}"
                                         SendingMessage="OnSendingMessage" />
下面是此控件的MessagesModelView

public class MessagesViewModel : ViewModelBase
{
    private const int DefaultUserId = 4;
    private ObservableCollection<CustomMessage> messages;
    private ObservableCollection<Person> people;
    private Person you;
    private Person conversationBuddy;
    private int currentGroup = 0;
    private CustomMessage previousMessage;

    private void InitializeMessages()
    {
        this.messages = new ObservableCollection<CustomMessage>();
        this.messages.CollectionChanged += this.OnMessagesCollectionChanged;

    }

    private void OnMessagesCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems == null)
        {
            return;
        }
        CustomMessage message = e.NewItems[0] as CustomMessage;
        if (previousMessage != null)
        {
            if (previousMessage.SenderId != message.SenderId)
            {
                this.currentGroup++;
            }
        }
        if (message.Group == null)
        {
            message.Group = this.currentGroup;
        }
        previousMessage = message;
    }

    private void InitializePeople()
    {
        this.people = new ObservableCollection<Person>();

        for (int i = 1; i <= 5; i++)
        {
            Person person = new Person() { PersonId = i, Name = "PERSON " + i, Picture = new Uri("Images/FrameThumbnail.png", UriKind.RelativeOrAbsolute) };
            this.people.Add(person);
        }
    }

    public Person You
    {
        get
        {
            return this.you;
        }
        set
        {
            this.you = value;
            this.OnPropertyChanged("You");
        }
    }

    public Person ConversationBuddy
    {
        get
        {
            return this.conversationBuddy;
        }
        set
        {
            this.conversationBuddy = value;
            this.OnPropertyChanged("ConversationBuddy");
        }
    }

    public ObservableCollection<CustomMessage> Messages
    {
        get
        {
            if (this.messages == null)
            {
                this.InitializeMessages();
            }
            return this.messages;
        }
        private set
        {
            this.messages = value;
        }
    }

    public ObservableCollection<Person> People
    {
        get
        {
            if (this.people == null)
            {
                this.InitializePeople();
            }
            return this.people;
        }
        private set
        {
            this.people = value;
        }
    }
}

public class CustomMessage : ConversationViewMessage, IComparable
{
    public CustomMessage(string text, DateTime timeStamp, ConversationViewMessageType type, int senderId, int? group = null)
        : base(text, timeStamp, type)
    {
        this.SenderId = senderId;
        this.Group = group;
    }

    public int SenderId
    {
        get;
        private set;
    }

    public int? Group
    {
        get;
        set;
    }

    public SolidColorBrush MessageBackground
    {
        get
        {
            int id = this.SenderId % 6;
            switch (id)
            {
                case 0: return new SolidColorBrush(Color.FromArgb(255, 51, 153, 51));
                case 1: return new SolidColorBrush(Color.FromArgb(255, 27, 161, 226));
                case 2: return new SolidColorBrush(Color.FromArgb(255, 255, 0, 151));
                case 3: return new SolidColorBrush(Color.FromArgb(255, 240, 150, 9));
                case 4: return new SolidColorBrush(Color.FromArgb(255, 0, 171, 169));
                case 5: return new SolidColorBrush(Color.FromArgb(255, 140, 191, 38));
            }
            return App.Current.Resources["PhoneAccentBrush"] as SolidColorBrush;
        }
    }

    public string FormattedTimeStamp
    {
        get
        {
            return this.TimeStamp.ToShortTimeString();
        }
    }

    public override bool Equals(object obj)
    {
        CustomMessage secondMessage = obj as CustomMessage;

        if (obj is DataGroup)
        {
            secondMessage = (obj as DataGroup).Key as CustomMessage;
        }

        return this.Group == secondMessage.Group;
    }

    public override int GetHashCode()
    {
        return this.Group.GetHashCode();
    }

    public int CompareTo(object obj)
    {
        CustomMessage targetMessage = obj as CustomMessage;

        if (targetMessage != null)
        {
            return this.Group > targetMessage.Group ? 1 : this.Group == targetMessage.Group ? 0 : -1;
        }

        if (obj is DataGroup)
        {
            targetMessage = (obj as DataGroup).Key as CustomMessage;

            return this.Group > targetMessage.Group ? 1 : this.Group == targetMessage.Group ? 0 : -1;
        }

        return 0;
    }
}

public class Person : ViewModelBase
{
    private int personId;
    private string name;
    private Uri picture;

    public int PersonId
    {
        get
        {
            return this.personId;
        }
        set
        {
            if (this.personId != value)
            {
                this.personId = value;
                this.OnPropertyChanged("PersonId");
            }
        }
    }

    public string Name
    {
        get
        {
            return this.name;
        }
        set
        {
            if (this.name != value)
            {
                this.name = value;
                this.OnPropertyChanged("Name");
            }
        }
    }

    public Uri Picture
    {
        get
        {
            return this.picture;
        }
        set
        {
            if (this.picture != value)
            {
                this.picture = value;
                this.OnPropertyChanged("Picture");
            }
        }
    }
}
这是我的聊天窗口的草图设计

如您所见,根据设计,传入消息显示在左侧,传出消息显示在右侧。所有消息都是多边形的,并按时间戳排序。
我在telerik工具包中寻找类似RadConversationView的容器,但找不到。所以我必须自己开发它。但我对xaml还是新手。所以我不知道如何创建这样的容器。

最简单的方法是使用ListBox或ListView实现这一点,并根据消息类型设置项目的水平对齐方式。硬编码示例:

  <ListBox>
        <ListBox.Items>
            <ListBoxItem Margin="10" HorizontalAlignment="Left">Incoming</ListBoxItem>
            <ListBoxItem Margin="10" HorizontalAlignment="Right">Outgoing</ListBoxItem>
            <ListBoxItem Margin="10" HorizontalAlignment="Left">Incoming</ListBoxItem>
            <ListBoxItem Margin="10" HorizontalAlignment="Right">Outgoing</ListBoxItem>
            <ListBoxItem Margin="10" HorizontalAlignment="Right">Outgoing</ListBoxItem>
            <ListBoxItem Margin="10" HorizontalAlignment="Right">Outgoing</ListBoxItem>
        </ListBox.Items>
    </ListBox>
若要使其成为动态的,可以定义ItemContainerStyle并在MessageType上放置触发器,前提是message类具有此属性

 <ListBox>
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="HorizontalAlignment" Value="Right"></Setter>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding MessageType}" Value="Incoming">
                        <Setter Property="HorizontalAlignment" Value="Left"></Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>

您是否有任何特定的问题或特定的问题,我们可以帮助您?我不知道如何创建容器,根据incomingMessageTemplate和outgoingMessageTemplate显示消息,并按时间戳排序显示它们。这是我的问题,WPF有很多操作指南。例如: