2017-04-21 39 views
1

注意:我正在使用MVVM Light ToolkitMahApps.Metro使用WPF和MVVM動態創建行和列

我已經檢查了答案,但它似乎沒有任何與我的問題有關。

我有一個網格,其列和標題應動態創建。列的數量和值未知,查看行數未知。

行中的行和數據表示數據庫表。所有數據都存在於ViewModel中。我的ViewModel中有一個ObservableCollection<ServerRow> ServerRows;

服務器行對象是一個模型,它看起來像這樣:

public class ServerRow : ObservableObject 
    { 
     private ObservableCollection<ServerColumn> _columns; 

     public ObservableCollection<ServerColumn> Columns 
     { 
      get { return _columns; } 
      set { Set(() => Columns, ref _columns, value); } 
     } 
    } 

這是一個ServerColumn類:

public class ServerColumn : ObservableObject 
    { 
     private string _name; 
     private string _type; 
     private string _value; 

     public string Name 
     { 
      get { return _name; } 
      set { Set(() => Name, ref _name, value); } 
     } 

     public string Type 
     { 
      get { return _type; } 
      set { Set(() => Type, ref _type, value); } 
     } 

     public string Value 
     { 
      get { return _value; } 
      set { Set(() => Value, ref _value, value); } 
     } 
} 

當時的想法是綁定的DataGrid到ObservableCollection<ServerRow> ServerRows;,然後生成列依賴在具有ServerColumnsServerRow對象中,該對象又具有Name(應該是列的標題),Type作爲列數據的數據類型,並且Value作爲應在每行/列中表示的值。

我的XAML是非常簡單(因爲它不是完整的,和課程 - 的不工作)

<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding ServerRows}"/> 

所以這就是爲什麼這個問題就在這裏。 如何正確編寫XAML以實現我想要做的事情?另外,我可能會設計一些東西 - 但我仍然是WPF/MVVM中的初學者,對我來說如此光禿禿的。 :/

這是結果,這很有意義,因爲網格試圖顯示單個列內的對象集合並調用其方法ToString()

enter image description here

+0

您期望輸出什麼,即您想要爲每個ServerRow顯示多少列?這些列在哪裏定義? – mm8

回答

1

我也有過這個問題。

如果你看一下在這裏完成:

https://github.com/taori/WMPR/blob/0a81bc6a6a4c6fc36edc4cbc99f0cfa8a2b8871c/src/WMPR/WMPR.Client/ViewModels/Sections/ReportEvaluationViewModel.cs#L503

您提供可重複的集合作爲ObservableCollection<object>當底層結構實際上是一個類型DynamicGridCell,它使用可在

找到一個DynamicGridCellDescriptor的

DynamicGridCell:

public class DynamicGridCell : DynamicObject, ICustomTypeDescriptor, IDictionary<string, object> 
{ 
    private readonly Dictionary<string, object> _values = new Dictionary<string, object>(); 

    AttributeCollection ICustomTypeDescriptor.GetAttributes() 
    { 
     return new AttributeCollection(); 
    } 

    string ICustomTypeDescriptor.GetClassName() 
    { 
     return nameof(DynamicGridCell); 
    } 

    string ICustomTypeDescriptor.GetComponentName() 
    { 
     return null; 
    } 

    TypeConverter ICustomTypeDescriptor.GetConverter() 
    { 
     return null; 
    } 

    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() 
    { 
     return null; 
    } 

    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() 
    { 
     return null; 
    } 

    object ICustomTypeDescriptor.GetEditor(Type editorBaseType) 
    { 
     return null; 
    } 

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents() 
    { 
     return null; 
    } 

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) 
    { 
     return null; 
    } 

    private PropertyDescriptor[] CreatePropertyDescriptors() 
    { 
     var result = new List<PropertyDescriptor>(); 
     foreach (var pair in _values) 
     { 
      result.Add(new DynamicGridCellDescriptor(pair.Key)); 
     } 

     return result.ToArray(); 
    } 

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
    { 
     var result = new PropertyDescriptorCollection(CreatePropertyDescriptors()); 
     return result; 
    } 

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) 
    { 
     var result = new PropertyDescriptorCollection(CreatePropertyDescriptors()); 
     return result; 
    } 

    object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) 
    { 
     return this; 
    } 

    public IEnumerator GetEnumerator() 
    { 
     return _values.GetEnumerator(); 
    } 

    IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() 
    { 
     return _values.GetEnumerator(); 
    } 

    void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) 
    { 
     _values.Add(item.Key, item.Value); 
    } 

    void ICollection<KeyValuePair<string, object>>.Clear() 
    { 
     _values.Clear(); 
    } 

    bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) 
    { 
     return _values.Contains(item); 
    } 

    void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) 
    { 
    } 

    bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) 
    { 
     if (_values.ContainsKey(item.Key)) 
     { 
      _values.Remove(item.Key); 
      return true; 
     } 

     return false; 
    } 

    public int Count => _values.Count; 

    bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false; 

    public bool ContainsKey(string key) 
    { 
     return _values.ContainsKey(key); 
    } 

    public void Add(string key, object value) 
    { 
     _values.Add(key, value); 
    } 

    bool IDictionary<string, object>.Remove(string key) 
    { 
     return _values.Remove(key); 
    } 

    public bool TryGetValue(string key, out object value) 
    { 
     return _values.TryGetValue(key, out value); 
    } 

    public object this[string key] 
    { 
     get { return _values[key]; } 
     set 
     { 
      if (_values.ContainsKey(key)) 
      { 
       _values[key] = value; 
      } 
      else 
      { 
       _values.Add(key, value); 
      } 
     } 
    } 

    public ICollection<string> Keys => _values.Keys; 
    public ICollection<object> Values => _values.Values; 
} 

DynamicGrid CellDescriptor

public class DynamicGridCellDescriptor : PropertyDescriptor 
    { 
     public DynamicGridCellDescriptor(string name) : base(name, null) 
     { 
     } 

     public override bool CanResetValue(object component) 
     { 
      return true; 
     } 

     public override object GetValue(object component) 
     { 
      return ((DynamicGridCell) component)[Name]; 
     } 

     public override void ResetValue(object component) 
     { 
      ((DynamicGridCell) component)[Name] = null; 
     } 

     public override void SetValue(object component, object value) 
     { 
      ((DynamicGridCell) component)[Name] = value; 
     } 

     public override bool ShouldSerializeValue(object component) 
     { 
      return false; 
     } 

     public override Type ComponentType => typeof(DynamicGridCell); 
     public override bool IsReadOnly => false; 
     public override Type PropertyType => typeof(object); 
    } 

只要確保你綁定到該屬性是ObservableCollection<object>反正型的 - 否則我自動網格列代沒有工作。

+0

我試過看代碼,但我不知道我明白髮生了什麼事。 您有一個名爲DynamicGridCell的自定義控件? 我不明白如何使用它。我已經綁定了一個ObservableCollection,我可以將它的訪問類型更改爲對象。 – Desomph

+0

這不是一個自定義控件。僅僅是一個定製的結構,可以讓你定義不定數量的列。像cell [「column1」] =「value1」; cell [「column2」] =「value2」; 每個索引器都會生成一列,並將在您的網格中呈現。你創建的每個細胞對象將變成一排 – Dbl

+0

對於沒有答案的道歉,但在週末有很多工作。 是的,這是我的答案!非常感謝! 但是,我仍然不明白它是如何工作的,那是在煩擾我。 您有沒有機會分享一些資源,從而找出如何獲得解決方案?將不勝感激:) – Desomph

0

你有一些邏輯問題。

當您設置一個DataGrid邊界集合的ItemsSource將用於創建行,如果你不改變它的屬性AutoGenerateColumns設置爲true。在這種情況下,DataGrid將爲綁定集合中的每個屬性生成一列,這正是您的示例中發生的情況。 您將屬性'Columns'綁定到實例並獲取名爲'Columns'的DataGrid列。並且您獲得儘可能多的行,因爲您在此屬性中顯示爲'(Collection)'的條目,因爲ServerColumn繼承自ObservableObject

您可以將AutoGenerateColumns設置爲false,並且必須由您自己創建列;通常在xaml =>硬編碼。

如果你真的想動態生成列,你必須編寫自己的邏輯來創建和綁定列。我曾經這樣做過,如果你想讓它變得通用,那就很麻煩了。 如果你想要一個DataGrid動態列,用戶可以更改值更加棘手,那麼只讀一個。

一種方法可能是使用ObservableCollection<string>作爲列名,另一種作法是存儲每行視圖模式的ObservableCollection

+0

用戶不會更改任何內容,網格僅用於表示結果(它是隻讀的)。 另外,如果它能夠緩解我在設計這方面的工作,我不會迴避代碼背後的某些行。但它必須保持活力。 – Desomph

+0

就像我說的:如果你想讓它動態化,你必須自己添加列。例如在行爲或附加屬性中。您需要DataGrid的實例來添加列及其綁定。編輯我的答案給你一個想法。 –

0

如果兩行列真正需要的是動態的,你最好的選擇是使用兩個嵌套ItemControl S,外一個代表行內一個列:

<ItemsControl ItemsSource="{Binding Rows}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <ItemsControl ItemsSource="{Binding Columns}" ItemTemplateSelector="{StaticResource ColumnTemplateSelector}"> 
       <ItemsControl.ItemsPanel> 
        <ItemsPanelTemplate> 
         <StackPanel Orientation="Horizontal"/> 
        </ItemsPanelTemplate> 
       </ItemsControl.ItemsPanel> 
      </ItemsControl> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

這可以讓你顯示不同類型的列,通過定義一個模板選擇,可能看起來有點類似以下內容:

public class ColumnTemplateSelector : DataTemplateSelector 
{ 
    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     var column = item as ServerColumn; 
     switch (column.Type) 
     { 
      case "Text": 
       return TextTemplate; 
      case "Number": 
       return NumberTemplate; 
      case "Image": 
       return ImageTemplate; 
     } 
     // Fallback 
     return TextTemplate; 
    } 

    public DataTemplate TextTemplate { get; set; } 
    public DataTemplate NumberTemplate { get; set; } 
    public DataTemplate ImageTemplate { get; set; } 
} 

...根據每個列的類型,不同的模板將引用(所有這些模板顯然需要在某個地方定義並引用爲StaticResource。 (這甚至可以輕鬆地創建多變的(不是隻讀)的網格。)

需要注意的是,不是外ItemsControl,你當然可以用ListView或從ItemsControl派生的任何其他控制。例如,如果您需要自動滾動,則使用ListView可能會很有用。