wpf - 带有用户控件的动态 WPF 数据网格

标签 wpf dynamic datagrid datatemplate

在过去的几个月里,我已经用过好几次了,但我不知道该怎么做。

我有一个 DataGrid,它应该在除第一列之外的所有列中显示一个可点击的用户控件,该列应该是一个没有编辑可能性的常规文本列。问题是列数必须是动态的,可以有 2 到 n 个。

因为我什至不知道从哪里开始,所以我没有任何示例代码。

如果有人能帮助我走上正轨,我将不胜感激。解决方案不必是适当的 MVVM 或非常花哨,它只需要工作即可。

最佳答案

更新 1 - 自包含的 c# 版本。

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.Specialized;
using System.Globalization;

namespace DynamicColumns
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.Loaded += 
                (o, e) => 
                {
                    this.PopulateItemsSource();
                };
        }

        private void PopulateItemsSource()
        {
            int months = Math.Max(new Random().Next(12), 1);

            this.d.ItemsSource =
                new string[]
                {
                    "John",
                    "Paul",
                    "Peter"
                }.Select(t =>
                    MonthlyPerformance.CreateDummy(t, months)).ToList();
        }

        private void RePopulateButton_Click(object sender, RoutedEventArgs e)
        {
            this.PopulateItemsSource();
        }
    }

    #region "Interfaces - must be in the shared between Objects & UI"

    public interface IDynamicPropertiesObject
    {
        Dictionary<string, string> Properties { get; }
    }

    #endregion

    #region "Objects"

    public class MonthlyPerformance : IDynamicPropertiesObject
    {
        public string PerformerName
        {
            get;
            set;
        }

        public Dictionary<string, string> Properties
        {
            get;
            private set;
        }

        public static MonthlyPerformance CreateDummy(string performerName,
            int months)
        {
            if (months < 1 || months > 12)
            {
                throw new ArgumentException(months.ToString());
            }

            Random random = new Random();

            return new MonthlyPerformance()
            {
                PerformerName =
                    performerName,
                Properties =
                    Enumerable.Range(1, months).ToDictionary(k => new DateTime(1, k, 1).ToString("MMM"), v => random.Next(100).ToString())
            };
        }
    }

    #endregion

    #region "UI"

    internal class DynamicPropertyValueConverter: IValueConverter
    {
        public object  Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            IDynamicPropertiesObject o = value as IDynamicPropertiesObject;

            if (o != null)
            {
                return o.Properties[parameter.ToString()];
            }

            return Binding.DoNothing;
        }

        public object  ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }   

    public class ExtendedDataGrid: DataGrid
    {
        public static readonly DependencyProperty IsDynamicColumnProperty = 
            DependencyProperty.RegisterAttached("IsDynamicColumn", 
                                    typeof(Boolean), 
                                    typeof(ExtendedDataGrid), 
                                    new PropertyMetadata(false));

        private DynamicPropertyValueConverter converter = null;

        public ExtendedDataGrid()
        {
            this.EnableColumnVirtualization = true;
            this.EnableRowVirtualization = true;
            this.AutoGenerateColumns = false;
        }

        private DynamicPropertyValueConverter Converter
        {
            get
            {
                if (this.converter == null)
                {
                    converter = new DynamicPropertyValueConverter();
                }

                return this.converter;
            }
        }

        protected override void  OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);

            this.ReGenerateColums();
        }

        private bool TryGetDynamicColumn(out DataGridColumn column)
        {
            column = 
                this.Columns.FirstOrDefault(t=>(bool)t.GetValue(ExtendedDataGrid.IsDynamicColumnProperty));

            return column != null;
        }

        private void ClearDynamicColumns()
        {
            DataGridColumn column;

            while (this.TryGetDynamicColumn(out column))
            {
                this.Columns.Remove(column);
            }
        }

        private void ReGenerateColums()
        {
            this.ClearDynamicColumns();

            if (this.Items.Count > 0)
            {
                IDynamicPropertiesObject o = 
                    this.Items[0] as IDynamicPropertiesObject;

                if (o != null)
                {
                    foreach (KeyValuePair<string, string> property
                        in o.Properties)
                    {
                        DataGridTextColumn column = 
                            new DataGridTextColumn()
                        {
                            Header = property.Key,
                            Binding = new Binding()
                            {
                                Converter = this.Converter,
                                ConverterParameter = property.Key
                            }
                        };

                        column.SetValue(ExtendedDataGrid.IsDynamicColumnProperty, true); // so we can remove it, when calling ClearDynamicColumns
                        this.Columns.Add(column);
                    }
                }
            }
        }
    }

    #endregion
}

标记:

<Window x:Class="DynamicColumns.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DynamicColumns"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button x:Name="RePopulateButton" Grid.Row="0" Click="RePopulateButton_Click">Re-Populate</Button>
        <local:ExtendedDataGrid x:Name="d" Grid.Row="1">
            <local:ExtendedDataGrid.Columns>
                <DataGridTextColumn Width="Auto" Binding="{Binding PerformerName}"/>
            </local:ExtendedDataGrid.Columns>
        </local:ExtendedDataGrid>
    </Grid>
</Window>

关于wpf - 带有用户控件的动态 WPF 数据网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8095403/

相关文章:

apache-flex - 在 itemEditor 中触发 itemEditEnd 事件

wpf - 将IEnumerable <T>与ObservableCollection <T>包装在一起

JAVA将 "unknown"类型的对象转换为另一个对象的类型

wpf - XAML 中的字符串插值

Clojure 中的动态原子

python - 如何在 Python 中调试动态定义的函数?

c# - 当用户在最后一行的最后一个单元格上按 Enter 键时,向 DataGrid 添加新行

WPF Datagrid 单元格文本换行

c# - 根据多个条件过滤 CollectionView

c# - SizeChanged 上的 ScrollViewer ChangeView