Мне дали задание создать простое окно чата Silverlight для двух человек. Мой контроль должен соответствовать следующим требованиям
- прокрутка
- Текст должен быть перенесен, если он слишком длинный
- Когда добавляется новый элемент / сообщение, он должен прокручивать этот элемент в поле зрения
Теперь я успешно создал пользовательский контроль для удовлетворения этих требований, но я столкнулся с возможной ошибкой / сбоем, которую не могу исправить. Я ищу исправление ошибки или другой подход к созданию прокручиваемого элемента управления чатом.
Вот код, который я использовал. Начнем с моего XAML для окна чата
<ListBox x:Name="lbChatHistory" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Beige">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock x:Name="lblPlayer" Foreground="{Binding ForeColor}" Text="{Binding Player}" Grid.Column="0"></TextBlock>
<ContentPresenter Grid.Column="1" Width="200" Content="{Binding Message}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Идея состоит в том, чтобы добавить новый элемент в список. Элемент (как изложено в XAML) представляет собой простую сетку из 2 столбцов. Один столбец для имени пользователя и один столбец для сообщения.
Теперь "элементы", которые я добавляю в ListBox, являются пользовательским классом. У него есть три свойства (Player, ForeColor и Message), для которых я использую привязку в своем XAML
Player - строка текущего пользователя для отображения.
ForeColor - это просто предпочтение цвета переднего плана. Это помогает различать разницу между сообщениями.
Сообщение - это WrapPanel . Я программно разбиваю предоставленную строку на пустое пространство для каждого слова . Затем для каждого слова я добавляю новый элемент TextBlock в WrapPanel
Вот пользовательский класс.
public class ChatMessage :DependencyObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public static DependencyProperty PlayerProperty = DependencyProperty.Register( "Player", typeof( string ), typeof( ChatMessage ),
new PropertyMetadata(
new PropertyChangedCallback( OnPlayerPropertyChanged ) ) );
public static DependencyProperty MessageProperty = DependencyProperty.Register( "Message", typeof( WrapPanel ), typeof( ChatMessage ),
new PropertyMetadata(
new PropertyChangedCallback( OnMessagePropertyChanged ) ) );
public static DependencyProperty ForeColorProperty = DependencyProperty.Register( "ForeColor", typeof( SolidColorBrush ), typeof( ChatMessage ),
new PropertyMetadata(
new PropertyChangedCallback( OnForeColorPropertyChanged ) ) );
private static void OnForeColorPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
ChatMessage c = d as ChatMessage;
c.ForeColor = ( SolidColorBrush ) e.NewValue;
}
public ChatMessage()
{
Message = new WrapPanel();
ForeColor = new SolidColorBrush( Colors.White );
}
private static void OnMessagePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
ChatMessage c = d as ChatMessage;
c.Message = ( WrapPanel ) e.NewValue;
}
private static void OnPlayerPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
ChatMessage c = d as ChatMessage;
c.Player = e.NewValue.ToString();
}
public SolidColorBrush ForeColor
{
get { return ( SolidColorBrush ) GetValue( ForeColorProperty ); }
set
{
SetValue( ForeColorProperty, value );
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs( "ForeColor" ));
}
}
public string Player
{
get { return ( string ) GetValue( PlayerProperty ); }
set
{
SetValue( PlayerProperty, value );
if ( PropertyChanged != null )
PropertyChanged( this, new PropertyChangedEventArgs( "Player" ) );
}
}
public WrapPanel Message
{
get { return ( WrapPanel ) GetValue( MessageProperty ); }
set
{
SetValue( MessageProperty, value );
if ( PropertyChanged != null )
PropertyChanged( this, new PropertyChangedEventArgs( "Message" ) );
}
}
}
Наконец, я добавляю свои предметы в список. Вот простой метод. Он принимает вышеупомянутый класс ChatMessage в качестве параметра
public void AddChatItem( ChatMessage msg )
{
lbChatHistory.Items.Add( msg );
lbChatHistory.ScrollIntoView( msg );
}
Теперь я проверил это, и все это работает. Проблема в том, что я использую полосу прокрутки. Вы можете прокрутить вниз, используя боковую полосу прокрутки или клавиши со стрелками, но при прокрутке вверх Silverlight вылетает. FireBug возвращает ManagedRuntimeError # 4004 с XamlParseException .
Я так близок к тому, чтобы иметь эту контрольную работу, я могу ее попробовать! Любые мысли о том, что я должен сделать или изменить? Есть ли лучший подход, чем тот, который я выбрал?
Заранее спасибо.
UPDATE
Я нашел альтернативное решение с использованием ScrollViewer и ItemsControl вместо элемента управления ListBox. По большей части это стабильно.