Отладка WPF, привязка AvalonEdit к свойству документа

весь день сижу и пытаюсь понять, почему не работает привязка к свойству AvalonEdits Document. AvalonEdit - это продвинутый текстовый редактор WPF - часть проекта SharpDevelop (он будет использоваться в SharpDevelop v4 Mirador).

Итак, когда я создал простой проект - один TextEditor (настоящее имя AvalonEdits в библиотеке) и создал простой класс с одним свойством - Document, который возвращает фиктивный объект с некоторым статическим текстом, привязка работает отлично.

Однако в реальном решении я привязываю коллекцию объектов SomeEditor к TabControl. TabControl имеет DataTemplate для SomeEditor и объект TextEditor.

<TabControl Grid.Column="1" x:Name="tabControlFiles" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
 <TabControl.Resources>
  <DataTemplate DataType="{x:Type m:SomeEditor}">
   <a:TextEditor 
   Document="{Binding Path=Document, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource NoopConverter}, IsAsync=True}" 
   x:Name="avalonEdit"></a:TextEditor> 
  </DataTemplate>  
 </TabControl.Resources>

 <TabControl.ItemContainerStyle>
  <Style BasedOn="{StaticResource TabItemStyle}" TargetType="{x:Type TabItem}">
   <Setter Property="IsSelected" Value="{Binding IsSelected}"></Setter>
  </Style>
 </TabControl.ItemContainerStyle>
</TabControl>

Это не работает. Что я исследовал до сих пор:

  • DataContext TextEditor установлен на соответствующий экземпляр SomeEditor
  • Свойство TextEditors Document установлено в некоторый другой экземпляр, чем свойство SomeEditor.Document
  • когда я устанавливаю точку останова на безоперационный конвертер, который прикреплен к этой привязке, он показывает мне правильное значение для документа (конвертер используется!)
  • Я также покопался в VisualTree, чтобы получить ссылку на TextEditor, и вызвал GetBindingExpression (TextEditor.DocumentProperty), и это ничего не вернуло

  • WPF выдает следующую информацию:

    Информация о System.Windows.Data: 10: невозможно получить значение с помощью привязки, и действительного резервного значения не существует; используя вместо этого значение по умолчанию. BindingExpression: Путь = Документ; DataItem = 'SomeEditor' (HashCode = 26280264); целевой элемент - TextEditor (Name = 'avalonEdit'); целевым свойством является Document (тип TextDocument)

  • Экземпляр SomeEditor, к которому привязан, уже имеет созданную и кэшированную копию Document до того, как произойдет привязка. Геттер никогда не вызывается.

Кто-нибудь может сказать мне, что может быть не так? Почему не задано BindingExpression? Почему получатель собственности никогда не вызывается?

// редактировать: новые тесты и новые результаты

Я прочитал еще немного и установил привязку в коде позади. Когда я это делаю, это работает. Почему установка этого в XAML не работает, а выполнение того же самого в коде работает?

// edit2: код также не работает при вызове сразу после добавления объекта в наблюдаемую коллекцию, которая используется в качестве источника данных более высокого уровня (это происходит вскоре после того, как должна сработать привязка xaml). Это заставляет меня думать, что это проблема времени. Кто-нибудь может что-то сказать по этому поводу?

// edit3: Код привязки:

private List<T> GetObjectOfTypeInVisualTree<T>(DependencyObject dpob) where T : DependencyObject
{
    int count = VisualTreeHelper.GetChildrenCount(dpob);
    List<T> returnlist = new List<T>();

    for (int i = 0; i < count; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(dpob, i);
        T childAsT = child as T;
        if (childAsT != null)
        {
            returnlist.Add(childAsT);
        }
        List<T> lst = GetObjectOfTypeInVisualTree<T>(child);
        if (lst != null)
        {
            returnlist.AddRange(lst);
        }
    }
    if (returnlist.Count > 0)
    {
        return returnlist;
    }
    return null;
}

private void RebindMenuItem_Click(object sender, RoutedEventArgs e)
{
    foreach (XHTMLStudioPrototypeFileEditor ed in CurrentProject.OpenedFiles)
    {

        List<ContentPresenter> cps = GetObjectOfTypeInVisualTree<ContentPresenter>(tabControlFiles);
        if (cps != null)
        {
            foreach (ContentPresenter cp in cps)
            {

                foreach (DataTemplate dt in tabControlFiles.Resources.Values)
                {
                    try
                    {
                        object o = dt.FindName("avalonEdit", cp);
                        TextEditor ted = (TextEditor)o;

                        bool isDataBound = BindingOperations.IsDataBound(ted, TextEditor.DocumentProperty);
                        if (!isDataBound)
                        {
                            BindingOperations.SetBinding(ted, TextEditor.DocumentProperty, new Binding("Document"));
                        }
                        Console.WriteLine(isDataBound);
                    }
                    catch (Exception)
                    {


                    }
                }
            }
        }
    }
}

person kubal5003    schedule 07.06.2010    source источник
comment
Не могли бы вы показать код, который вы использовали для программного создания привязки? Кроме того, что произойдет, если вы удалите IsAsync?   -  person JustABill    schedule 09.06.2010
comment
Если я удалю IsAsync, ничего не произойдет. Я пробовал каждую комбинацию, чтобы заставить ее работать.   -  person kubal5003    schedule 09.06.2010


Ответы (2)


Вот еще шесть вещей, которые стоит попробовать:

Тщательно найдите в своем приложении любое место, где вы напрямую назначаете свойству Document редактора TextEditor. Похоже, что какой-то код где-то выполняет avalonEdit.Document = ..., который перезаписывает привязку. Я бы поискал по всему вашему приложению строки целого слова с регистром совпадений «Document» и «DocumentProperty» и задумался бы над каждым случаем, чтобы посмотреть, можно ли установить это свойство.

Установите точку останова в TextEditor.OnDocumentChanged, чтобы проверить, правильно ли привязан документ, а затем снова изменить его. Проверьте стеки вызовов с отключенным параметром «Только мой код» и отображением внешнего кода.

Попробуйте установить точки останова в NoopConverter.Convert, SomeEditor.get_Document и TextEditor.OnDocumentChanged, чтобы выяснить точную последовательность операций. Также обратите внимание, когда отображается сообщение об ошибке привязки.

Временно измените конструктор TextEditor, чтобы сохранить ссылку на каждый экземпляр в общедоступном поле статического списка, чтобы вы могли определить, какие редакторы TextEditor когда-либо создавались, а затем напишите код, который просматривает их, отображая их GetHashCode() и их BindingOperations.GetBindingExpression(editor, DocumentProperty) результаты. Убедитесь, что вы удалили общедоступное статическое поле, когда закончите!

Выньте «Path =» из вашего XAML, который создает привязку, чтобы он лучше соответствовал версии C #. (Однажды у меня была проблема, когда XAML интерпретировал путь, отличный от конструктора Binding, из-за ITypeDescriptorContext, переданного в PropertyConverter.) Точный эквивалент опубликованного вами кода C # - Document="{Binding Document}".

Создайте настраиваемый прослушиватель трассировки и установите в нем точку останова, чтобы получить стек вызовов при возникновении ошибки привязки, выполните поиск кадров стека, чтобы найти задействованные объекты и дать им идентификаторы объектов отладчика (щелкните правой кнопкой мыши, Создать идентификатор объекта), затем исследуйте фактические значения свойств, чтобы убедиться, что они соответствуют ожиданиям.

Наслаждаться!

person Ray Burns    schedule 15.06.2010
comment
Я кратко отвечу по пунктам. 1) Готово, результатов нет. 2) Сделано в первую очередь. 3) Каждый раз разные результаты - возможно, разные потоки выполняют привязку, а все остальное. 4) и 5) Нет проблем с созданием TextEditors. Я это уже проверил. Я всегда получаю новый экземпляр и знаю, что код C # был в порядке - он работал. А теперь окончательные результаты: после нескольких чисток и сборок он внезапно заработал. Я не знаю, в чем причина, но все же предполагаю, что это безопасность потоков и время. Я полагаю, что не весь этот код является потокобезопасным, и именно поэтому он вёл себя так странно. - person kubal5003; 16.06.2010
comment
Через несколько дней я загрузил AvalonDock, и это потребовало его переписать по-другому - теперь у меня есть прямой доступ к экземпляру AvalonEdit, и я могу делать все, что захочу. Это работает. Большое спасибо за ответ. Это было очень тщательно, и вы представили ценные методы отладки. Ваша награда. - person kubal5003; 16.06.2010

Просто наблюдение: у меня была такая же проблема, и я просмотрел исходный код AvalonEdit; похоже, проблема в том, что конструктор TextEditor перезаписывает свойство Document (создает экземпляр нового TextDocument); если вы закомментируете это, привязки работают; однако, если у вас нет привязки, вам нужно будет внести дополнительные изменения. Я постараюсь обсудить это с авторами и, может быть, предложу патч.

person Alex Paven    schedule 31.08.2010