Skip to content

简单在 WinUI 仿造 WPF 的 ColumnDefinition SharedSizeGroup 共享列宽功能

Updated: at 23:25,Created: at 23:25

本文将告诉大家如何在 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;
}
}

本文代码放在 githubgitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快

先创建一个空文件夹,接着使用命令行 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 文件夹,即可获取到源代码

更多技术博客,请参阅 博客导航


知识共享许可协议

原文链接: http://blog.lindexi.com/post/%E7%AE%80%E5%8D%95%E5%9C%A8-WinUI-%E4%BB%BF%E9%80%A0-WPF-%E7%9A%84-ColumnDefinition-SharedSizeGroup-%E5%85%B1%E4%BA%AB%E5%88%97%E5%AE%BD%E5%8A%9F%E8%83%BD

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系