2008-11-26 9 views
112

मेरा डब्ल्यूपीएफ एप्लिकेशन डेटा के सेट जेनरेट करता है जिसमें हर बार कॉलम की एक अलग संख्या हो सकती है। आउटपुट में शामिल प्रत्येक कॉलम का विवरण है जिसका उपयोग स्वरूपण लागू करने के लिए किया जाएगा। उत्पादन का एक सरलीकृत संस्करण हो सकता है कुछ की तरह: इस वर्गमैं एक WPF डेटाग्रिड को कॉलम की एक चर संख्या में कैसे बांधूं?

class Data 
{ 
    IList<ColumnDescription> ColumnDescriptions { get; set; } 
    string[][] Rows { get; set; } 
} 

एक WPF डेटा ग्रिड पर DataContext के रूप में सेट किया गया है, लेकिन मैं वास्तव में कॉलम प्रोग्राम के रूप में बनाने के लिए:

for (int i = 0; i < data.ColumnDescriptions.Count; i++) 
{ 
    dataGrid.Columns.Add(new DataGridTextColumn 
    { 
     Header = data.ColumnDescriptions[i].Name, 
     Binding = new Binding(string.Format("[{0}]", i)) 
    }); 
} 

वहाँ किसी भी तरह से बदलने के लिए है इसके बजाय XAML फ़ाइल में डेटा बाइंडिंग के साथ यह कोड?

उत्तर

113

यहाँ बाइंडिंग कॉलम के लिए एक समाधान है डेटाग्रिड में। चूंकि कॉलम की संपत्ति केवल पढ़ी जाती है, जैसा कि सभी ने देखा है, मैंने एक संलग्न संपत्ति बनाई है जिसे बाइंडेबल कॉलम कहा जाता है जो डेटाग्रिड में कॉलम अपडेट करता है जब भी संग्रह संग्रहित घटना के माध्यम से संग्रह बदल जाता है।

हम इस

<DataGrid Name="dataGrid" 
      local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}" 
      AutoGenerateColumns="False" 
      ...> 

तरह ColumnCollection को DataGridColumn के

public ObservableCollection<DataGridColumn> ColumnCollection 
{ 
    get; 
    private set; 
} 

तो हम बाध्य कर सकते हैं BindableColumns के इस संग्रह है, तो संलग्न संपत्ति BindableColumns

+3

यह है सही जवाब, ब्रावो! –

+0

मैं सहमत हूं, मुझे यह समाधान सबसे अच्छा लगता है। – Jaime

+1

एमवीवीएम पैटर्न – WPFKK

1

आप ऑटोजनेर कॉलम और डेटा टेम्पलेट के साथ ऐसा करने में सक्षम हो सकते हैं। मैं सकारात्मक नहीं हूं अगर यह बहुत सारे काम के बिना काम करेगा, तो आपको इसके साथ खेलना होगा। ईमानदारी से अगर आपके पास पहले से ही एक समाधान समाधान है तो मैं अभी तक बदलाव नहीं करूँगा जब तक कि कोई बड़ा कारण न हो। डेटाग्रिड नियंत्रण बहुत अच्छा हो रहा है लेकिन इसे अभी भी कुछ काम की ज़रूरत है (और मेरे पास बहुत सी सीखने के लिए छोड़ दिया गया है) ताकि आसानी से गतिशील कार्य करने में सक्षम हो सकें।

+0

मेरे कारण है कि ASP.Net से आ रही है कि मैं क्या सभ्य डेटा बाइंडिंग और मैं कर रहा हूँ के साथ किया जा सकता है के लिए नए कर रहा हूँ सुनिश्चित नहीं है कि इसकी सीमाएं कहां हैं। मेरे पास AutoGenerateColumns के साथ एक खेल होगा, धन्यवाद। –

17

मैंने अपना शोध जारी रखा है और इसे करने का कोई उचित तरीका नहीं मिला है। डेटाग्रिड पर कॉलम प्रॉपर्टी कुछ ऐसा नहीं है जिसके खिलाफ मैं बाध्य कर सकता हूं, असल में यह केवल पढ़ा जाता है।

ब्रायन ने ऑटोगनेर कॉलम के साथ कुछ किया जा सकता है, इसलिए मैंने देखा। यह आइटम्ससोर्स में ऑब्जेक्ट्स के गुणों को देखने के लिए सरल नेट प्रतिबिंब का उपयोग करता है और प्रत्येक के लिए एक कॉलम उत्पन्न करता है। शायद मैं प्रत्येक कॉलम के लिए एक संपत्ति के साथ फ्लाई पर एक प्रकार उत्पन्न कर सकता हूं लेकिन यह ट्रैक से बाहर निकल रहा है।

के बाद से इस समस्या को इतनी आसानी से कोड में sovled है मैं एक सरल विस्तार विधि मैं फोन जब भी डेटा संदर्भ नए कॉलम के साथ अद्यतन किया जाता है के साथ चिपके रहते हैं जाएगा:

public static void GenerateColumns(this DataGrid dataGrid, IEnumerable<ColumnSchema> columns) 
{ 
    dataGrid.Columns.Clear(); 

    int index = 0; 
    foreach (var column in columns) 
    { 
     dataGrid.Columns.Add(new DataGridTextColumn 
     { 
      Header = column.Name, 
      Binding = new Binding(string.Format("[{0}]", index++)) 
     }); 
    } 
} 

// E.g. myGrid.GenerateColumns(schema); 
+0

'इंडेक्स' क्या करता है? –

+1

उच्चतम मतदान और स्वीकृत समाधान सबसे अच्छा नहीं है! दो साल बाद जवाब होगा: http://msmvps.com/blogs/deborahk/archive/2011/01/23/populating-a-datagrid-with- गतिशील-columns-in-a-silverlight- appplication-using- mvvm.aspx – Mikhail

+4

नहीं, यह नहीं होगा। वैसे भी प्रदान नहीं किया गया लिंक, क्योंकि उस समाधान का परिणाम पूरी तरह से अलग है! – 321X

2

आप ग्रिड परिभाषा के साथ एक usercontrol बना सकते हैं और कर सकते हैं xaml में विभिन्न कॉलम परिभाषाओं के साथ 'बच्चे' नियंत्रण परिभाषित करें।

जनक:


public ObservableCollection<DataGridColumn> gridColumns 
{ 
    get 
    { 
    return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty); 
    } 
    set 
    { 
    SetValue(ColumnsProperty, value); 
    } 
} 
public static readonly DependencyProperty ColumnsProperty = 
    DependencyProperty.Register("gridColumns", 
    typeof(ObservableCollection<DataGridColumn>), 
    typeof(parentControl), 
    new PropertyMetadata(new ObservableCollection<DataGridColumn>())); 

public void LoadGrid() 
{ 
    if (gridColumns.Count > 0) 
    myGrid.Columns.Clear(); 

    foreach (DataGridColumn c in gridColumns) 
    { 
    myGrid.Columns.Add(c); 
    } 
} 

बाल Xaml:


<local:parentControl x:Name="deGrid">   
    <local:parentControl.gridColumns> 
    <toolkit:DataGridTextColumn Width="Auto" Header="1" Binding="{Binding Path=.}" /> 
    <toolkit:DataGridTextColumn Width="Auto" Header="2" Binding="{Binding Path=.}" /> 
    </local:parentControl.gridColumns> 
</local:parentControl> 

और अंत में माता-पिता कॉलम लोड करने के लिए स्तंभों के लिए एक निर्भरता संपत्ति और एक विधि की जरूरत है , मुश्किल भाग यह है कि 'लोडग्रिड' कहां कॉल करना है '।
मैं अपनी खिड़की निर्माता में InitalizeComponent के बाद फोन करके काम करने के लिए इस के साथ संघर्ष कर लेकिन चीजों को मिला कर रहा हूँ (childGrid है एक्स: window.xaml में नाम):

childGrid.deGrid.LoadGrid(); 

Related blog entry

9

मैं पाया है एक एक अच्छा चाल के साथ डेबोरा कुराता द्वारा ब्लॉग आलेख कैसे varia दिखाने के लिए एक डेटा ग्रिड में स्तंभों की ble संख्या:

Populating a DataGrid with Dynamic Columns in a Silverlight Application using MVVM

असल में, वह एक DataGridTemplateColumn बनाता है और ItemsControl अंदर कि एकाधिक स्तंभों को प्रदर्शित करता है डालता है।

+1

काम करने के लिए लाइनब्रेक नहीं मिल सकता है यह सिल्वरलाइट और डब्ल्यूपीएफ दोनों के लिए पूरी तरह से काम करता है! – Mikhail

+1

यह प्रोग्राम किए गए संस्करण के समान परिणाम नहीं है !! – 321X

+0

@ 321 एक्स: क्या आप कृपया बताए गए अंतरों के बारे में विस्तार से बता सकते हैं (और यह भी निर्दिष्ट करें कि * प्रोग्राम किए गए संस्करण * के द्वारा आपका क्या मतलब है, क्योंकि इसके सभी समाधान प्रोग्राम किए गए हैं), कृपया? –

5

मैं गतिशील रूप से एक स्तंभ इस तरह कोड की सिर्फ एक लाइन का उपयोग जोड़ने के लिए यह संभव बनाने में कामयाब रहे:

MyItemsCollection.AddPropertyDescriptor(
    new DynamicPropertyDescriptor<User, int>("Age", x => x.Age)); 

प्रश्न के बारे में, यह एक XAML आधारित समाधान नहीं है (के बाद से के रूप में उल्लेख नहीं है इसे करने का कोई उचित तरीका नहीं), न तो यह एक समाधान है जो सीधे डेटाग्रिड के साथ काम करेगा। कॉलम। यह वास्तव में डेटाग्रिड बाउंड आइटम्ससोर्स के साथ काम करता है, जो ITypedList लागू करता है और इस तरह PropertyDescriptor पुनर्प्राप्ति के लिए कस्टम विधियां प्रदान करता है। कोड में एक ही स्थान पर आप अपने ग्रिड के लिए "डेटा पंक्तियां" और "डेटा कॉलम" परिभाषित कर सकते हैं।

आप चाहते हैं:

IList<string> ColumnNames { get; set; } 
//dict.key is column name, dict.value is value 
Dictionary<string, string> Rows { get; set; } 

आप उदाहरण के लिए इस्तेमाल कर सकते हैं:

var descriptors= new List<PropertyDescriptor>(); 
//retrieve column name from preprepared list or retrieve from one of the items in dictionary 
foreach(var columnName in ColumnNames) 
    descriptors.Add(new DynamicPropertyDescriptor<Dictionary, string>(ColumnName, x => x[columnName])) 
MyItemsCollection = new DynamicDataGridSource(Rows, descriptors) 

और अपने ग्रिड MyItemsCollection के लिए बाध्य इसी कॉलम के साथ से भरे जाएंगे इस्तेमाल करते हैं। उन स्तंभों को गतिशील रूप से रनटाइम पर संशोधित (नया जोड़ा या मौजूदा हटाया जा सकता है) और ग्रिड स्वचालित रूप से इसके कॉलम संग्रह को रीफ्रेश कर देगा।

ऊपर वर्णित डायनामिकप्रॉपर्टी डिस्क्रिप्टर नियमित संपत्ति डिस्क्रिप्टर के लिए अपग्रेड है और कुछ अतिरिक्त विकल्पों के साथ दृढ़ता से टाइप की गई कॉलम परिभाषा प्रदान करता है। डायनामिकडेटा ग्रिडसोर्स अन्यथा मूल संपत्ति डिस्क्रिप्टर के साथ बस ठीक कार्यक्रम करेगा।

3

स्वीकार्य उत्तर का एक संस्करण बनाया जो सदस्यता रद्द करता है।

public class DataGridColumnsBehavior 
{ 
    public static readonly DependencyProperty BindableColumnsProperty = 
     DependencyProperty.RegisterAttached("BindableColumns", 
              typeof(ObservableCollection<DataGridColumn>), 
              typeof(DataGridColumnsBehavior), 
              new UIPropertyMetadata(null, BindableColumnsPropertyChanged)); 

    /// <summary>Collection to store collection change handlers - to be able to unsubscribe later.</summary> 
    private static readonly Dictionary<DataGrid, NotifyCollectionChangedEventHandler> _handlers; 

    static DataGridColumnsBehavior() 
    { 
     _handlers = new Dictionary<DataGrid, NotifyCollectionChangedEventHandler>(); 
    } 

    private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) 
    { 
     DataGrid dataGrid = source as DataGrid; 

     ObservableCollection<DataGridColumn> oldColumns = e.OldValue as ObservableCollection<DataGridColumn>; 
     if (oldColumns != null) 
     { 
      // Remove all columns. 
      dataGrid.Columns.Clear(); 

      // Unsubscribe from old collection. 
      NotifyCollectionChangedEventHandler h; 
      if (_handlers.TryGetValue(dataGrid, out h)) 
      { 
       oldColumns.CollectionChanged -= h; 
       _handlers.Remove(dataGrid); 
      } 
     } 

     ObservableCollection<DataGridColumn> newColumns = e.NewValue as ObservableCollection<DataGridColumn>; 
     dataGrid.Columns.Clear(); 
     if (newColumns != null) 
     { 
      // Add columns from this source. 
      foreach (DataGridColumn column in newColumns) 
       dataGrid.Columns.Add(column); 

      // Subscribe to future changes. 
      NotifyCollectionChangedEventHandler h = (_, ne) => OnCollectionChanged(ne, dataGrid); 
      _handlers[dataGrid] = h; 
      newColumns.CollectionChanged += h; 
     } 
    } 

    static void OnCollectionChanged(NotifyCollectionChangedEventArgs ne, DataGrid dataGrid) 
    { 
     switch (ne.Action) 
     { 
      case NotifyCollectionChangedAction.Reset: 
       dataGrid.Columns.Clear(); 
       foreach (DataGridColumn column in ne.NewItems) 
        dataGrid.Columns.Add(column); 
       break; 
      case NotifyCollectionChangedAction.Add: 
       foreach (DataGridColumn column in ne.NewItems) 
        dataGrid.Columns.Add(column); 
       break; 
      case NotifyCollectionChangedAction.Move: 
       dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex); 
       break; 
      case NotifyCollectionChangedAction.Remove: 
       foreach (DataGridColumn column in ne.OldItems) 
        dataGrid.Columns.Remove(column); 
       break; 
      case NotifyCollectionChangedAction.Replace: 
       dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn; 
       break; 
     } 
    } 

    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); 
    } 
} 
0

तरह से मैं प्रोग्राम के रूप में कर का एक नमूना है:

public partial class UserControlWithComboBoxColumnDataGrid : UserControl 
{ 
    private Dictionary<int, string> _Dictionary; 
    private ObservableCollection<MyItem> _MyItems; 
    public UserControlWithComboBoxColumnDataGrid() { 
     _Dictionary = new Dictionary<int, string>(); 
     _Dictionary.Add(1,"A"); 
     _Dictionary.Add(2,"B"); 
     _MyItems = new ObservableCollection<MyItem>(); 
     dataGridMyItems.AutoGeneratingColumn += DataGridMyItems_AutoGeneratingColumn; 
     dataGridMyItems.ItemsSource = _MyItems; 

    } 
private void DataGridMyItems_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) 
     { 
      var desc = e.PropertyDescriptor as PropertyDescriptor; 
      var att = desc.Attributes[typeof(ColumnNameAttribute)] as ColumnNameAttribute; 
      if (att != null) 
      { 
       if (att.Name == "My Combobox Item") { 
        var comboBoxColumn = new DataGridComboBoxColumn { 
         DisplayMemberPath = "Value", 
         SelectedValuePath = "Key", 
         ItemsSource = _ApprovalTypes, 
         SelectedValueBinding = new Binding("Bazinga"), 
        }; 
        e.Column = comboBoxColumn; 
       } 

      } 
     } 

} 
public class MyItem { 
    public string Name{get;set;} 
    [ColumnName("My Combobox Item")] 
    public int Bazinga {get;set;} 
} 

    public class ColumnNameAttribute : Attribute 
    { 
     public string Name { get; set; } 
     public ColumnNameAttribute(string name) { Name = name; } 
} 
संबंधित मुद्दे