How do I reuse VisualState, VisualStateGroup, and VsualStateManager in a share?
I have created many buttons that use the same VisualStateManager, VisualStateGroup and VisualState in my Windows Phone project. Then I want to reuse them as shared resources as another type of item, like margin, colors, etc.
But I only found a way to reuse the storyboard so it would be better if I can reuse the entire VisualState.
Is there a reusable VisualState solution?
source to share
The solution I found, I tested in Windows 10 UWP app, I tested several options like XAML deserialization, cloning, etc., but I eventually found the following solution to be the best:
1 - Set VisualStateGroup as resource
<Application.Resources>
<DataTemplate x:Key="VisualStateTemplate">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup >
<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" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="WideView">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="860" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Text.(RelativePanel.RightOf)" Value="Image" />
<Setter Target="Content.(RelativePanel.Below)" Value="Image" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</Application.Resources>
This is the first trick you can use to "load" VisualStateGroup
multiple times.
2 - Implement an attached property to set the control that is VisualStateGroup
public class VisualStateExtensions : DependencyObject
{
public static void SetVisualStatefromTemplate(UIElement element, DataTemplate value)
{
element.SetValue(VisualStatefromTemplateProperty, value);
}
public static DataTemplate GetVisualStatefromTemplate(UIElement element)
{
return (DataTemplate) element.GetValue(VisualStatefromTemplateProperty);
}
public static readonly DependencyProperty VisualStatefromTemplateProperty = DependencyProperty.RegisterAttached("VisualStatefromTemplate", typeof(DataTemplate), typeof(VisualStateExtensions), new PropertyMetadata(null, VisualStatefromTemplateChanged));
private static void VisualStatefromTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FrameworkElement frameworkElement)
{
var visualStateGroups = VisualStateManager.GetVisualStateGroups(frameworkElement);
if (visualStateGroups != null)
{
var template = (DataTemplate) e.NewValue;
var content = (FrameworkElement) template.LoadContent();
if (VisualStateManager.GetVisualStateGroups(content) is IList list)
{
var source = list.Cast<VisualStateGroup>().ToList();
var original = source.First();
source.RemoveAt(0);
visualStateGroups.Add(original);
}
}
}
}
}
Just copy and paste this step and now in your controls just add:
3 - Add attached property
<UserControl x:Class="Example.MyUserControl1"...>
<RelativePanel x:Name="Root" local:VisualStateExtensions.VisualStatefromTemplate="{StaticResource VisualStateTemplate}" >
</UserControl>
<UserControl x:Class="Example.MyUserControl2"...>
<RelativePanel x:Name="Root" local:VisualStateExtensions.VisualStatefromTemplate="{StaticResource VisualStateTemplate}" >
</UserControl>
And with that, you can share a VisualStateGroup among multiple controls without repeating code.
source to share