Silverlight Chat WrapPanel Crash / Ошибка - PullRequest
0 голосов
/ 30 марта 2010

Мне дали задание создать простое окно чата Silverlight для двух человек. Мой контроль должен соответствовать следующим требованиям

  1. прокрутка
  2. Текст должен быть перенесен, если он слишком длинный
  3. Когда добавляется новый элемент / сообщение, он должен прокручивать этот элемент в поле зрения

Теперь я успешно создал пользовательский контроль для удовлетворения этих требований, но я столкнулся с возможной ошибкой / сбоем, которую не могу исправить. Я ищу исправление ошибки или другой подход к созданию прокручиваемого элемента управления чатом.

Вот код, который я использовал. Начнем с моего 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. По большей части это стабильно.

1 Ответ

0 голосов
/ 30 марта 2010

Я нашел альтернативное решение, использующее ScrollViewer и ItemsControl вместо элемента управления ListBox. По большей части это стабильно.

Вот XAML, который я сейчас использую.

<ScrollViewer x:Name="lbChatHistoryScroller">
                    <ItemsControl x:Name="lbChatHistory" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                        <ItemsControl.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="1750" Content="{Binding Message}">
                                    </ContentPresenter>
                                </Grid>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </ScrollViewer>
...