none
[UWP]Adaptative Triggers with custom attached properties in Windows 10 UWP

    Question

  • I am trying to set in the visual states a custom attached property I have tried several ways with the complete namespace, without, with the alias, etc. without success any idea?

    xmlns:p="using:Controls.Views.Properties"

    <RelativePanel x:Name="RootPanel" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups>

    <VisualStateGroup x:Name="VisualStateGroup">

    <VisualState x:Name="NarrowView">

    <VisualState.StateTriggers>

    <AdaptiveTrigger MinWindowWidth="0" />

    </VisualState.StateTriggers>

    <VisualState.Setters>

    //This is where the value is not achieved

    <Setter Target="Text.(p:RelativeSize.Width)" Value="0.5"/>

    Text is a control and p is the namespace

    It does not just fire any error, I can replace p: with j: and it doesn't crash, I do not know how to solve that.

    More details  here


    Juan Pablo G.C.

    Friday, August 7, 2015 12:49 PM

Answers

  • I have tested and It should work but <Setter Target="Text.(RelativeSize.Width)" Value="0.8" /> it is never called.

    I think there is an internal limitation that only works with the controls added in the windows namespace, and does not process and neither throws exception because avoids custom attached properties.

    The workaround I found is:

    1.- The complete example:

    <Page x:Name="Root"
    x:Class="Deletetb.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Deletetb"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <RelativePanel Background="Magenta">
     <VisualStateManager.VisualStateGroups>
      <VisualStateGroup x:Name="VisualStateGroup" CurrentStateChanged="VisualStateGroup_CurrentStateChanged">
       <VisualState x:Name="NarrowView">
        <VisualState.StateTriggers>
         <AdaptiveTrigger MinWindowWidth="0" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
         <Setter Target="Text.(RelativePanel.Below)" Value="Image" />
         <Setter Target="Content.(RelativePanel.Below)" Value="Text" />
         <Setter Target="Text.(RelativePanel.RightOf)" Value="{x:Null}" />
         <Setter Target="Text.(RelativeSize.Width)" Value="1.0"/>
         <Setter Target="Image.(RelativeSize.Width)" Value="1.0"/>
        </VisualState.Setters>
       </VisualState>
       <VisualState x:Name="WideView">
        <VisualState.StateTriggers>
         <AdaptiveTrigger MinWindowWidth="860" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
         <Setter Target="Text.(RelativePanel.Below)" Value="{x:Null}" />
         <Setter Target="Text.(RelativePanel.RightOf)" Value="Image" />
         <Setter Target="Content.(RelativePanel.Below)" Value="Image" />
         <Setter Target="Text.(RelativeSize.Width)" Value="0.5"/>
         <Setter Target="Image.(RelativeSize.Width)" Value="0.5"/>
        </VisualState.Setters>
       </VisualState>
      </VisualStateGroup>
     </VisualStateManager.VisualStateGroups>
     <Image x:Name="Image" Source="ms-appx:///Assets/StoreLogo.png"  local:RelativeSize.Container="{Binding ElementName=Root}" local:RelativeSize.Width="0.5" />
     <TextBlock x:Name="Text" RelativePanel.Below="Image" local:RelativeSize.Width="0.5"  TextWrapping="WrapWholeWords" Text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum." />
     <Border x:Name="Content" Background="Blue" RelativePanel.Below="Text" >
      <TextBlock Text="Other Content" HorizontalAlignment="Center" VerticalAlignment="Center" />
     </Border>

    </RelativePanel>
    </Page>

    2.- The RelativeSize custom attached properties:

    public class RelativeSize : DependencyObject
    {
     private static List<FrameworkElement> elements = new List<FrameworkElement>();
     private static FrameworkElement Container = null;
     private static bool containerready = false;
     public static void SetContainer(UIElement element, FrameworkElement value)
     {
      element.SetValue(ContainerProperty, value);
     }
     public static FrameworkElement GetContainer(UIElement element)
     {
      return (FrameworkElement)element.GetValue(ContainerProperty);
     }
     public static readonly DependencyProperty ContainerProperty =
      DependencyProperty.RegisterAttached("Container", typeof(FrameworkElement), typeof(RelativeSize), new PropertyMetadata(null, ContainerChanged));
     private static void ContainerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
      Container = (e.NewValue as FrameworkElement);
      Container.SizeChanged += (sc, ec) =>
      {
       foreach (var element in elements)
       {
        var rWidth = element.GetValue(RelativeSize.WidthProperty);
        if (rWidth != null)
        {
         element.Width = (double)rWidth * Container.ActualWidth;
        }
       }
      };
      containerready = true;
     }
     public static void SetWidth(UIElement element, double value)
     {
      element.SetValue(WidthProperty, value);
     }
     public static double GetWidth(UIElement element)
     {
      return (double)element.GetValue(WidthProperty);
     }
     public static readonly DependencyProperty WidthProperty =
       DependencyProperty.RegisterAttached("Width", typeof(double), typeof(RelativeSize), new PropertyMetadata(0.0, WidthChanged));
     private static async void WidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
      while (!containerready)
       await Task.Delay(60);

      var fe = d as FrameworkElement;
      if (fe != null)
      {
       if (!elements.Contains(fe))
        elements.Add(fe);
       fe.Width = (double)e.NewValue * Container.ActualWidth;
      }
     }
    }

    3.- the VisualStateGroup_CurrentStateChanged

    private void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
    {
     foreach (var sbase in e.NewState.Setters)
     {
      var setter = sbase as Setter;
      var spath = setter.Target.Path.Path;
      var element = setter.Target.Target as FrameworkElement;
      if (spath.Contains(nameof(RelativeSize)))
      {
       string property = spath.Split('.').Last().TrimEnd(')');
       var prop = typeof(RelativeSize).GetMethod($"Set{property}");

       prop.Invoke(null, new object[] { element, setter.Value });
      }
     }
    }

    As you can see in the e.NewState.Setters the setter with relativesize is in there, so the base event simply miss it and you need the event I wrote.


    Juan Pablo G.C.

    Tuesday, August 18, 2015 10:50 AM

All replies

  • Why not just ask about how to use this custom attach property on that link you provided.
    Monday, August 10, 2015 12:44 PM
  • Hello Juan Pablo,

    >> It does not just fire any error, I can replace p: with j: and it doesn't crash, I do not know how to solve that.

    Please have a try with below demo:

    The XAML:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup CurrentStateChanged="VisualStateGroup_CurrentStateChanged">
                    <VisualState x:Name="WideView">
                        <VisualState.StateTriggers>
                            <AdaptiveTrigger MinWindowWidth="860" />
                        </VisualState.StateTriggers>
                        <VisualState.Setters>
                            <Setter Target="myPanel.Orientation" Value="Vertical" />
                            <Setter Target="myPanel.Width" Value="380" />
                            <Setter Target="myTextBlock.Foreground" Value="Green" />
                            <Setter Target="Text.(RelativeSize.Width)" Value="0.8" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <StackPanel x:Name="myPanel" Orientation="Horizontal">
                <Image x:Name="Image" local:RelativeSize.Container="{Binding ElementName=Root}" local:RelativeSize.Width="0.4"  Source="ms-appx:///Assets/StoreLogo.png"  />
                <TextBlock x:Name="Text" RelativePanel.Below="Image" local:RelativeSize.Width="0.1" HorizontalAlignment="Left" TextWrapping="WrapWholeWords" Text="Lorem ipsum ..." />
            </StackPanel>
        </Grid>

    Please have a try and also notice that since the RelativeSize.Width is an attached property, we have to set it in the Text control, or the VisualState.Setters would not find the proper property and then parse failed.

    By the way, I suggest you could check this guide post to add a proper tag for the thread title.

    Regards.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.


    • Edited by Fred Bao Tuesday, August 18, 2015 10:09 AM
    • Proposed as answer by Fred Bao Tuesday, August 18, 2015 10:09 AM
    • Unproposed as answer by Fred Bao Tuesday, August 18, 2015 12:56 PM
    Monday, August 10, 2015 1:03 PM
  • I have tested and It should work but <Setter Target="Text.(RelativeSize.Width)" Value="0.8" /> it is never called.

    I think there is an internal limitation that only works with the controls added in the windows namespace, and does not process and neither throws exception because avoids custom attached properties.

    The workaround I found is:

    1.- The complete example:

    <Page x:Name="Root"
    x:Class="Deletetb.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Deletetb"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <RelativePanel Background="Magenta">
     <VisualStateManager.VisualStateGroups>
      <VisualStateGroup x:Name="VisualStateGroup" CurrentStateChanged="VisualStateGroup_CurrentStateChanged">
       <VisualState x:Name="NarrowView">
        <VisualState.StateTriggers>
         <AdaptiveTrigger MinWindowWidth="0" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
         <Setter Target="Text.(RelativePanel.Below)" Value="Image" />
         <Setter Target="Content.(RelativePanel.Below)" Value="Text" />
         <Setter Target="Text.(RelativePanel.RightOf)" Value="{x:Null}" />
         <Setter Target="Text.(RelativeSize.Width)" Value="1.0"/>
         <Setter Target="Image.(RelativeSize.Width)" Value="1.0"/>
        </VisualState.Setters>
       </VisualState>
       <VisualState x:Name="WideView">
        <VisualState.StateTriggers>
         <AdaptiveTrigger MinWindowWidth="860" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
         <Setter Target="Text.(RelativePanel.Below)" Value="{x:Null}" />
         <Setter Target="Text.(RelativePanel.RightOf)" Value="Image" />
         <Setter Target="Content.(RelativePanel.Below)" Value="Image" />
         <Setter Target="Text.(RelativeSize.Width)" Value="0.5"/>
         <Setter Target="Image.(RelativeSize.Width)" Value="0.5"/>
        </VisualState.Setters>
       </VisualState>
      </VisualStateGroup>
     </VisualStateManager.VisualStateGroups>
     <Image x:Name="Image" Source="ms-appx:///Assets/StoreLogo.png"  local:RelativeSize.Container="{Binding ElementName=Root}" local:RelativeSize.Width="0.5" />
     <TextBlock x:Name="Text" RelativePanel.Below="Image" local:RelativeSize.Width="0.5"  TextWrapping="WrapWholeWords" Text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum." />
     <Border x:Name="Content" Background="Blue" RelativePanel.Below="Text" >
      <TextBlock Text="Other Content" HorizontalAlignment="Center" VerticalAlignment="Center" />
     </Border>

    </RelativePanel>
    </Page>

    2.- The RelativeSize custom attached properties:

    public class RelativeSize : DependencyObject
    {
     private static List<FrameworkElement> elements = new List<FrameworkElement>();
     private static FrameworkElement Container = null;
     private static bool containerready = false;
     public static void SetContainer(UIElement element, FrameworkElement value)
     {
      element.SetValue(ContainerProperty, value);
     }
     public static FrameworkElement GetContainer(UIElement element)
     {
      return (FrameworkElement)element.GetValue(ContainerProperty);
     }
     public static readonly DependencyProperty ContainerProperty =
      DependencyProperty.RegisterAttached("Container", typeof(FrameworkElement), typeof(RelativeSize), new PropertyMetadata(null, ContainerChanged));
     private static void ContainerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
      Container = (e.NewValue as FrameworkElement);
      Container.SizeChanged += (sc, ec) =>
      {
       foreach (var element in elements)
       {
        var rWidth = element.GetValue(RelativeSize.WidthProperty);
        if (rWidth != null)
        {
         element.Width = (double)rWidth * Container.ActualWidth;
        }
       }
      };
      containerready = true;
     }
     public static void SetWidth(UIElement element, double value)
     {
      element.SetValue(WidthProperty, value);
     }
     public static double GetWidth(UIElement element)
     {
      return (double)element.GetValue(WidthProperty);
     }
     public static readonly DependencyProperty WidthProperty =
       DependencyProperty.RegisterAttached("Width", typeof(double), typeof(RelativeSize), new PropertyMetadata(0.0, WidthChanged));
     private static async void WidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
      while (!containerready)
       await Task.Delay(60);

      var fe = d as FrameworkElement;
      if (fe != null)
      {
       if (!elements.Contains(fe))
        elements.Add(fe);
       fe.Width = (double)e.NewValue * Container.ActualWidth;
      }
     }
    }

    3.- the VisualStateGroup_CurrentStateChanged

    private void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
    {
     foreach (var sbase in e.NewState.Setters)
     {
      var setter = sbase as Setter;
      var spath = setter.Target.Path.Path;
      var element = setter.Target.Target as FrameworkElement;
      if (spath.Contains(nameof(RelativeSize)))
      {
       string property = spath.Split('.').Last().TrimEnd(')');
       var prop = typeof(RelativeSize).GetMethod($"Set{property}");

       prop.Invoke(null, new object[] { element, setter.Value });
      }
     }
    }

    As you can see in the e.NewState.Setters the setter with relativesize is in there, so the base event simply miss it and you need the event I wrote.


    Juan Pablo G.C.

    Tuesday, August 18, 2015 10:50 AM