Karol_P
Karol_P

Reputation: 265

Dynamic generate column mvvm

I try to make a ListView with dynamic generation of column. I use mvvm patern. How i can implement this? In this momemt I have only static columns.

<ListView ItemsSource="{Binding ProblemProducts}"
                  Grid.Row="1" Grid.RowSpan="4" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="4">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Spisujący" DisplayMemberBinding="{Binding _spisujacy}" Width="auto"/>
                    <GridViewColumn Header="Miejsce składowania" DisplayMemberBinding="{Binding MiejsceSkladowania}" Width="auto"/>
                    <GridViewColumn Header="Typ spisu" DisplayMemberBinding="{Binding _typSpisu}" Width="auto"/>
                    <GridViewColumn Header="Kod" DisplayMemberBinding="{Binding Kod}" width="auto"/>
                </GridView>
            </ListView.View>
        </ListView>

Upvotes: 24

Views: 17348

Answers (2)

Sergei B.
Sergei B.

Reputation: 3227

You can create GridView with appropriate columns dynamically using converter. Here is working example:

App screen shot

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:WpfApplication1="clr-namespace:WpfApplication1" 
        mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        d:DesignHeight="189" d:DesignWidth="312" Width="300" Height="300">
    <Window.Resources>
        <WpfApplication1:ConfigToDynamicGridViewConverter x:Key="ConfigToDynamicGridViewConverter" />
    </Window.Resources>
    <ListView ItemsSource="{Binding Products}" View="{Binding ColumnConfig, Converter={StaticResource ConfigToDynamicGridViewConverter}}"/>    
</Window>

MainWindow.xaml.cs

using System.Collections.Generic;
using System.Windows;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }

    public class ViewModel
    {
        public ColumnConfig ColumnConfig { get; set; }
        public IEnumerable<Product> Products { get; set; }

        public ViewModel()
        {
            Products = new List<Product> { new Product { Name = "Some product", Attributes = "Very cool product" }, new Product { Name = "Other product", Attributes = "Not so cool one" } };
            ColumnConfig = new ColumnConfig { Columns = new List<Column> { new Column { Header = "Name", DataField = "Name" }, new Column { Header = "Attributes", DataField = "Attributes" } } };
        }
    }

    public class ColumnConfig
    {
        public IEnumerable<Column> Columns { get; set; }
    }

    public class Column
    {
        public string Header { get; set; }
        public string DataField { get; set; }
    }

    public class Product
    {
        public string Name { get; set; }
        public string Attributes { get; set; }
    }
}

ConfigToDynamicGridViewConverter.cs

using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApplication1
{
    public class ConfigToDynamicGridViewConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var config = value as ColumnConfig;
            if (config != null)
            {
                var gridView = new GridView();
                foreach (var column in config.Columns)
                {
                    var binding = new Binding(column.DataField);
                    gridView.Columns.Add(new GridViewColumn {Header = column.Header, DisplayMemberBinding = binding});
                }
                return gridView;
            }
            return Binding.DoNothing;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}

Upvotes: 64

Claus Dr&#230;by
Claus Dr&#230;by

Reputation: 11

Thanks Sergei, for a marvelous answer.

I used it in a slightly different form, since I needed to add columns with non-text datatypes.

So the following modification to Sergei's answer allows you to have ContentControl wrappers on the data values. They will then be rendered according to the DataTemplates defined for the values in each cell.

The column will be wrapped if ContentControlDataField is used rather than TextDataField (originally DataField):

    public class ConfigToDynamicGridViewConverter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        var config = value as ColumnConfig;
        if (config != null) {
            var grdiView = new GridView();
            foreach (var column in config.Columns) {
                bool cc = !string.IsNullOrEmpty(column.ContentControlDataField);
                var binding = new Binding(cc ? column.ContentControlDataField : column.TextDataField);
                if (cc) {
                    var template = new DataTemplate();
                    var fact = new FrameworkElementFactory(typeof(ContentControl));
                    fact.SetBinding(ContentControl.ContentProperty, binding);
                    template.VisualTree = fact;
                    grdiView.Columns.Add(new GridViewColumn {Header = column.Header, CellTemplate = template});
                } else
                    grdiView.Columns.Add(new GridViewColumn {Header = column.Header, DisplayMemberBinding = binding});
            }
            return grdiView;
        }
        return Binding.DoNothing;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        throw new NotSupportedException();
    }
}

public class ColumnConfig {
    public IEnumerable<Column> Columns { get; set; }
}

public class Column {
    public string Header { get; set; }
    public string TextDataField { get; set; }
    public string ContentControlDataField { get; set; }
}

Upvotes: 1

Related Questions