I am converting an application from WPF to UWP and found this thread. There seems to be no good solution on the Internet, so I'm trying to βsolveβ this problem with a workaround.
NOTE. The following is UNTESTED in UWP (but works in WPF), as I partially participate in a large non-compiling port, but theoretically it should work ...
1 Create a binding property RelativeSourceBinding
This class has two properties: AncestorType and Ancestor. When AncestorType changes, we subscribe to FrameworkElement.Loaded (to handle the parent changes) and find the visual type parent and assign the attached Ancestor property.
public class RelativeSourceBinding { public static readonly DependencyProperty AncestorTypeProperty = DependencyProperty.RegisterAttached("AncestorType", typeof(Type), typeof(RelativeSourceBinding), new PropertyMetadata(default(Type), OnAncestorTypeChanged)); public static void SetAncestorType(DependencyObject element, Type value) { element.SetValue(AncestorTypeProperty, value); } public static Type GetAncestorType(DependencyObject element) { return (Type)element.GetValue(AncestorTypeProperty); } private static void OnAncestorTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((FrameworkElement)d).Loaded -= OnFrameworkElementLoaded; if (e.NewValue != null) { ((FrameworkElement)d).Loaded += OnFrameworkElementLoaded; OnFrameworkElementLoaded((FrameworkElement) d, null); } } private static void OnFrameworkElementLoaded(object sender, RoutedEventArgs e) { var ancestorType = GetAncestorType((FrameworkElement) sender); if (ancestorType != null) { var findAncestor = ((FrameworkElement) sender).FindVisualParent(ancestorType); RelativeSourceBinding.SetAncestor(((FrameworkElement)sender), findAncestor); } else { RelativeSourceBinding.SetAncestor(((FrameworkElement)sender), null); } } public static readonly DependencyProperty AncestorProperty = DependencyProperty.RegisterAttached("Ancestor", typeof(UIElement), typeof(RelativeSourceBinding), new PropertyMetadata(default(FrameworkElement))); public static void SetAncestor(DependencyObject element, UIElement value) { element.SetValue(AncestorProperty, value); } public static UIElement GetAncestor(DependencyObject element) { return (UIElement)element.GetValue(AncestorProperty); } }
Where FindVisualParent is an extension method defined as
public static UIElement FindVisualParent(this UIElement element, Type type) { UIElement parent = element; while (parent != null) { if (type.IsAssignableFrom(parent.GetType())) { return parent; } parent = VisualTreeHelper.GetParent(parent) as UIElement; } return null; }
2 Apply the RelativeSourceBinding property in XAML
some BEFORE XAML in WPF would look like this:
<Style x:Key="SomeStyle" TargetType="local:AClass"> <Style.Setters> <Setter Property="SomeProperty" Value="{Binding Foo, RelativeSource={RelativeSource AncestorType=local:AnotherClass}}" /> </Style.Setters> </Style>
and AFTER xaml
<Style x:Key="SomeStyle" TargetType="local:AClass"> <Style.Setters> <Setter Property="apc:RelativeSourceBinding.AncestorType" Value="local:AnotherClass"/> <Setter Property="Foreground" Value="{Binding Path=(apc:RelativeSourceBinding.Ancestor).Foo, RelativeSource={RelativeSource Self}}" /> </Style.Setters> </Style>
This is a bit messy, but in case you only have one type of FindAncestor RelativeSource, it should work.