How to handle Freezable in custom MarkupExtension?

I have a working custom markup extension that retrieves information from a DataContext specific way (not relevant for this question).

Everything is fine until I use this markup extension in elements that are not part of the visual or logical tree. In my specific example, in an InputBindings element. In this scenario, instead of getting FrameworkElement as DependencyObject I get a Freezable ( KeyBinding ).

How can I access a DataContext through code?

My XAML code:

 <UserControl.InputBindings> <KeyBinding Key="CapsLock" Command="{wtc:CommandBinding {x:Static b:Commands.OpenTimeLine}}" /> </UserControl.InputBindings> 

Code in my custom markup extension where I usually retrieve my DataContext :

 protected override object ProvideValue( DependencyObject dependencyObject, DependencyProperty dependencyProperty ) { if ( dependencyObject is Freezable ) { // TODO: How to handle freezable? } _frameworkElement = dependencyObject as FrameworkElement; if ( _frameworkElement == null ) { throw new InvalidImplementationException( "The DataContextBinding may only be used on framework elements." ); } if ( !_dataContextChangedHooked ) { _frameworkElement.DataContextChanged += DataContextChanged; _dataContextChangedHooked = true; } return ProvideValue( _frameworkElement.DataContext ); } 

All source code is also online. I have a fairly extensive class hierarchy for markup extensions.

AbstractMarkupExtensionAbstractDependencyPropertyBindingExtensionAbstractDataContextBindingExtensionCommandBindingExtension

+4
source share
2 answers

One solution is surprisingly easy. Assuming that the DataContext you are looking for matches the DataContext your root object, you can simply use IRootObjectProvider . This provider is available through IServiceProvider , which is passed as an argument to ProvideValue .

 var rootProvider = (IRootObjectProvider)ServiceProvider .GetService( typeof( IRootObjectProvider ) ); _frameworkElement = rootProvider.RootObject as FrameworkElement; 

There may be more complex scenarios in which you need to go through the tree (through LogicalChildren ) to find the desired DataContext .

+2
source

Here would be an unpleasant way to reflect:

 var context = (FrameworkElement)typeof(DependencyObject) .GetProperty("InheritanceContext", BindingFlags.NonPublic | BindingFlags.Instance) .GetValue(dependencyObject, null); var datacontext = context.DataContext; 

(Casting to FrameworkElement unsafe, InheritanceContext also of type DependencyObject , InheritanceContext usually an object that declares a property that uses Freezable , if it is not FrameworkElement you may need to re-register)

0
source

Source: https://habr.com/ru/post/1393392/


All Articles