本文將告訴大家如何在 WinUI 3 或 UNO 裡面,仿造 WPF 的 ColumnDefinition SharedSizeGroup 共享列寬功能
本文的實現程式碼是大量從 https://github.com/Qiu233/WinUISharedSizeGroup 抄的,感謝大佬提供的程式碼。我在此基礎上簡化了對 Behavior 的依賴,在本文末尾放上了全部程式碼的下載方法
實現效果如下:
在介面放入兩個 Grid 容器,這兩個 Grid 容器分別都有兩列,其中第零個 Grid 裡面的首列放入一個帶背景的 Border 控制元件,預設情況下寬度被壓縮,期望能透過 SharedSizeGroup 的能力共享其他 Grid 的列寬而被撐開。第一個 Grid 裡面的首列放入一個按鈕,按鈕點選的時候修改按鈕的寬度,程式碼如下
<Grid local:ColumnSharedSizeHelper.IsSharedSizeScope="true">
<Grid.RowDefinitions>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
</Grid.RowDefinitions>
<Grid x:Name="Grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Background="Blue" local:ColumnSharedSizeHelper.SharedSizeGroup="S1"></Border>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Width="100" local:ColumnSharedSizeHelper.SharedSizeGroup="S1" Click="Button_OnClick"/>
</Grid>
</Grid>
如以上程式碼可以看到新增了名為 ColumnSharedSizeHelper 的輔助類用來提供 IsSharedSizeScope 和 SharedSizeGroup 附加屬性,這兩個附加屬性和在 WPF 中有一點不一樣的是不能放入在 ColumnDefinition 裡面。現實中我也確實沒有想到什麼辦法可以附加到 ColumnDefinition 裡面實現功能。這也就讓我仿造的功能比 WPF 弱
在後臺程式碼裡面的 Button_OnClick
只修改按鈕寬度,程式碼如下
private void Button_OnClick(object sender, RoutedEventArgs e)
{
var button = (Button) sender;
button.Width += 100;
}
執行程式碼的介面效果如下圖
核心程式碼是 ColumnSharedSizeHelper 型別,其實現邏輯如下
public static class ColumnSharedSizeHelper
{
// Copy From https://github.com/Qiu233/WinUISharedSizeGroup
public static readonly DependencyProperty IsSharedSizeScopeProperty =
DependencyProperty.RegisterAttached("IsSharedSizeScope", typeof(bool), typeof(UIElement), new PropertyMetadata(false));
private static readonly DependencyProperty SharedSizeGroupProperty =
DependencyProperty.RegisterAttached("SharedSizeGroup", typeof(string), typeof(UIElement), new PropertyMetadata(null));
public static void SetIsSharedSizeScope(DependencyObject o, bool group) => o.SetValue(IsSharedSizeScopeProperty, group);
public static bool GetIsSharedSizeScope(DependencyObject o) => (bool) o.GetValue(IsSharedSizeScopeProperty);
public static void SetSharedSizeGroup(DependencyObject o, string group)
{
o.SetValue(SharedSizeGroupProperty, group);
if (o is FrameworkElement framework)
{
framework.Loaded -= FrameworkOnLoaded;
framework.Loaded += FrameworkOnLoaded;
void FrameworkOnLoaded(object sender, RoutedEventArgs e)
{
TrySetSize(framework);
framework.SizeChanged -= Framework_SizeChanged;
framework.SizeChanged += Framework_SizeChanged;
}
}
}
private static void Framework_SizeChanged(object sender, SizeChangedEventArgs args)
{
if (sender is not FrameworkElement currentFrameworkElement)
{
return;
}
TrySetSize(currentFrameworkElement);
}
private static void TrySetSize(FrameworkElement currentFrameworkElement)
{
var sharedSizeGroup = GetSharedSizeGroup(currentFrameworkElement);
if (string.IsNullOrEmpty(sharedSizeGroup))
{
return;
}
if (currentFrameworkElement.Parent is not Grid grid)
{
throw new InvalidOperationException();
}
FrameworkElement p = currentFrameworkElement;
while (!ColumnSharedSizeHelper.GetIsSharedSizeScope(p))
{
if (VisualTreeHelper.GetParent(p) is not FrameworkElement fe)
{
return;
}
else
{
p = fe;
}
}
if (p == currentFrameworkElement)
{
return;
}
if (!ColumnSharedSizeHelper.GetIsSharedSizeScope(p))
{
return;
}
var group = p.GetValue(GroupsProperty) as Dictionary<string, ColumnSharedSizeGroup>;
if (group == null)
{
group = new Dictionary<string, ColumnSharedSizeGroup>();
p.SetValue(GroupsProperty, group);
}
if (!group.TryGetValue(sharedSizeGroup, out var columnSharedSizeGroup))
{
columnSharedSizeGroup = new ColumnSharedSizeGroup();
group.Add(sharedSizeGroup, columnSharedSizeGroup);
}
columnSharedSizeGroup.Update(currentFrameworkElement);
}
public static string GetSharedSizeGroup(DependencyObject o)
{
return (string) o.GetValue(SharedSizeGroupProperty);
}
public static readonly DependencyProperty GroupsProperty =
DependencyProperty.RegisterAttached(nameof(ColumnSharedSizeGroup), typeof(Dictionary<string, ColumnSharedSizeGroup>), typeof(UIElement),
new PropertyMetadata(null));
class ColumnSharedSizeGroup
{
public void Update(FrameworkElement currentFrameworkElement)
{
var grid = (Grid) currentFrameworkElement.Parent;
var value = (int) currentFrameworkElement.GetValue(Grid.ColumnProperty);
var column = grid.ColumnDefinitions[value];
if (!_columns.Contains(column))
{
_columns.Add(column);
}
var adjustments = new List<ColumnDefinition>();
var width = currentFrameworkElement.ActualWidth + currentFrameworkElement.Margin.Left + currentFrameworkElement.Margin.Right;
if (width > _columnSize)
{
_columnSize = width;
adjustments.AddRange(_columns);
}
else
{
adjustments.Add(column);
}
foreach (var columnDefinition in adjustments)
{
columnDefinition.Width = new GridLength(_columnSize);
}
}
private readonly List<ColumnDefinition> _columns = [];
private double _columnSize = 0.0;
}
}
本文程式碼放在 github 和 gitee 上,可以使用如下命令列拉取程式碼。我整個程式碼倉庫比較龐大,使用以下命令列可以進行部分拉取,拉取速度比較快
先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 48c6e653a28a5f5609738a288b9b34b31f37c18c
以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令列繼續輸入以下程式碼,將 gitee 源換成 github 源進行拉取程式碼。如果依然拉取不到程式碼,可以發郵件向我要程式碼
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 48c6e653a28a5f5609738a288b9b34b31f37c18c
獲取程式碼之後,進入 UnoDemo/JeawehonawbuWhaikeregaryere 資料夾,即可獲取到原始碼
更多技術部落格,請參閱 部落格導航