Home > OS >  Get Element Root's ViewModel Context in WINUI3
Get Element Root's ViewModel Context in WINUI3

Time:04-15

I have a page which contains a ListView x:bound to an object in my ViewModel. This object contains a list of objects (timestamps) that contains a list of Subjects that contains a list of another objects. I'm presenting the data in 2 list views, one inside another.

<ListView
  x:Name="primaryList" // for exemplification purposes
  ItemsSource="{x:Bind ViewModel.VideoProject.TimeStamps, Mode=OneWay}"
  ItemClick='{x:Bind ViewModel.ListViewTimeStamps_ItemClick, Mode=OneWay}'>

The ListView contains a DataTemplate for another ListView

<ListView.ItemTemplate>
  <DataTemplate>
  <StackPanel Spacing="5">
  <TextBlock Text="{Binding Id}"
  FontSize="15"
  HorizontalAlignment="Left"
  FontWeight="Bold" />
  
  <ListView ItemsSource="{Binding Subjects}"
  x:Name="secondaryList"
  SelectionMode="Multiple">
  <ListView.ItemTemplate>
....

And the second ListView is followed by another same structure.

My goal is to bind the second ListView ItemClickEvent to ListViewTimeStamps_ItemClick method inside my ViewModel, because I need the data contained in the object that secondaryListView holds (Subject). I could try to set the Data Template Context to the ViewModel but it would break the Subject bind.

I found a ton of questions about this topic but differently from WPF there's not AncestorType to catch the up tree reference.

Obs: My project is based on the Template Model which creates the XAML .cs with the ViewModel as a Property. I also haven't set the DataContext on the XAML page because I can x:bind normally my view model to the page elements without explicit set.

Is there a way to accomplish without using Attached Properties? Thank you.

CodePudding user response:

Since there is no support for setting the AncestorType property of a RelativeSource in WinUI, there is no way to accomplish this in pure XAML without writing some code.

You could implement an attached bevaiour as suggested and exemplified here:

public static class AncestorSource
{
    public static readonly DependencyProperty AncestorTypeProperty =
        DependencyProperty.RegisterAttached(
            "AncestorType",
            typeof(Type),
            typeof(AncestorSource),
            new PropertyMetadata(default(Type), OnAncestorTypeChanged)
    );

    public static void SetAncestorType(FrameworkElement element, Type value) =>
        element.SetValue(AncestorTypeProperty, value);

    public static Type GetAncestorType(FrameworkElement element) =>
        (Type)element.GetValue(AncestorTypeProperty);

    private static void OnAncestorTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement target = (FrameworkElement)d;
        if (target.IsLoaded)
            SetDataContext(target);
        else
            target.Loaded  = OnTargetLoaded;
    }

    private static void OnTargetLoaded(object sender, RoutedEventArgs e)
    {
        FrameworkElement target = (FrameworkElement)sender;
        target.Loaded -= OnTargetLoaded;
        SetDataContext(target);
    }

    private static void SetDataContext(FrameworkElement target)
    {
        Type ancestorType = GetAncestorType(target);
        if (ancestorType != null)
            target.DataContext = FindParent(target, ancestorType);
    }

    private static object FindParent(DependencyObject dependencyObject, Type ancestorType)
    {
        DependencyObject parent = VisualTreeHelper.GetParent(dependencyObject);
        if (parent == null)
            return null;

        if (ancestorType.IsAssignableFrom(parent.GetType()))
            return parent;

        return FindParent(parent, ancestorType);
    }
}

So no replacement so far for AncestorType?

No. Not in XAML.

  • Related