我正在开发wpf中的messenger应用程序。
我想创建控件以在聊天室中显示消息
我希望像在windowsphone的消息应用程序或任何其他智能手机中显示传入和传出消息一样。
左侧的传入消息,传出的消息 - 在右侧,并按时间戳排序。
我查看了telerik控件工具包的示例。在那里,他们使用整个容器来显示按时间戳排序的所有消息,您为其指定了传入和传出消息的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的新手。所以我不知道如何创建这样的容器。
答案 0 :(得分:0)
大多数情况下,您可以使用ListBox或ListView实现此目的,并根据消息类型设置Items的HorizontalAlignment。硬编码示例:
<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上(假设您的消息类具有此属性)。
<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>