UWP equivalent FindAncestor function in uwp

I have a list of orders, and when the order status is Canceled, I want to blink on the text. So far, my code is working. However, sometimes it throws an exception:

WinRT Information: Cannot resolve TargetName lblOrderStatus

For some reason lblOrderStatus . So, I want to use "FindAncestor", but FindAncestor does not exist in UWP. Is there an equivalent FindAncestor function in uwp?

Here is my code:

<ItemsControl x:Name="Orders" Grid.Row="1" Background="Transparent"> ... ... ... <ItemsControl.ItemTemplate> <DataTemplate> <Grid> ... ... ... <Viewbox Grid.Column="3" StretchDirection="DownOnly" HorizontalAlignment="Right"> <TextBlock x:Name="lblOrderStatus" Text="{Binding Path=OrderItemStatus, Mode=OneWay}" FontSize="18"> <TextBlock.Resources> <Storyboard x:Name="sbBlinking"> <DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.Opacity)" Storyboard.TargetName="lblOrderStatus" From="1" To="0" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="Forever" /> </Storyboard> </TextBlock.Resources> <interactive:Interaction.Behaviors> <core:DataTriggerBehavior Binding="{Binding OrderItemStatus, Converter={StaticResource EnumToStringConverter}}" ComparisonCondition="Equal" Value="Cancelled"> <media:ControlStoryboardAction Storyboard="{StaticResource sbBlinking}" /> </core:DataTriggerBehavior> </interactive:Interaction.Behaviors> </TextBlock> </Viewbox> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> 
+3
c # uwp win-universal-app windows-10-universal uwp-xaml
source share
3 answers

Given all the solutions I've seen, I believe that using the ElementName binding is the easiest workaround for UWP without the RelativeSource AncestorType binding parameter.

Assuming you have a Page with its DataContext set in the viewmodel using the MyCommand , and you want each item in your list to execute it when a button is clicked:

 <Page Name="thisPage"> ... <ListView ...> <ListView.ItemTemplate> <DataTemplate> <Button Command="{Binding ElementName=thisPage, Path=DataContext.MyCommand}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> </Page> 

My initial problem with this solution is that you cannot extract the DataTemplate as a resource to use on multiple screens (or even dialogs); thisPage may not exist in each of these places, or it may be inappropriate to name the root element "thisPage".

But if you use a convention in which you include a token user interface element on every screen that uses this DataTemplate, and refer to it by its serial name, it will work. By default, this DataContext will be your ViewModel (assuming your root element too)

 <Rectangle Name="VmDcHelper" Visibility="Collapsed"/> 

... then in your standalone XAML file resource you can write your DataTemplate as follows:

 <DataTemplate x:Key="MyDataTemplate"> <Button Command="{Binding ElementName=VmDcHelper, Path=DataContext.MyCommand}" /> </DataTemplate> 

Then on each page / screen / dialog in which you use this template resource, just run a copy of that rectangle (or something else) and everything will link correctly at runtime

This is clearly a hacking solution, but when you think about it, it no longer looks like a hack than using WPF AncestorType (to make sure that the type of your ancestor is always consistent in all the places where you use your DataTemplate).

+2
source share

In XAML, you can try using RelativeSource, it provides a means to specify the source of the binding in terms of relative relationship in the graph of runtime objects. For example, using TemplatedParent :

 Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Parent.ActualHeight} 

or

 <Binding RelativeSource="{RelativeSource TemplatedParent}" ></Binding> 

In code, you are trying to use the VisualTreeHelper.GetParent method. https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.visualtreehelper.getparent

something like the following, here is an example of a utility function

 internal static void FindChildren<T>(List<T> results, DependencyObject startNode) where T : DependencyObject { int count = VisualTreeHelper.GetChildrenCount(startNode); for (int i = 0; i < count; i++) { DependencyObject current = VisualTreeHelper.GetChild(startNode, i); if ((current.GetType()).Equals(typeof(T)) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T)))) { T asType = (T)current; results.Add(asType); } FindChildren<T>(results, current); } } 

The following example shows code that validates a parent

 ((StackPanel)LinePane.Parent).ActualWidth; 

Also, here is a good blog post showing this class in action. http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html

0
source share

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.

0
source share

All Articles