在这篇文章中,将探讨如何在Windows Phone 7应用程序中实现一个聊天视图,该视图能够以对话的形式展示消息列表。此外,还将扩展这个概念,通过添加一个输入文本字段,允许用户与ELIZA(一个AI聊天机器人)进行对话。本文将探讨在回复时如何滚动消息列表,以确保最新的消息始终可见。
可以点击查看代码的实际运行效果。
曾希望在互联网上找到一个不错的C# ELIZA实现(一个经典的聊天应用程序,扮演治疗师的角色),但发现的唯一的实现相当基础。如果知道任何替代方案,请告诉!
简单聊天应用程序的布局使用了一个ConversationView用户控件实例,文本输入位于屏幕底部:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid contribControls:GridUtils.RowDefinitions=",Auto">
<ScrollViewer x:Name="ConversationScrollViewer">
<local:ConversationView x:Name="conversationView" />
</ScrollViewer>
<Grid Grid.Row="1" contribControls:GridUtils.RowDefinitions=",," Margin="0,10,0,0">
<Rectangle Fill="White" Grid.RowSpan="2" />
<txt:WatermarkedTextBox Watermark="type a message" TextWrapping="Wrap" AcceptsReturn="True" Padding="0" x:Name="TextInput" GotFocus="TextInput_GotFocus" LostFocus="TextInput_LostFocus" />
<Path Data="m 0,0 l 16,0 l 0,16 l -16,-16" Fill="White" Margin="0,0,5,0" HorizontalAlignment="Right" Grid.Row="2" />
</Grid>
</Grid>
</Grid>
注意简化的Grid标记,其中字符串",Auto"被用于代替更冗长的RowDefinition XAML元素。WatermarkedTextBox来自WindowsPhoneGeek的文章(它工作得很好——谢谢!)。为了使输入字段看起来像一个对话气泡,添加了一个简单的Path和Rectangle。
当用户点击输入TextBlock时,手机键盘将被显示,允许他们输入消息。这是遇到的第一个主要问题!
当手机键盘显示时,应用程序内容会被“推”上去以腾出空间给键盘。不幸的是,这会导致用户正在回复的消息被推到屏幕顶部之外……
因为ConversationView位于ScrollViewer内,可以滚动以将消息推到屏幕更下方,但这将需要一个负的滚动偏移量,这是不可能的!
为了绕过这个问题,可以添加一个元素,用于在需要时将顶部消息向下“推”。以下标记添加了一个Rectangle,它位于ConversationView上方,有两个Storyboard,用于展开/折叠Rectangle,允许在需要时将消息推下去:
<ScrollViewer x:Name="ConversationScrollViewer">
<StackPanel Orientation="Vertical" x:Name="ConversationContentContainer" VerticalAlignment="Top">
<Rectangle Width="100" Height="0" x:Name="PaddingRectangle">
<Rectangle.Resources>
<Storyboard x:Name="PaddingRectangleShowAnim">
<DoubleAnimation Storyboard.TargetName="PaddingRectangle" Storyboard.TargetProperty="(Height)" To="400" Duration="00:00:00.3" />
</Storyboard>
<Storyboard x:Name="PaddingRectangleHideAnim">
<DoubleAnimation Storyboard.TargetName="PaddingRectangle" Storyboard.TargetProperty="(Height)" To="0" Duration="00:00:00.3" />
</Storyboard>
</Rectangle.Resources>
</Rectangle>
<local:ConversationView x:Name="conversationView" />
</StackPanel>
</ScrollViewer>
可以检测输入TextBlock何时获得焦点,以确定何时显示键盘。使用之前在博客中讨论的Show / Hide扩展方法,可以触发动画,使这个填充矩形在键盘显示或隐藏时增长或收缩:
private void TextInput_GotFocus(object sender, RoutedEventArgs e)
{
PaddingRectangle.Show();
ApplicationBar.IsVisible = true;
}
private void TextInput_LostFocus(object sender, RoutedEventArgs e)
{
PaddingRectangle.Hide();
ApplicationBar.IsVisible = false;
}
如果填充矩形使其可见,可以看到它如何推动内容如下:
下一步是确保ConversationView始终滚动,以便最新的消息可见。ScrollViewer有一个ScrollToVerticalOffset方法,可以用来程序性地滚动内容,但由于这不是一个依赖属性,所以不能通过Storyboard进行动画处理。
在这里,使用了为Windows Phone 7跳转列表控件采用的相同技巧,其中使用一个私有依赖属性,在它的更改处理程序中设置滚动偏移值,作为滚动Storyboard的目标:
private DependencyProperty VerticalOffsetProperty = DependencyProperty.Register("VerticalOffset", typeof(double), typeof(MainPage), new PropertyMetadata(0.0, OnVerticalOffsetChanged));
private static void OnVerticalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MainPage app = d as MainPage;
app.OnVerticalOffsetChanged(e);
}
private void OnVerticalOffsetChanged(DependencyPropertyChangedEventArgs e)
{
ConversationScrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
使用这个依赖属性,可以创建一个简单的Storyboard和DoubleAnimation来滚动以显示最新的消息:
private void ScrollConvesationToEnd()
{
// 从当前位置开始
scrollViewerScrollToEndAnim.From = ConversationScrollViewer.VerticalOffset;
// 将滚动位置设置为包含内容的高度
scrollViewerScrollToEndAnim.To = ConversationContentContainer.ActualHeight;
// 开始!
scrollViewerStoryboard.Begin();
}
就这样,一个功能完备的聊天应用程序就完成了。享受吧!