OnEnabledChanged вызывается раньше
визуальное дерево завершено, и, таким образом,
не находит ScrollViewer
Используйте Dispatcher.BeginInvoke , чтобы поставить в очередь остальную часть работы, которая должна выполняться асинхронно, после построения визуального дерева. Вам также необходимо вызвать ApplyTemplate , чтобы убедиться, что шаблон создан:
d.Dispatcher.BeginInvoke(new Action(() =>
{
((FrameworkElement)d).ApplyTemplate();
d.SetValue(ScrollViewerProperty, FindScrollViewer(d));
}));
Обратите внимание, что вам не нужно проверять, отличается ли новое значение от старого. Каркас обрабатывает это при настройке свойств зависимостей.
Вы также можете использовать FrameworkTemplate.FindName , чтобы получить ScrollViewer из FlowDocumentScrollViewer. FlowDocumentScrollViewer имеет именованную часть шаблона типа ScrollViewer с именем PART_ContentHost, где он фактически будет размещать контент. Это может быть более точным, если зритель повторно шаблонизирован и имеет более одного ScrollViewer в качестве дочернего элемента.
var control = d as Control;
if (control != null)
{
control.Dispatcher.BeginInvoke(new Action(() =>
{
control.ApplyTemplate();
control.SetValue(ScrollViewerProperty,
control.Template.FindName("PART_ContentHost", control)
as ScrollViewer);
}));
}
Я не знаю, как прикрепить к
DependencyProperty, содержащий
FlowDocument. Мой план состоял в том, чтобы использовать это
измененное событие для инициализации
Свойство ManagedRange. (Вручную
срабатывает в первый раз, если
необходимо.)
Нет встроенного в каркас способа получения уведомления об изменении свойства из свойства произвольной зависимости. Однако вы можете создать свой собственный DependencyProperty и просто привязать его к тому, который хотите посмотреть. См. Уведомление об изменении свойств зависимостей для получения дополнительной информации.
Создать свойство зависимости:
private static readonly DependencyProperty InternalDocumentProperty =
DependencyProperty.RegisterAttached(
"InternalDocument",
typeof(FlowDocument),
typeof(YourType),
new PropertyMetadata(OnFlowDocumentChanged));
И замените ваш код отражения в OnEnabledChanged на просто:
BindingOperations.SetBinding(d, InternalDocumentProperty,
new Binding("Document") { Source = d });
При изменении свойства Document FlowDocumentScrollViewer привязка обновит InternalDocument, и будет вызван OnFlowDocumentChanged.
Я не знаю, как добраться до
Свойство ScrollViewer изнутри
Метод range_Changed, так как он не
иметь объект DependencyObject.
Свойство отправителя будет TextRange, поэтому вы можете использовать ((TextRange)sender).Start.Parent
, чтобы получить объект DependencyObject, а затем пройтись по визуальному дереву.
Более простым способом было бы использовать лямбда-выражение для захвата переменной d
в OnMonitoredRangeChanged, выполнив что-то вроде этого:
range.Changed += (sender, args) => range_Changed(d);
А затем создается перегрузка range_Changed, которая принимает объект DependencyObject. Тем не менее, это будет немного сложнее удалить обработчик, когда вы закончите.
Кроме того, хотя в ответе Обнаружение изменения и прокрутки FlowDocument говорится, что TextRange.Changed будет работать, я не видел, чтобы он действительно срабатывал при тестировании. Если это не работает для вас, и вы хотите использовать отражение, существует событие TextContainer.Changed, которое, похоже, срабатывает:
var container = doc.GetType().GetProperty("TextContainer",
BindingFlags.Instance | BindingFlags.NonPublic).GetValue(doc, null);
var changedEvent = container.GetType().GetEvent("Changed",
BindingFlags.Instance | BindingFlags.NonPublic);
EventHandler handler = range_Changed;
var typedHandler = Delegate.CreateDelegate(changedEvent.EventHandlerType,
handler.Target, handler.Method);
changedEvent.GetAddMethod(true).Invoke(container, new object[] { typedHandler });
Параметр sender
будет TextContainer, и вы можете снова использовать отражение, чтобы вернуться к FlowDocument:
var document = sender.GetType().GetProperty("Parent",
BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(sender, null) as FlowDocument;
var viewer = document.Parent;