不修改模板的前提下修改VisualState中的某些值

原文連接:不修改模板的前提下修改VisualState中的某些值 - 超威藍火html

UWP裏有一件很是使人不爽的事,大部分控件只提供了Normal狀態下的Background,Foreground,BorderBrush,而控件通常至少具備Normal、PointerOver、Pressed、Disabled,ItemContainerStyle還有Selected、PointerOverSelected、PressedSelected這幾種。那麼常規方法怎麼修改這幾個狀態內的值呢?
固然是貼一遍又臭又長的Style。
那若是有不少不是很同樣的控件,除了修改模板或者自定義一個控件以外,有沒有辦法修改狀態內的值呢?
答案是確定的。咱們能夠經過某些方法拿到VisualStateGroup對象,而後操做裏面的Storyboard。
首先咱們須要一個獲取VisualStateGroup的方法,這玩意兒藏在Style中的Template中的第一個子元素裏,而這個子元素會在須要的元素ApplyTemplate後,經過當猴子(爬樹)獲得:git

public static Task<VisualStateGroup> GetCommonStates(this FrameworkElement element)
{
    var vGroup = VisualStateManager.GetVisualStateGroups(element)?.FirstOrDefault(x => x.Name == "CommonStates");
    var resultSource = new TaskCompletionSource<VisualStateGroup>();

    if (vGroup == null)
    {
        if (element.GetFirstChild() is FrameworkElement ele)
        {
            element = ele;
            vGroup = VisualStateManager.GetVisualStateGroups(element)?.FirstOrDefault(x => x.Name == "CommonStates");
        }
        else if (!element.IsLoaded())
        {
            void Element_Loaded(object sender, RoutedEventArgs e)
            {
                element.Loaded -= Element_Loaded;
                vGroup = VisualStateManager.GetVisualStateGroups(element)?.FirstOrDefault(x => x.Name == "CommonStates");
                if (vGroup == null)
                {
                    if (element.GetFirstChild() is FrameworkElement ele2)
                    {
                        element = ele2;
                        vGroup = VisualStateManager.GetVisualStateGroups(element)?.FirstOrDefault(x => x.Name == "CommonStates");
                    }
                }
                resultSource.SetResult(vGroup);
            }
            element.Loaded += Element_Loaded;
        }
        else
        {
            return null;
        }
    }
    else
    {
        resultSource.SetResult(vGroup);
    }
    return resultSource.Task;
}

這是一個老問題,在設置附加屬性的時候,元素可能並無加載完,這時候是沒有第一個子元素的,因此要用一個內部方法或者內部的RouteEventHandler掛到Loaded上去獲取。內部是由於要共享變量,並且要在進入Loaded事件以後卸載掉這個方法。
接下來咱們要從CommonStates中獲取Pressed和PointerOver這兩個State:github

var commonStates = await ele.GetCommonStates();

 if (commonStates != null)
 {
     var storyboard = commonStates.States.FirstOrDefault(x => x.Name == "PointerOver")?.Storyboard;
     if (storyboard != null)
     {
         var list = storyboard.Children.Where(x => BackgroundNames.Contains(Storyboard.GetTargetName(x).ToLowerInvariant()) && Storyboard.GetTargetProperty(x) == "Background");

         foreach (var item in list)
         {
             item.SetAnimationValue(a.NewValue);
         }
     }
 }

這樣就大功告成了。
使用方法以下:this

xmlns:helper="using:PointerStateHelper.Helpers"

<Button HorizontalAlignment="Center" VerticalAlignment="Center"
        helper:PointerOverHelper.Background="Red" helper:PointerOverHelper.Foreground="Blue" helper:PointerOverHelper.BorderBrush="Transparent"
        helper:PressedHelper.Background="Green" helper:PressedHelper.Foreground="Gray" helper:PressedHelper.BorderBrush="Transparent"
        Content="哈哈哈" Padding="20,10" />

github:https://github.com/cnbluefire/PointerStateHelperspa

相關文章
相關標籤/搜索