2013-03-21 14 views
11

Sto sviluppando un prodotto in wpf (modello mvvm). Ho uno scenario in cui ho trascorso più di una settimana senza risultati positivi. Pls aiutami se puoi ... Ecco lo scenario, in base alla personalizzazione dell'utente (l'utente ll seleziona le colonne) Devo visualizzare un insieme di dati in datagrid. Attualmente sto legando una ObservableCollection con un insieme di proprietà alla fonte di dati di datagrid. Questo mi limita a dimensioni di colonna fisse. Nota: il numero n di colonne nome è elencato per la selezione dell'utente. Se è fatto in codice dietro è facile da "datagrid.columns.add()". Qualcuno là fuori può aiutarmi in questo scenario.E 'possibile ottenere colonne dinamiche su datagrid wpf nel modello mvvm?

mia XAML:

<my:DataGrid AutoGenerateColumns="False" Margin="357,121.723,82,41" Name="dataGrid3" c:DataGridExtension.Columns="{Binding ColumnCollection}" /> 

la mia classe di comando:

public static class DataGridExtension 
{ 
    public static ObservableCollection<DataGridColumn> GetColumns(DependencyObject obj) 
    { 
     return (ObservableCollection<DataGridColumn>)obj.GetValue(ColumnsProperty); 
    } 

    public static void SetColumns(DependencyObject obj, ObservableCollection<DataGridColumn> value) 
    { 
     obj.SetValue(ColumnsProperty, value); 
    } 

    public static readonly DependencyProperty ColumnsProperty = 
     DependencyProperty.RegisterAttached("Columns", typeof(ObservableCollection<DataGridColumn>),typeof(DataGridExtension), 
     new UIPropertyMetadata (new ObservableCollection<DataGridColumn>(), OnDataGridColumnsPropertyChanged)); 

    private static void OnDataGridColumnsPropertyChanged(DependencyObject d, 
      DependencyPropertyChangedEventArgs e) 
    { 
     if (d.GetType() == typeof(DataGrid)) 
     { 
      DataGrid myGrid = d as DataGrid; 

      ObservableCollection<DataGridColumn> Columns = (ObservableCollection<DataGridColumn>)e.NewValue; 

      if (Columns != null) 
      { 
       myGrid.Columns.Clear(); 

       if (Columns != null && Columns.Count > 0) 
       { 
        foreach (DataGridColumn dataGridColumn in Columns) 
        { 
         myGrid.Columns.Add(dataGridColumn); 
        } 
       } 


       Columns.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs args) 
       { 
        if (args.NewItems != null) 
        { 
         //foreach (DataGridColumn column in args.NewItems.Cast<DataGridColumn>()) 
         //{ 
         // myGrid.Columns.Add(column); 
         //} 
        } 

        if (args.OldItems != null) 
        { 

         //foreach (DataGridColumn column in args.OldItems.Cast<DataGridColumn>()) 
         //{ 
         // myGrid.Columns.Remove(column); 
         //} 
        } 
       }; 

      } 
     } 
    } 
} 

e la mia proprietà in ViewModel:

private ObservableCollection<DataGridColumn> _columnCollection = new ObservableCollection<DataGridColumn>(); 
public ObservableCollection<DataGridColumn> ColumnCollection 
{ 
    get 
    { 
     return this._columnCollection; 
    } 
    set 
    { 
     _columnCollection = value; 
     base.OnPropertyChanged("ColumnCollection"); 
     //Error 
     //base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(() => this.ColumnCollection); 
    } 
} 
+4

mvvm non limita l'utilizzo del codice dietro – blindmeis

+0

Modalità grezza: crea tutte le colonne in primo piano e nasconde ciò che non è selezionato dall'utente. –

+0

blindmeis, come hai detto mvvm non mi limita il codice, i miei TL sono ... –

risposta

8

Grazie per il vostro sforzo del ragazzo ... finalmente ho trovato la soluzione ....

qui la sua .. (MVVM WPF pieno)

Nel mio file di comando:

public class DataGridColumnsBehavior 
    { 
     public static readonly DependencyProperty BindableColumnsProperty = 
      DependencyProperty.RegisterAttached("BindableColumns", 
               typeof(ObservableCollection<DataGridColumn>), 
               typeof(DataGridColumnsBehavior), 
               new UIPropertyMetadata(null, BindableColumnsPropertyChanged)); 
     private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) 
     { 
      DataGrid dataGrid = source as DataGrid; 
      ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>; 
      dataGrid.Columns.Clear(); 
      if (columns == null) 
      { 
       return; 
      } 
      foreach (DataGridColumn column in columns) 
      { 
       dataGrid.Columns.Add(column); 
      } 
      columns.CollectionChanged += (sender, e2) => 
      { 
       NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs; 
       if (ne.Action == NotifyCollectionChangedAction.Reset) 
       { 
        dataGrid.Columns.Clear(); 
        if (ne.NewItems != null) 
        { 
         foreach (DataGridColumn column in ne.NewItems) 
         { 
          dataGrid.Columns.Add(column); 
         } 
        } 
       } 
       else if (ne.Action == NotifyCollectionChangedAction.Add) 
       { 
        if (ne.NewItems != null) 
        { 
         foreach (DataGridColumn column in ne.NewItems) 
         { 
          dataGrid.Columns.Add(column); 
         } 
        } 
       } 
       else if (ne.Action == NotifyCollectionChangedAction.Move) 
       { 
        dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex); 
       } 
       else if (ne.Action == NotifyCollectionChangedAction.Remove) 
       { 
        if (ne.OldItems != null) 
        { 
         foreach (DataGridColumn column in ne.OldItems) 
         { 
          dataGrid.Columns.Remove(column); 
         } 
        } 
       } 
       else if (ne.Action == NotifyCollectionChangedAction.Replace) 
       { 
        dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn; 
       } 
      }; 
     } 
     public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value) 
     { 
      element.SetValue(BindableColumnsProperty, value); 
     } 
     public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element) 
     { 
      return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty); 
     } 
    } 

nel mio XAML:

<my:DataGrid AutoGenerateColumns="False" Margin="357,121.723,82,41" Name="dataGrid3" ItemsSource="{Binding Path=Datatable}" c:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}" /> 

e finalmente nel mio ViewModel:

private ObservableCollection<DataGridColumn> _columnCollection = new ObservableCollection<DataGridColumn>(); 
     public ObservableCollection<DataGridColumn> ColumnCollection 
     { 
      get 
      { 
       return this._columnCollection; 
      } 
      set 
      { 
       _columnCollection = value; 
       base.OnPropertyChanged("ColumnCollection"); 
       //Error 
       //base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(() => this.ColumnCollection); 
      } 
     } 
     private DataTable _datatable = new DataTable(); 
     public DataTable Datatable 
     { 
      get 
      { 
       return _datatable; 
      } 
      set 
      { 
       if (_datatable != value) 
       { 
        _datatable = value; 
       } 
       base.OnPropertyChanged("Datatable"); 
      } 
     } 

e nel mio costruttore:

public MainViewModel() 
     { 
Datatable.Columns.Add("Name",typeof(string)); 
      Datatable.Columns.Add("Color", typeof(string)); 
      Datatable.Columns.Add("Phone", typeof(string)); 
      Datatable.Rows.Add("Vinoth", "#00FF00", "456345654"); 
      Datatable.Rows.Add("lkjasdgl", "Blue", "45654"); 
      Datatable.Rows.Add("Vinoth", "#FF0000", "456456"); 
System.Windows.Data.Binding bindings = new System.Windows.Data.Binding("Name"); 
      System.Windows.Data.Binding bindings1 = new System.Windows.Data.Binding("Phone"); 
      System.Windows.Data.Binding bindings2 = new System.Windows.Data.Binding("Color"); 
      DataGridTextColumn s = new DataGridTextColumn(); 
      s.Header = "Name"; 
      s.Binding = bindings; 
      DataGridTextColumn s1 = new DataGridTextColumn(); 
      s1.Header = "Phone"; 
      s1.Binding = bindings1; 
      DataGridTextColumn s2 = new DataGridTextColumn(); 
      s2.Header = "Color"; 
      s2.Binding = bindings2; 

      FrameworkElementFactory textblock = new FrameworkElementFactory(typeof(TextBlock)); 
      textblock.Name = "text"; 
      System.Windows.Data.Binding prodID = new System.Windows.Data.Binding("Name"); 
      System.Windows.Data.Binding color = new System.Windows.Data.Binding("Color"); 
      textblock.SetBinding(TextBlock.TextProperty, prodID); 
      textblock.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap); 
      //textblock.SetValue(TextBlock.BackgroundProperty, color); 
      textblock.SetValue(TextBlock.NameProperty, "textblock"); 
      //FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border)); 
      //border.SetValue(Border.NameProperty, "border"); 
      //border.AppendChild(textblock); 
      DataTrigger t = new DataTrigger(); 
      t.Binding = new System.Windows.Data.Binding { Path = new PropertyPath("Name"), Converter = new EnableConverter(), ConverterParameter ="Phone" }; 
      t.Value = 1; 
      t.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.LightGreen, textblock.Name)); 
      t.Setters.Add(new Setter(TextBlock.ToolTipProperty, bindings, textblock.Name)); 
      DataTrigger t1 = new DataTrigger(); 
      t1.Binding = new System.Windows.Data.Binding { Path = new PropertyPath("Name"), Converter = new EnableConverter(), ConverterParameter = "Phone" }; 
      t1.Value = 2; 
      t1.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.LightYellow, textblock.Name)); 
      t1.Setters.Add(new Setter(TextBlock.ToolTipProperty, bindings, textblock.Name)); 

      DataTemplate d = new DataTemplate(); 
      d.VisualTree = textblock; 
      d.Triggers.Add(t); 
      d.Triggers.Add(t1); 

      DataGridTemplateColumn s3 = new DataGridTemplateColumn(); 
      s3.Header = "Name 1"; 
      s3.CellTemplate = d; 
      s3.Width = 140; 

      ColumnCollection.Add(s); 
      ColumnCollection.Add(s1); 
      ColumnCollection.Add(s2); 
      ColumnCollection.Add(s3); 
    } 
+3

Come si avvicina questo approccio Pure MVVM quando si utilizza "DataGridColumn" da System.Windows.Controls nel ViewModel? –

3

Vorrei estendere l'esempio precedente (risposta) la possibilità di iscriversi e annullare l'iscrizione all'evento CollectionChanged.

Comportamento (aggiungere riferimento System.Windows.Interactivity):

public class ColumnsBindingBehaviour : Behavior<DataGrid> 
{ 
    public ObservableCollection<DataGridColumn> Columns 
    { 
     get { return (ObservableCollection<DataGridColumn>) base.GetValue(ColumnsProperty); } 
     set { base.SetValue(ColumnsProperty, value); } 
    } 

    public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", 
     typeof(ObservableCollection<DataGridColumn>), typeof(ColumnsBindingBehaviour), 
      new PropertyMetadata(OnDataGridColumnsPropertyChanged)); 

    private static void OnDataGridColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) 
    { 
     var context = source as ColumnsBindingBehaviour; 

     var oldItems = e.OldValue as ObservableCollection<DataGridColumn>; 

     if (oldItems != null) 
     { 
      foreach (var one in oldItems) 
       context._datagridColumns.Remove(one); 

      oldItems.CollectionChanged -= context.collectionChanged; 
     } 

     var newItems = e.NewValue as ObservableCollection<DataGridColumn>; 

     if (newItems != null) 
     { 
      foreach (var one in newItems) 
       context._datagridColumns.Add(one); 

      newItems.CollectionChanged += context.collectionChanged; 
     } 
    } 

    private ObservableCollection<DataGridColumn> _datagridColumns; 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 

     this._datagridColumns = AssociatedObject.Columns; 
    } 


    private void collectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     switch (e.Action) 
     { 
      case NotifyCollectionChangedAction.Add: 
       if (e.NewItems != null) 
        foreach (DataGridColumn one in e.NewItems) 
         _datagridColumns.Add(one); 
       break; 

      case NotifyCollectionChangedAction.Remove: 
       if (e.OldItems != null) 
        foreach (DataGridColumn one in e.OldItems) 
         _datagridColumns.Remove(one); 
       break; 

      case NotifyCollectionChangedAction.Move: 
       _datagridColumns.Move(e.OldStartingIndex, e.NewStartingIndex); 
       break; 

      case NotifyCollectionChangedAction.Reset: 
       _datagridColumns.Clear(); 
       if (e.NewItems != null) 
        foreach (DataGridColumn one in e.NewItems) 
         _datagridColumns.Add(one); 
       break; 
     } 
    } 
} 

Vista:

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 

    xmlns:loc="clr-namespace:WpfApplication1" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    Title="MainWindow" Height="350" Width="525"> 
<Grid> 
    <DataGrid x:Name="_dataGrid"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Test" /> 
     </DataGrid.Columns> 
     <i:Interaction.Behaviors> 
      <loc:ColumnsBindingBehaviour Columns="{Binding DataGridColumns}"/>   
     </i:Interaction.Behaviors> 
    </DataGrid> 
</Grid> 

ViewModel:

 private ObservableCollection<DataGridColumn> _dataGridColumns; 
    public ObservableCollection<DataGridColumn> DataGridColumns 
    { 
     get 
     { 
      if (_dataGridColumns == null) 
       _dataGridColumns = new ObservableCollection<DataGridColumn>() 
       { 
        new DataGridTextColumn() 
        { 
         Header = "Column1" 
        } 
       }; 

      return _dataGridColumns; 
     } 
     set 
     { 
      _dataGridColumns = value; 
      OnPropertyChanged("DataGridColumns"); 
     } 
    }