UWP里有一件非常令人不爽的事,大部分控制項只提供了Normal狀態下的Background,Foreground,BorderBrush,而控制項一般至少具有Normal、PointerOver、Pressed、Disabled,ItemContainerStyle還有Selected、Pointer ...
UWP里有一件非常令人不爽的事,大部分控制項只提供了Normal狀態下的Background,Foreground,BorderBrush,而控制項一般至少具有Normal、PointerOver、Pressed、Disabled,ItemContainerStyle還有Selected、PointerOverSelected、PressedSelected這幾種。那麼常規方法怎麼修改這幾個狀態內的值呢?
當然是貼一遍又臭又長的Style。
那如果有很多不是很一樣的控制項,除了修改模板或者自定義一個控制項之外,有沒有辦法修改狀態內的值呢?
答案是肯定的。我們可以通過某些方法拿到VisualStateGroup對象,然後操作裡面的Storyboard。
首先我們需要一個獲取VisualStateGroup的方法,這玩意兒藏在Style中的Template中的第一個子元素里,而這個子元素會在需要的元素ApplyTemplate後,通過當猴子(爬樹)得到:
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;
}
原文鏈接:不修改模板的前提下修改VisualState中的某些值 - 超威藍火
這是一個老問題,在設置附加屬性的時候,元素可能並沒有載入完,這時候是沒有第一個子元素的,所以要用一個內部方法或者內部的RouteEventHandler掛到Loaded上去獲取。內部是因為要共用變數,而且要在進入Loaded事件之後卸載掉這個方法。
接下來我們要從CommonStates中獲取Pressed和PointerOver這兩個State:
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);
}
}
}
這樣就大功告成了。
使用方法如下:
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/PointerStateHelper