简体   繁体   English

UWP与uwp中的FindAncestor等效

[英]UWP equivalent function to FindAncestor in uwp

I have a list of orders and when the order status is Cancelled , I want to blink the text. 我有一个订单列表,当订单状态为Cancelled时 ,我想要闪烁文本。 So far, my code works. 到目前为止,我的代码工作。 However, sometimes it will throws exception: 但是, 有时会抛出异常:

WinRT information: Cannot resolve TargetName lblOrderStatus WinRT信息:无法解析TargetName lblOrderStatus

For some reason lblOrderStatus can be found. 由于某种原因,可以找到lblOrderStatus So, I want to use "FindAncestor", but FindAncestor doesn't exists in UWP. 所以,我想使用“FindAncestor”,但UWP中不存在FindAncestor。 Is there any equivalent function to FindAncestor in uwp? 在uwp中是否有与FindAncestor等效的功能?

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>

Considering all the solutions I've seen, I feel that using ElementName binding is the simplest workaround to UWP not having a RelativeSource AncestorType binding option. 考虑到我见过的所有解决方案,我觉得使用ElementName绑定是UWP最简单的解决方法,没有RelativeSource AncestorType绑定选项。

Assuming you've got a Page with its DataContext set to a viewmodel with a command MyCommand , and you want each item in your list to execute it when its button is clicked: 假设你有一个将其DataContext设置为带有命令MyCommand的viewmodel的Page ,并且你希望列表中的每个项目在单击其按钮时执行它:

<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 can't extract the DataTemplate out as a resource to use it on multiple screens (or even dialog boxes); 我对此解决方案的初始问题是,您无法将DataTemplate作为资源提取出来,以便在多个屏幕(甚至是对话框)上使用它; thisPage might not exist in each of those places, or it might not be appropriate to name the root element "thisPage". thisPage可能不存在于每个位置,或者可能不适合将根元素命名为“thisPage”。

But if you use a convention where you include a token UI element in every screen that uses that DataTemplate, and refer to it by a consistent name, it will work. 但是如果你使用一个约定,你在每个使用该DataTemplate的屏幕中包含一个令牌UI元素,并通过一致的名称引用它,它将起作用。 By default this element's DataContext will be your ViewModel (assuming your root element does too) 默认情况下,此元素的DataContext将是您的ViewModel(假设您的根元素也是如此)

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

...then in your standalone resources XAML file you can write your DataTemplate like this: ...然后在您的独立资源XAML文件中,您可以像这样编写DataTemplate:

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

Then, on every page/screen/dialog that you use that template resource, just drop in a copy of that Rectangle (or whatever) and everything will bind correctly at run-time 然后,在您使用该模板资源的每个页面/屏幕/对话框上,只需放入该Rectangle(或其他)的副本,一切都将在运行时正确绑定

This is clearly a hack solution, but after thinking about it some more, it doesn't feel like any more of a hack than using WPF's AncestorType in the first place (having to ensure that your ancestor type is always consistent in all the places you use your DataTemplate). 这显然是一个黑客攻击解决方案,但在考虑了它之后,它首先感觉不再像使用WPF的AncestorType那样的黑客攻击(必须确保你的祖先类型在你所有的地方总是一致的使用您的DataTemplate)。

I'm converting an app from WPF to UWP and found this thread. 我正在将一个应用程序从WPF转换为UWP并找到了这个帖子。 It seems there are no good solutions on the web, so here is my attempt to 'solve' this problem via workaround. 网络上似乎没有好的解决方案,所以这是我尝试通过解决方法“解决”这个问题。

NOTE: The following is UNTESTED in UWP (but works in WPF) as I'm part way through a large non-compiling port, but theoretically it should work... 注意: 以下是UWP UNTESTED(但在WPF作品)因为我通过大量非编译口部分的方式,但理论上它应该工作...

1 Create a RelativeSourceBinding Attached Property 1创建RelativeSourceBinding附加属性

This class has two properties: AncestorType and Ancestor. 该类有两个属性:AncestorType和Ancestor。 When the AncestorType changes, we subscribe to FrameworkElement.Loaded (to handle parent changes) and find the visual parent of type and assign to the Ancestor attached property. 当AncestorType更改时,我们订阅FrameworkElement.Loaded(以处理父更改)并找到类型的可视父级并分配给Ancestor附加属性。

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 FindVisualParent是一个定义为的扩展方法

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 2在XAML中应用RelativeSourceBinding属性

some BEFORE xaml in WPF would look like this WPF中的一些前缀xaml看起来像这样

<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 和之后的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>

It's a bit messy but in the case where you only have one RelativeSource FindAncestor type to find, it should work. 它有点乱,但在你只有一个RelativeSource FindAncestor类型要查找的情况下,它应该有效。

In XAML You can try using RelativeSource, it provides a means to specify the source of a binding in terms of a relative relationship in the run-time object graph. 在XAML中您可以尝试使用RelativeSource,它提供了一种根据运行时对象图中的相对关系指定绑定源的方法。 For example using TemplatedParent : 例如,使用TemplatedParent

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

or 要么

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

In code you try using the VisualTreeHelper.GetParent method. 在代码中,您尝试使用VisualTreeHelper.GetParent方法。 https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.visualtreehelper.getparent 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 checks for an element's 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 http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM