本文将告诉大家如何在 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 文件夹,即可获取到源代码

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


本文会经常更新,请阅读原文: https://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.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者收藏我的博客导航

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

微软最具价值专家


无盈利,不卖课,做纯粹的技术博客

以下是广告时间

推荐关注 Edi.Wang 的公众号

欢迎进入 Eleven 老师组建的 .NET 社区

以上广告全是友情推广,无盈利