在WPF DataGrid中显示许多列

时间:2014-09-08 18:19:14

标签: c# wpf xaml datagrid

这是使用WPF和Datagrid的问题。

有许多对象,用户希望在表中看到行,其中列是日期。

|  Name     |Jan 2014|Feb 2014|Mar 2014|...|
--------------------------------------------
| coolObj1  |    10.0|    10.0|    20.0|...|
| coolObj2  |    15.0|    19.0|    25.0|...|

我的问题:

  • 这些代码有效,但对如何正确实现验证没有任何想法。如果值不只是双倍(也可以是int或字符串)会怎么样?
  • 如果用户想要限制范围[0..50]的某些值?
  • ,该怎么办?
  • 有没有办法用XAML创建列?
  • 也许有更合适的方式来表示这样的“多列”数据?

在Qt中有很多方法可以处理这样的情况(使用QAbstractItemModel Luke!)

Screenshot of result

XAML代码:

<Window x:Class="Columns.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Find easter eggs" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Grid.Row="0" HorizontalAlignment="Left" Margin="5" Click="OnClick">Update grid</Button>
        <DataGrid x:Name="Grid"
                  CanUserAddRows="False"
                  CanUserDeleteRows="False"
                  CanUserResizeRows="False"
                  CanUserReorderColumns="False"
                  CanUserResizeColumns="True"
                  CanUserSortColumns="False"
                  Grid.Row="1" />
    </Grid>
</Window>

Xaml.cs代码:

namespace Columns
{
    public partial class MainWindow
    {
        private SomeDict _dict;

        public MainWindow()
        {
            InitializeComponent();
            GetData();
            UpdateView();
        }

        private void GetData()
        {
            // We assume that there can be a lot of dicts
            // Currently we work with the only one

            _dict = new SomeDict();
            Random rnd = new Random();

            var start = new DateTime(2010, 1, 1);
            var end = new DateTime(2020, 1, 1);

            for (DateTime date = start; date < end; date = date.AddMonths(1))
            {
                _dict.Add(date, rnd.NextDouble());
            }
        }

        private void UpdateView()
        {
            var row = new RowContext();

            // Create name column
            string columnName = "Name";
            ICellContext cellContext = new NameCell("Easter egg");
            row[columnName] = cellContext;

            Grid.Columns.Clear();
            Grid.Columns.Add(new DataGridTextColumn()
            {
                Binding = new Binding(columnName),
                Header = columnName,
                Width = 100,
            });

            var stringToDoubleConverter = new StringToDoubleConverter();

            // Create column for each date in dictionary

            foreach (var pair in _dict)
            {
                DateTime date = pair.Key;
                columnName = AsColumnName(date);

                Grid.Columns.Add(new DataGridTextColumn()
                {
                    Binding = new Binding(columnName)
                    {
                        StringFormat = "N3",
                        Mode = BindingMode.TwoWay,
                        Converter = stringToDoubleConverter,
                    },
                    IsReadOnly = false,
                    Width = 80,
                    Header = AsReadable(date),
                });

                cellContext = new CellContext(_dict, date, typeof (double), pair.Value);
                row[columnName] = cellContext;
            }

            // Finally set ItemsSource
            Grid.ItemsSource = new[] {row};
        }

        private string AsReadable(DateTime date)
        {
            return date.ToString("d");
        }

        private string AsColumnName(DateTime date)
        {
            return date.ToString("yy-MM-dd");
        }

        private void OnClick(object sender, RoutedEventArgs e)
        {
            UpdateView();
        }
    }

    public interface ICellContext
    {
        object Value { get; set; }
        bool IsEditable { get; }
        Type PropertyType { get; }
    }

    internal sealed class NameCell : ICellContext
    {
        private string _name;

        public NameCell(string name)
        {
            _name = name;
        }

        public object Value
        {
            get { return _name; }
            set { }
        }

        public bool IsEditable
        {
            get { return false; }
        }

        public Type PropertyType
        {
            get { return typeof(string); }
        }
    }

    internal sealed class CellContext : ICellContext, INotifyPropertyChanged
    {
        private readonly bool _isEditable;
        private readonly SomeDict _dict;
        private readonly DateTime _date;
        private readonly Type _propertyType;
        private object _initialValue;

        /// <summary>
        ///     Initializes a new instance of the <see cref="T:System.Object" /> class.
        /// </summary>
        public CellContext(SomeDict dict, DateTime date, Type propertyType, object initialValue, bool isEditable=true)
        {
            _dict = dict;
            _date = date;
            _propertyType = propertyType;
            _initialValue = initialValue;
            _isEditable = isEditable;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public object Value
        {
            get { return _initialValue; }
            set
            {
                Debug.Assert(value.GetType() == PropertyType);
                if (Equals(value, _initialValue)) return;

                // Create do/undo action, write value
                _dict[_date] = Convert.ToDouble(value);
                _initialValue = value;

                OnPropertyChanged("Value");
            }
        }

        public bool IsEditable
        {
            get { return _isEditable; }
        }

        public Type PropertyType
        {
            get { return _propertyType; }
        }

        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public sealed class RowContext : DynamicObject, INotifyPropertyChanged
    {
        private readonly IDictionary<string, object> data;

        public RowContext()
        {
            data = new Dictionary<string, object>();
        }

        public object this[string columnName]
        {
            get
            {
                object value;
                if (data.TryGetValue(columnName, out value))
                {
                    var iproperty = value as ICellContext;
                    if (iproperty != null)
                    {
                        return iproperty.Value;
                    }

                    return value;
                }

                return null;
            }
            set
            {
                object value1;
                if (!data.TryGetValue(columnName, out value1))
                {
                    data.Add(columnName, value);
                    OnPropertyChanged(columnName);
                }
                else
                {
                    var iproperty = value1 as ICellContext;
                    if (iproperty != null)
                    {
                        iproperty.Value = value;
                        OnPropertyChanged(columnName);
                    }
                    else if (value1 != value)
                    {
                        data[columnName] = value;
                        OnPropertyChanged(columnName);
                    }
                }
            }
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return data.Keys;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = this[binder.Name];
            return true;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            this[binder.Name] = value;
            return true;
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    internal sealed class SomeDict : Dictionary<DateTime, object>
    {
    }
}

1 个答案:

答案 0 :(得分:1)

似乎只有两种方法可以实现这种行为:

  1. 如上例所示使用DynamicObject。
  2. 使用DataTable