This blog post describes how to implement a conversation / messaging style application with Windows Phone 7. It covers how to style the speech bubbles and the scrolling of the conversation list view when the phone keyboard is shown.
A couple of weeks ago, I wrote a blog post which described the creation of a Windows Phone 7 ConversationView, a view which renders a list of messages so that they look like a conversation. In this blog post, I am going to extend the concept further, by adding an input text field allowing you to have a conversation with ELIZA, an A.I. chatterbot. This blog post will look at some of the tricky issues regarding scrolling the list of messages so that the most recent is always visible when responding.
You can see a video of this code in action by clicking on this link.
I hoped that I could find a decent C# ELIZA (a classic chatter application that takes on the role of a therapist) implementation on the internet, however, the only one I found was rather basic. If you know of any alternatives, please let me know!
In my previous blog post, I created a
UserControl which renders each
Message instance, where the template used to render the message is dependant on which side of the conversation it relates to. Refer to my previous blog post for implementation details.
The layout for my simple chat application uses an instance of the
ConversationView user control, with a text input located at the bottom of the screen:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid Grid.Row="1" contribControls:GridUtils.RowDefinitions=",," Margin="0,10,0,0">
<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"
Note the simplified
Grid markup from the WP7Contrib codeplex project, where the string
",Auto" is used in place of the more verbose
RowDefinition XAML elements.
WatermarkedTextBox is from an article by WindowsPhoneGeek (and it works like a charm – thanks!). A simple
Rectangle are added to the layout so that the input field looks like a speech bubble.
When the user clicks on the input
TextBlock, the phone keyboard will be revealed, allowing them to enter their message. This is where we stumble upon our first major problem!
When the phone keyboard is displayed, your application content is ‘pushed’ upwards to make space for the keyboard. Unfortunately, this results in the message which the user is replying to being pushed off the top of the screen …
ConversationView sits within a
ScrollViewer, we can scroll to push the message further down the screen, however, this would require a negative scroll offset, which isn’t possible!
To circumnavigate this issue, we can add an element which is used to ‘push’ the top message downwards so that it is located at the bottom of the
ScrollViewer. The following markup adds a
Rectangle which sits above the
ConversationView, with two
Storyboards that expand / collapse the
Rectangle allowing us to push the messages down when we need them:
<StackPanel Orientation="Vertical" x:Name="ConversationContentContainer" VerticalAlignment="Top">
<Rectangle Width="100" Height="0" x:Name="PaddingRectangle">
Storyboard.TargetProperty="(Height)" To="400" Duration="00:00:00.3"/>
Storyboard.TargetProperty="(Height)" To="0" Duration="00:00:00.3"/>
We can detect when the input
TextBlock receives focus to determine when the keyboard will be revealed. Using the Show / Hide extension methods I blogged about a while back, we can fire the animations which make this padding rectangle grow or shrink when the keyboard is shown or hidden:
private void TextInput_GotFocus(object sender, RoutedEventArgs e)
ApplicationBar.IsVisible = true;
private void TextInput_LostFocus(object sender, RoutedEventArgs e)
ApplicationBar.IsVisible = false;
If we fill the rectangle so that it is visible, you can see how it pushes down the content as shown below:
The next step is to ensure that the
ConversationView is always scrolled so that the most recent message is visible. The
ScrollViewer has a
ScrollToVerticalOffset method which can be used to programmatically scroll the content, however, because this is not a dependency property it cannot be animated via
Here I am using the same trick I employed for the Windows Phone 7 Jump List control, where a
private dependency property that sets the scroll offset value in its change handler is used as a target for the scrolling
private DependencyProperty VerticalOffsetProperty =
DependencyProperty.Register("VerticalOffset", typeof(double), typeof(MainPage),
new PropertyMetadata(0.0, OnVerticalOffsetChanged));
private static void OnVerticalOffsetChanged(DependencyObject d,
MainPage app = d as MainPage;
private void OnVerticalOffsetChanged(DependencyPropertyChangedEventArgs e)
Using this dependency property, we can create a simple
DoubleAnimation that scrolls to reveal the latest message:
private void ScrollConvesationToEnd()
scrollViewerScrollToEndAnim.From = ConversationScrollViewer.VerticalOffset;
scrollViewerScrollToEndAnim.To = ConversationContentContainer.ActualHeight;
And there you have it, a fully functional conversation application. Enjoy!
You can download the complete source code: WP7ConversationView.zip.