概述 UWP Community Toolkit Extensions 中有一個為FrameworkElement 提供的擴展 - FrameworkElement Extensions,本篇我們結合代碼詳細講解 FrameworkElement Extensions 的實現。 FrameworkE ...
概述
UWP Community Toolkit Extensions 中有一個為FrameworkElement 提供的擴展 - FrameworkElement Extensions,本篇我們結合代碼詳細講解 FrameworkElement Extensions 的實現。
FrameworkElement Extensions 為 FrameworkElement 提供了一種簡單的綁定實際尺寸的方式,擴展利用 EnableActualSizeBinding 來指定是否允許實時綁定實際尺寸中的 ActualWidth 和 ActualHeight。 接下來看看官方示例的截圖:
Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/extensions/frameworkelementextensions
Namespace: Microsoft.Toolkit.Uwp.UI.Extensions; Nuget: Microsoft.Toolkit.Uwp.UI;
開發過程
代碼分析
FrameworkElement Extensions 的功能實現比較簡單,在 FrameworkElementExtensions.cs 類中;先看一下類的結構:
我們看到,類中定義了三個依賴屬性:
- EnableActualSizeBindingProperty - boolean,標誌是否允許實際尺寸綁定;屬性改變時觸發 OnEnableActualSizeBindingtPropertyChanged;
- ActualHeightProperty - double,實際尺寸的高度; 預設值 double.NaN;
- ActualWidthProperty - double,實際尺寸的寬度;預設值 double.NaN;
而這三個依賴屬性分別對應的 get 和 set 方法分別是:
- GetEnableActualSizeBinding(obj) 和 SetEnableActualSizeBinding(obj, value)
- GetActualHeight(obj) 和 SetActualHeight(obj, value)
- GetActualWidth(obj) 和 SetActualWidth(obj, value)
下麵看一下實際綁定和 EnableActualSizeBinding 的處理代碼:
在 OnEnableActualSizeBindingtPropertyChanged(sender, args) 方法處理中,可以看到當 EnableActualSizeBinding 變為 True 時,強制刷新一次實際尺寸,且開始響應 SizeChanged 事件,處理同樣是刷新實際尺寸屬性;而當 EnableActualSizeBinding 變為 False 時,去掉 SizeChanged 事件的監聽;
這樣的結果就是,當 EnableActualSizeBinding 變為 False 時,獲取到的 ActualHeight 和 ActualWidrh 一直都是變為 False 時最後一個值,不管尺寸怎麼改變都不會被更新和監聽;
而通過擴展設置的綁定,和直接設置 ActualHeight 和 ActualWidth 的綁定的區別就是,直接設置的方式,對於 Width 和 Height 未指定的情況綁定無效,且不會更新,而擴展的方式可以獲取初始尺寸且可以實時更新;
private static void OnEnableActualSizeBindingtPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { var baseElement = sender as FrameworkElement; if (baseElement == null) { return; } if ((bool)args.NewValue) { // Size may have changed while this was disabled, so we force an updated once user enables it UpdateActualSizeProperties(baseElement, null); // Subscribe to event baseElement.SizeChanged += UpdateActualSizeProperties; } else { // Unsubscribe from event baseElement.SizeChanged -= UpdateActualSizeProperties; } }
在下麵的 UpdateActualSizeProperties(sender, args) 方法中,可以看到綁定目標的 ActualHeight 和 ActualWidth 在改變時,會實時賦值,這樣綁定的屬性就能得到實時的更新。
private static void UpdateActualSizeProperties(object sender, RoutedEventArgs routedEventArgs) { var baseElement = sender as FrameworkElement; if (baseElement == null) { return; } // Update only if needed var currentHeight = GetActualHeight(baseElement); if (currentHeight != baseElement.ActualHeight) { SetActualHeight(baseElement, baseElement.ActualHeight); } // Update only if needed var currentWidth = GetActualWidth(baseElement); if (currentWidth != baseElement.ActualWidth) { SetActualWidth(baseElement, baseElement.ActualWidth); } }
代碼簡單分析如上,大家在實際項目中可以對這個類進行擴展,比如把 Opacity,Color 等也作為可以實時綁定的值,實現方式和 ActualHeight ActualWidth 很類似,大家可以自行擴展,然後把擴展後的類提 PR 到 UWPCOmmunityToolkit Github 中。
調用示例
我們創建了三個 Rectangle,第一個是綁定目標,第二和第三個去綁定第一個的實際尺寸;可以看到因為第二個紅色矩形使用 ActualHeight 和 ActualWidth 直接進行綁定,所以並沒有綁定到正確的值;而第三個淺藍色矩形的初始綁定值是正確的;而在第一個矩形的尺寸隨著 GridSplitter 變化時,紅色矩形沒有任何變化,而淺藍色矩形會跟隨變化更新尺寸;這和我們預期的結果是一致的。
<StackPanel x:Name="RootGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="48"> <Grid Height="300"> <Grid.RowDefinitions> <RowDefinition MinHeight="100" MaxHeight="300" /> <RowDefinition Height="11" /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition MinWidth="100" MaxWidth="800" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Rectangle Grid.Column="0" Grid.Row="0" extensions:FrameworkElementExtensions.EnableActualSizeBinding="True" Fill="Blue" Stroke="Gray" x:Name="TargetObject" StrokeThickness="1"/> <!--Column Grid Splitter--> <controls:GridSplitter Width="11" Background="Gray" GripperCursor="Default" HorizontalAlignment="Left" Grid.Column="1" ResizeDirection="Auto" ResizeBehavior="BasedOnAlignment" CursorBehavior="ChangeOnGripperHover" GripperForeground="White"/> <!--Row Grid Splitter--> <controls:GridSplitter Grid.Row="1" Background="Gray" Height="11" HorizontalAlignment="Stretch"> <controls:GridSplitter.Element> <Grid> <TextBlock HorizontalAlignment="Center" IsHitTestVisible="False" VerticalAlignment="Center" Text="" Foreground="White" FontFamily="Segoe MDL2 Assets"/> </Grid> </controls:GridSplitter.Element> </controls:GridSplitter> </Grid> <Rectangle Margin="12,12" HorizontalAlignment="Left" Height="{Binding ElementName=TargetObject, Path=ActualHeight}" Width="{Binding ElementName=TargetObject, Path=ActualWidth}" Fill="Red" Stroke="Gray" StrokeThickness="1"/> <Rectangle Margin="12,12" HorizontalAlignment="Left" Height="{Binding ElementName=TargetObject, Path=(extensions:FrameworkElementExtensions.ActualHeight)}" Width="{Binding ElementName=TargetObject, Path=(extensions:FrameworkElementExtensions.ActualWidth)}" Fill="LightBlue" Stroke="Gray" StrokeThickness="1"/> </StackPanel>
總結
到這裡我們就把 UWP Community Toolkit Extensions 中的 FrameworkElement Extensions 的源代碼實現過程和簡單的調用示例講解完成了,希望能對大家更好的理解和使用這個擴展有所幫助。歡迎大家多多交流,謝謝!
最後,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通過微博關註最新動態。
衷心感謝 UWPCommunityToolkit 的作者們傑出的工作,Thank you so much, UWPCommunityToolkit authors!!!