2008-12-10 13 views
11

मैं डेटा के एक सेट को प्रदर्शित करने के लिए एक सूची दृश्य के ग्रिड व्यू मोड का उपयोग करना चाहता हूं जो मेरा प्रोग्राम बाहरी स्रोत से प्राप्त होगा। डेटा में दो एरे, कॉलम नामों में से एक और नियंत्रण को पॉप्युलेट करने के लिए स्ट्रिंग मानों में से एक होगा।एक गतिशील परिभाषा के साथ WPF GridView

मुझे नहीं लगता कि एक उपयुक्त वर्ग कैसे बनाया जाए जिसे मैं सूची दृश्य में आइटम के रूप में उपयोग कर सकता हूं। आइटमों को पॉप्युलेट करने का एकमात्र तरीका यह है कि इसे कॉलम का प्रतिनिधित्व करने वाले गुणों वाले वर्ग में सेट करना है, लेकिन मुझे रन-टाइम से पहले कॉलम का कोई ज्ञान नहीं है।

मैं वर्णित रूप से एक आइटम टेम्पलेट बना सकता हूं जैसा कि वर्णन किया गया है: Create WPF ItemTemplate DYNAMICALLY at runtime लेकिन यह अभी भी मुझे वास्तविक डेटा का वर्णन करने के तरीके के नुकसान के कारण छोड़ देता है।

कोई भी मदद कृतज्ञता से प्राप्त हुई।

उत्तर

11

आप GridView को GridViewColumns जोड़ सकते हैं गतिशील रूप से इस तरह की एक विधि का उपयोग कर पहली सरणी दिया:

private void AddColumns(GridView gv, string[] columnNames) 
{ 
    for (int i = 0; i < columnNames.Length; i++) 
    { 
     gv.Columns.Add(new GridViewColumn 
     { 
      Header = columnNames[i], 
      DisplayMemberBinding = new Binding(String.Format("[{0}]", i)) 
     }); 
    } 
} 

मैं मूल्यों से युक्त पंक्तियों * कॉलम लंबाई की होगी दूसरी सरणी मान। उस स्थिति में, आपके आइटम लंबाई COLUMNS के स्ट्रिंग सरणी हो सकते हैं। आप सरणी को विभाजित करने के लिए Array.Copy या LINQ का उपयोग कर सकते हैं। सिद्धांत यहां प्रदर्शित किया गया है:

<Grid> 
    <Grid.Resources> 
     <x:Array x:Key="data" Type="{x:Type sys:String[]}"> 
      <x:Array Type="{x:Type sys:String}"> 
       <sys:String>a</sys:String> 
       <sys:String>b</sys:String> 
       <sys:String>c</sys:String> 
      </x:Array> 
      <x:Array Type="{x:Type sys:String}"> 
       <sys:String>do</sys:String> 
       <sys:String>re</sys:String> 
       <sys:String>mi</sys:String> 
      </x:Array> 
     </x:Array> 
    </Grid.Resources> 
    <ListView ItemsSource="{StaticResource data}"> 
     <ListView.View> 
      <GridView> 
       <GridViewColumn DisplayMemberBinding="{Binding Path=[0]}" Header="column1"/> 
       <GridViewColumn DisplayMemberBinding="{Binding Path=[1]}" Header="column2"/> 
       <GridViewColumn DisplayMemberBinding="{Binding Path=[2]}" Header="column3"/> 
      </GridView> 
     </ListView.View> 
    </ListView> 
</Grid> 
5

धन्यवाद, यह बहुत उपयोगी है।

मैंने इसे निम्नानुसार एक गतिशील संस्करण बनाने के लिए उपयोग किया। मैं स्तंभ शीर्षकों बनाया के रूप में आप का सुझाव दिया:

public class MyCollection : ObservableCollection<List<String>> 
{ 
    public MyCollection() 
     : base() 
    { 
    } 
} 

:

<ListView Name="myListview" DockPanel.Dock="Left"/> 

मेरी डेटा रखने के लिए ObservableCollection के लिए एक आवरण वर्ग निर्मित:

private void AddColumns(List<String> myColumns) 
{ 
    GridView viewLayout = new GridView(); 
    for (int i = 0; i < myColumns.Count; i++) 
    { 
     viewLayout.Columns.Add(new GridViewColumn 
     { 
      Header = myColumns[i], 
      DisplayMemberBinding = new Binding(String.Format("[{0}]", i)) 
     }); 
    } 
    myListview.View = viewLayout; 
} 

XAML में बहुत बस ListView सेट करें और मेरी सूची दृश्य को बाध्य करें:

results = new MyCollection(); 

Binding binding = new Binding(); 
binding.Source = results; 
myListview.SetBinding(ListView.ItemsSourceProperty, binding); 

तो यह पॉप्युलेट करने के लिए, यह सिर्फ किसी भी पुराने डेटा बाहर समाशोधन और नए जोड़ने का मामला था:

results.Clear(); 
List<String> details = new List<string>(); 
for (int ii=0; ii < externalDataCollection.Length; ii++) 
{ 
    details.Add(externalDataCollection[ii]); 
} 
results.Add(details); 

संभवतः हैं यह कर की neater तरीके हैं, लेकिन यह अपने आवेदन के लिए बहुत उपयोगी है। एक बार फिर धन्यवाद।

3

यह सुनिश्चित नहीं है कि यह अभी भी प्रासंगिक है लेकिन मुझे सेलटेम्प्लेट चयनकर्ता के साथ व्यक्तिगत कोशिकाओं को स्टाइल करने का एक तरीका मिला है।

public class DataMatrixCellTemplateSelectorWrapper : DataTemplateSelector 
    { 
     private readonly DataTemplateSelector _ActualSelector; 
     private readonly string _ColumnName; 
     private Dictionary<string, object> _OriginalRow; 

     public DataMatrixCellTemplateSelectorWrapper(DataTemplateSelector actualSelector, string columnName) 
     { 
      _ActualSelector = actualSelector; 
      _ColumnName = columnName; 
     } 

     public override DataTemplate SelectTemplate(object item, DependencyObject container) 
     { 
      // The item is basically the Content of the ContentPresenter. 
      // In the DataMatrix binding case that is the dictionary containing the cell objects. 
      // In order to be able to select a template based on the actual cell object and also 
      // be able to bind to that object within the template we need to set the DataContext 
      // of the template to the actual cell object. However after the template is selected 
      // the ContentPresenter will set the DataContext of the template to the presenters 
      // content. 
      // So in order to achieve what we want, we remember the original DataContext and then 
      // change the ContentPresenter content to the actual cell object. 
      // Therefor we need to remember the orginal DataContext otherwise in subsequent calls 
      // we would get the first cell object. 

      // remember old data context 
      if (item is Dictionary<string, object>) 
      { 
       _OriginalRow = item as Dictionary<string, object>; 
      } 

      if (_OriginalRow == null) 
       return null; 

      // get the actual cell object 
      var obj = _OriginalRow[_ColumnName]; 

      // select the template based on the cell object 
      var template = _ActualSelector.SelectTemplate(obj, container); 

      // find the presenter and change the content to the cell object so that it will become 
      // the data context of the template 
      var presenter = WpfUtils.GetFirstParentForChild<ContentPresenter>(container); 
      if (presenter != null) 
      { 
       presenter.Content = obj; 
      } 

      return template; 
     } 
    } 

नोट:: यह एक सा hacky क्योंकि आप सेल के लिए उचित DataContext पाने के लिए ContentPresenter की सामग्री के साथ चारों ओर चबाना करने के लिए है (ताकि आप सेल टेम्पलेट में वास्तविक सेल आइटम करने के लिए बाध्य कर सकते हैं) है मैं DataMatrix को CodeProject आलेख से बदल दिया ताकि पंक्तियां शब्दकोश (कॉलमनाम -> सेल ऑब्जेक्ट) हों।

मैं गारंटी नहीं दे सकता कि यह समाधान कुछ तोड़ नहीं देगा या भविष्य में तोड़ नहीं देगा।नेट रिलीज यह इस तथ्य पर निर्भर करता है कि सामग्री प्रदाता टेम्पलेट को अपनी सामग्री में चुनने के बाद DataContext सेट करता है। (परावर्तक इन मामलों में एक बहुत मदद करता है :))

जब GridColumns बनाने, मैं ऐसा ही कुछ:

  var column = new GridViewColumn 
          { 
           Header = col.Name, 
           HeaderTemplate = gridView.ColumnHeaderTemplate 
          }; 
      if (listView.CellTemplateSelector != null) 
      { 
       column.CellTemplateSelector = new DataMatrixCellTemplateSelectorWrapper(listView.CellTemplateSelector, col.Name); 
      } 
      else 
      { 
       column.DisplayMemberBinding = new Binding(string.Format("[{0}]", col.Name)); 
      } 
      gridView.Columns.Add(column); 

नोट: मैं ListView विस्तार किया है इतना है कि यह एक CellTemplateSelector संपत्ति आप करने के लिए बाध्य कर सकते हैं XAML में

@Edit 15/03/2011: http://codesilence.wordpress.com/2011/03/15/listview-with-dynamic-columns/

1

पूरी तरह progmatic संस्करण:

मैं एक छोटे से लेख जो एक छोटे डेमो परियोजना संलग्न है लिखा था
 var view = grid.View as GridView; 
     view.Columns.Clear(); 
     int count=0; 
     foreach (var column in ViewModel.GridData.Columns) 
     { 
      //Create Column 
      var nc = new GridViewColumn(); 
      nc.Header = column.Field; 
      nc.Width = column.Width; 
      //Create template 
      nc.CellTemplate = new DataTemplate(); 
      var factory = new FrameworkElementFactory(typeof(System.Windows.Controls.Border)); 
      var tbf = new FrameworkElementFactory(typeof(System.Windows.Controls.TextBlock)); 

      factory.AppendChild(tbf); 
      factory.SetValue(System.Windows.Controls.Border.BorderThicknessProperty, new Thickness(0,0,1,1)); 
      factory.SetValue(System.Windows.Controls.Border.MarginProperty, new Thickness(-7,0,-7,0)); 
      factory.SetValue(System.Windows.Controls.Border.BorderBrushProperty, Brushes.LightGray); 
      tbf.SetValue(System.Windows.Controls.TextBlock.MarginProperty, new Thickness(6,2,6,2)); 
      tbf.SetValue(System.Windows.Controls.TextBlock.HorizontalAlignmentProperty, column.Alignment); 

      //Bind field 
      tbf.SetBinding(System.Windows.Controls.TextBlock.TextProperty, new Binding(){Converter = new GridCellConverter(), ConverterParameter=column.BindingField}); 
      nc.CellTemplate.VisualTree = factory; 

      view.Columns.Add(nc); 
      count++; 
     } 
1

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

+0

इस तरह मैं करना चाहता हूं लेकिन यह स्पष्ट नहीं करता कि कैसे। संलग्न प्रॉप्स और डीपी अभी भी मेरे लिए एक रहस्य हैं। किसी भी उदाहरण की सराहना की जाएगी। इस पर एक नज़र डालें (https://rachel53461.wordpress.com/2011/09/17/wpf-grids-rowcolumn-count-properties/)। क्या यह है कि यह कैसे है? – VivekDev

संबंधित मुद्दे