locked
ListBox крешится при скроле с кастомным ItemTemplate RRS feed

  • Общие обсуждения

  • Всем привет. Была следующая задача: В листбоксе отобразить форматируемый текст с поддержкой тапа по ссылкам.

    Вот XAML. Events представляет из себя List из Paragraph. Соответсвенно каждому EventControl биндится Paragraph. ок.

                        <ListBox Grid.Row="1" DataContext="{Binding Events}" ItemsSource="{Binding}">
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <local:EventControl FontSize="24" Margin="8, 2, 0, 2" Paragraph="{Binding}" LinkTapped="OnLinkTapped" />
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>

    Вот EventControl. У него есть event LinkTapped. Стреляет во время тапа по ссылке внутри Paragraph.

    public class EventControl : UserControl { public delegate void LinkTappedEventHandler(object sender, Uri link); public event LinkTappedEventHandler LinkTapped; public const string ParagraphPropertyName = "Paragraph"; public static readonly DependencyProperty ParagraphProperty = DependencyProperty.Register(ParagraphPropertyName, typeof(Paragraph), typeof(EventControl), new PropertyMetadata( new PropertyChangedCallback (ParagraphPropertyChanged))); public List<Paragraph> Paragraph { get { return (List<Paragraph>)GetValue(ParagraphProperty); } set { SetValue(ParagraphProperty, value); } } private static void ParagraphPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { Paragraph par = dependencyPropertyChangedEventArgs.NewValue as Paragraph; RichTextBox rcb = new RichTextBox();

    /* rcb.Tap += (s, a) => { var richTB = s as RichTextBox; var textPointer = richTB.GetPositionFromPoint(a.GetPosition(richTB)); var element = textPointer.Parent as TextElement; while (element != null && !(element is Hyperlinker)) { if (element.ContentStart != null && element != element.ElementStart.Parent) { element = element.ElementStart.Parent as TextElement; } else { element = null; } } if (element == null) return; if (((rcb.Parent as StackPanel).Parent as EventControl).LinkTapped != null) { ((rcb.Parent as StackPanel).Parent as EventControl).LinkTapped((rcb.Parent as StackPanel).Parent, (element as Hyperlinker).NavigateUri); } }; */ rcb.Blocks.Add(par);

    (dependencyObject as EventControl).Content = rcb; } }

    Hyperlinker представляет из себя Span с возможносью форматирования текста + собственно Юри.

    public class Hyperlinker : Span
        {
            public const string NavigateUriPropertyName = "NavigateUri";
    
            public static readonly DependencyProperty NavigateUriProperty =
               DependencyProperty.Register(NavigateUriPropertyName,
                                           typeof(Uri),
                                           typeof(Hyperlinker),
                                           new PropertyMetadata(
                                               new PropertyChangedCallback
                                                   (NavigateUriPropertyChanged)));
            public Uri NavigateUri
            {
                get { return (Uri)GetValue(NavigateUriProperty); }
                set { SetValue(NavigateUriProperty, value); }
            }
    
            private static void NavigateUriPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
            {
                return;
            }
        }

    Собрал, запустил.. Все работает. Радуюсь.. Потом когда элементов стало больше, то при скролле приложение начинает сразу падать. Думаю проблема как то связана с виртуализацией( Что теперь делать - ума не приложу. 

    После первоначально загрузки поставил бряку в ParagraphPropertyChanged. Функция нормально отрабатывает, а после нее приложение вылетает с исключением [System.ArgumentException] = {"The parameter is incorrect. "}. Где оно происходит, не пончтно.. Что я делаю не так? 

    Если что, воркэраундится отключением виртуализации:

    <ListBox Grid.Row="1" DataContext="{Binding Events}" ItemsSource="{Binding}">
                            <ListBox.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel />
                                </ItemsPanelTemplate>
                            </ListBox.ItemsPanel>
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <local:EventControl FontSize="24" Margin="8, 2, 0, 2" Paragraph="{Binding}" LinkTapped="OnLinkTapped" />
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>

    либо

    <ListBox Grid.Row="1" DataContext="{Binding Events}" ItemsSource="{Binding}" VirtualizingStackPanel.IsVirtualizing="False">
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <local:EventControl FontSize="24" Margin="8, 2, 0, 2" Paragraph="{Binding}" LinkTapped="OnLinkTapped" />
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>

    Второй способ почему то не работает и вызывает креш с исключением Set property 'System.Windows.Controls.VirtualizingStackPanel.IsVirtualizing' threw an exception. 

    Это спасло от креша при скроле, однако креш все равно происходит. Теперь при повторном открытии страницы. Когда Events снова биндится. Решается принудительно очисткой Events, но ноги отттуда же растут. Не знаю что делать :(



    25 декабря 2014 г. 10:05