2014-09-08 3 views
1

Voici le problème à l'aide de WPF et Datagrid.Afficher plusieurs colonnes dans WPF DataGrid

Il existe de nombreux objets que les utilisateurs veulent voir comme des lignes dans une table, où les colonnes sont des dates.

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

Mes questions:

  • Ce code fonctionne, mais ont aucune idée de la façon de mettre en œuvre correctement la validation. Et si les valeurs ne sont pas seulement des doubles (cela peut être des int ou des chaînes aussi)?
  • Et si les utilisateurs veulent limiter certaines valeurs pour la plage [0..50]?
  • Est-il possible de créer ces colonnes avec XAML?
  • Peut-être y a-t-il des moyens plus appropriés de représenter de telles données "à plusieurs colonnes"?

Dans Qt, il y avait beaucoup de façons de travailler avec des situations comme celle-ci (utilisez QAbstractItemModel Luc!)

Screenshot of result

Code 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> 

Code 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

Oui, vous pouvez le faire en XAML si tout est prédéfini. La validation est un tout autre animal cependant. Pourriez-vous préciser la question? – BradleyDotNET

+0

@Rustam expliquez-moi ce que vous avez besoin http://chat.stackoverflow.com/rooms/18165/wpf –

+0

@BradleyDotNET, je sais que si toutes les colonnes sont connues (par exemple les propriétés de certains objets), il y aura un moyen pour définir cela en XAML. Mais que faire si la plage de dates est modifiable? –

Répondre

1

C'est Il semble qu'il existe seulement deux façons d'implémenter un tel comportement:

  1. En utilisant DynamicObject comme dans l'exemple ci-dessus.
  2. En utilisant DataTable
Questions connexes