WPF 写了一个支持数据绑定的Grid

复制代码
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp4
{
    /*例子:
      <ScrollViewer>
        <local:DataBindGrid x:Name="grid">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"></ColumnDefinition>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition Width="auto"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="22"></RowDefinition>
            </Grid.RowDefinitions>

            <TextBlock Text="Header1" Margin="10,0,0,0" Grid.Column="0"></TextBlock>
            <TextBlock Text="Header2" Margin="10,0,0,0" Grid.Column="1"></TextBlock>
            <TextBlock Text="Header3" Margin="10,0,0,0" Grid.Column="2"></TextBlock>

            <local:DataBindGrid.ItemTemplate>
                <ControlTemplate>
                    <Grid Height="30">
                        <!-- 这里设置了Height等于设置了DataBindGrid的行高 -->
                        <TextBlock Text="{Binding Name0}" Margin="10,0,0,0" Grid.Column="0" MouseDown="AddDataClick"></TextBlock>
                        <TextBlock Text="{Binding Name1}" Margin="10,0,0,0" Grid.Column="1" MouseDown="DeleteDataClick"></TextBlock>
                        <TextBlock Text="{Binding Name2}" Margin="10,0,0,0" Grid.Column="2"></TextBlock>
                        <TextBlock Text="{Binding Name3}" Margin="10,0,0,0" Grid.Column="3"></TextBlock>
                    </Grid>
                </ControlTemplate>
            </local:DataBindGrid.ItemTemplate>
        </local:DataBindGrid>
    </ScrollViewer>
     */
    public class DataBindGrid:Grid
    {
        public static readonly DependencyProperty ItemTemplateProperty= DependencyProperty.Register("ItemTemplate", typeof(ControlTemplate), typeof(DataBindGrid), null);
        public ControlTemplate ItemTemplate
        {
            get
            {
                return (ControlTemplate)GetValue(ItemTemplateProperty);
            }
            set
            {
                SetValue(ItemTemplateProperty, value);
            }
        }

        public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(System.Collections.IEnumerable), typeof(DataBindGrid), new PropertyMetadata(OnItemsSourcePropertyValueChanged));
        private static void OnItemsSourcePropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataBindGrid grid = (DataBindGrid)d;
            if (e.OldValue != null && e.OldValue is INotifyCollectionChanged data)
            {
                data.CollectionChanged -= grid._notifyCollectionChanged_CollectionChanged;
            }

            grid._notifyCollectionChanged = e.NewValue as INotifyCollectionChanged;
            if (grid._notifyCollectionChanged != null && grid._notifyCollectionChanged is INotifyCollectionChanged data2)
            {
                data2.CollectionChanged += grid._notifyCollectionChanged_CollectionChanged;
            }


            grid.Bind();
        }
        public System.Collections.IEnumerable ItemsSource
        {
            get
            {
                return (System.Collections.IEnumerable)GetValue(ItemsSourceProperty);
            }
            set
            {
                SetValue(ItemsSourceProperty, value);
            }
        }

        public delegate void CreateRowDefinitionHandler(object sender, RowDefinition row, int rowIndex);


        INotifyCollectionChanged _notifyCollectionChanged;

        public event CreateRowDefinitionHandler CreateRowDefinition;
        ConcurrentDictionary<object, int> _dataContextDict = new ConcurrentDictionary<object, int>();
        List<FrameworkElement> _headerChildren = new List<FrameworkElement>();

        public DataBindGrid()
        {
      
        }

        private void _notifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.Dispatcher.Invoke(() =>
            {
                if (e.Action == NotifyCollectionChangedAction.Remove)
                {
                    foreach (var data in e.OldItems)
                    {
                        var index = _dataContextDict[data];
                        _dataContextDict.TryRemove(data, out int o);
                        removeRow(index);
                    }
                }
                else if (e.Action == NotifyCollectionChangedAction.Reset)
                {
                    this.Bind();
                }
                else if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    var newStartIndex = e.NewStartingIndex;
                    if (_headerChildren.Count > 0)
                        newStartIndex++;

                    for (int i = 0; i < e.NewItems.Count; i++)
                    {
                        var row = new RowDefinition();

                        if (this.CreateRowDefinition != null)
                        {
                            this.CreateRowDefinition(this, row, i + newStartIndex);
                        }
                        this.RowDefinitions.Insert(i + newStartIndex, row);
                    }

                    foreach (var pair in _dataContextDict)
                    {
                        if (pair.Value >= newStartIndex)
                        {
                            var index = pair.Value + e.NewItems.Count;
                            _dataContextDict[pair.Key] = index;

                            for (int i = 0; i < this.Children.Count; i++)
                            {
                                var ctrl = (FrameworkElement)this.Children[i];
                                if (ctrl.DataContext == pair.Key)
                                {
                                    ctrl.SetValue(Grid.RowProperty, index);
                                }
                            }
                        }
                    }

                    var rowIndex = newStartIndex;
                    foreach (var data in e.NewItems)
                    {
                        _dataContextDict[data] = rowIndex;

                        createRowControls(data, rowIndex);

                        rowIndex++;
                    }
                }
            });

        }

        void removeRow(int rowIndex)
        {
            for (int i = 0; i < this.Children.Count; i++)
            {
                var ctrl = this.Children[i];
                var rowvalue = ctrl.GetValue(Grid.RowProperty);
                if (rowvalue != null && rowvalue.Equals(rowIndex))
                {
                    this.Children.RemoveAt(i);
                    i--;
                }
            }

            this.RowDefinitions.RemoveAt(rowIndex);

            foreach (var pair in _dataContextDict)
            {
                if (pair.Value >= rowIndex)
                {
                    var index = pair.Value - 1;
                    _dataContextDict[pair.Key] = index;

                    for (int i = 0; i < this.Children.Count; i++)
                    {
                        var ctrl = (FrameworkElement)this.Children[i];
                        if (ctrl.DataContext == pair.Key)
                        {
                            ctrl.SetValue(Grid.RowProperty, index);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 创建行的子控件
        /// </summary>
        /// <param name="data"></param>
        /// <param name="rowIndex"></param>
        void createRowControls(object data, int rowIndex)
        {
            var rowDef = this.RowDefinitions[rowIndex];
            var templateContainer = this.ItemTemplate.LoadContent() as Panel;
            if (templateContainer == null)
                throw new Exception("ItemTemplate的根元素必须是Panel");

            if (double.IsNaN(templateContainer.Height) == false)
                rowDef.Height = new GridLength(templateContainer.Height);

            rowDef.MinHeight = templateContainer.MinHeight;

            rowDef.MaxHeight = templateContainer.MaxHeight;

            while (templateContainer.Children.Count > 0)
            {
                var ctrl = (FrameworkElement)templateContainer.Children[0];
                templateContainer.Children.RemoveAt(0);
                ctrl.DataContext = data;
                ctrl.SetValue(Grid.RowProperty, rowIndex);
                this.Children.Add(ctrl);
            }
            templateContainer = null;

        }

        void Bind()
        {
            if (_headerChildren.Count == 0)
            {
                foreach (FrameworkElement ctrl in this.Children)
                {
                    _headerChildren.Add(ctrl);
                }
            }
            _dataContextDict.Clear();
            for (int i = 0; i < this.Children.Count; i++)
            {
                var ctrl = this.Children[i];
                if (_headerChildren.Contains(ctrl) == false)
                {
                    this.Children.RemoveAt(i);
                    i--;
                }
            }
            var rowIndex = 0;
            if (_headerChildren.Count > 0)
            {
                if (rowIndex >= this.RowDefinitions.Count)
                {
                    var row = new RowDefinition();

                    if (this.CreateRowDefinition != null)
                    {
                        this.CreateRowDefinition(this, row, rowIndex);
                    }
                    this.RowDefinitions.Add(row);
                }
                rowIndex++;
            }

            if (this.ItemTemplate == null)
                return;



            var list = (System.Collections.IEnumerable)_notifyCollectionChanged;
            foreach (var dataitem in list)
            {
                _dataContextDict[dataitem] = rowIndex;
                if (rowIndex >= this.RowDefinitions.Count)
                {
                    var row = new RowDefinition();

                    if (this.CreateRowDefinition != null)
                    {
                        this.CreateRowDefinition(this, row, rowIndex);
                    }
                    this.RowDefinitions.Add(row);
                }

                createRowControls(dataitem, rowIndex);

                rowIndex++;
            }
        }
    }
}
复制代码

 

posted @   IWing  阅读(219)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
历史上的今天:
2016-10-13 MySql允许远程访问
点击右上角即可分享
微信分享提示