Динамическая сетка данных WPF с пользовательскими контролями - PullRequest
3 голосов
/ 11 ноября 2011

Я занимался этим несколько раз за последние пару месяцев, но не могу понять, как это сделать.

У меня есть DataGrid, который должен отображать кликабельный пользовательский контроль во всех столбцах, кроме первого, который должен быть обычным текстовым столбцом без возможности редактирования.Проблема в том, что количество столбцов должно быть динамическим, может быть от 2 до n.

Поскольку я даже не знаю, с чего начать, у меня нет примера кода.

Если бы кто-нибудь мог помочь мне встать на путь, я был бы очень благодарен.Решение не обязательно должно быть правильным MVVM или необычным, оно просто должно работать.

1 Ответ

0 голосов
/ 11 ноября 2011

ОБНОВЛЕНИЕ 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>
...