2

My MVVM WPF application currently has a GridView that binds to a ViewModel property and has the columns defined in the XAML:

<ListView Grid.Row="0" ItemsSource="{Binding GroupedOrders}">
    <ListView.View>
            <GridView>
                <GridViewColumn>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding Item2, Mode=OneWay}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="Date" DisplayMemberBinding="{Binding Item1.Date, StringFormat={}{0:dd/MM/yyyy}}" />
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Item1.Name}" />
                <!-- lots more -->
            </GridView>
    </ListView.View>
</ListView>

GroupedOrders is an ObservableCollection of a Tuple of two different item types: an "Order" object and a Boolean that controls whether or not, for this particular view, it is "selected".

However, that "selected" property hasn't been modelled well. It's come to light that each Order needs multiple "selected" properties, depending on the number of dates in the order, which can be between one and five.

To model this in the UI, I need to replace that single Checkbox GridViewColumn with a dynamic construct that creates a similar Checkbox GridviewColumn for each date in the order. So GroupedOrders becomes a Tuple <Order, List<bool>> instead, and there will need to be one column for each bool in the List.

At any given instance, the size of that list will be the same for all the Orders in the grid. But if the user loads new data into the grid, the size of the list will change.

However, I cannot see how to do this in MVVM. Existing solutions seem to be for code-behind where the GridView can be grabbed as an object and manipulated on the fly. The only MVVM solution I've seen to this is to use a Converter to building the entire GridView on the fly, which would seem to be massive overkill for this situation, where there are a lot of columns in the GridView but only a small number need to be dynamic.

Is there another way?

6
  • could you please explain further about multiple "selected" properties? is it kind of Dictionary<string, bool> and each row will have the same count of the "Multiple Selected Properties" ? Commented Jul 23, 2014 at 14:02
  • @YuliamChandra Thanks! It's currently a Tuple<Order, bool> but the effect is much the same. It'll need to become a Tuple<Order, List<bool>> so that each date can be selected independently. For a given instance, each row will have the same number of dates, but it can change if the user loads some new data into the datagrid. I have edited the question to reflect this. Does it make sense? Commented Jul 23, 2014 at 14:05
  • 1
    Hmmm.. how about if States is a collection in an Order, then the column is a ListView that binds to the States? State is a class contains Name (string) and Value (bool) properties? Commented Jul 23, 2014 at 14:23
  • @YuliamChandra So ...you can create dynamic columns by putting a whole ListView inside a single column? I had no idea that was even possible! If I can get that to work properly, then yes, what you're proposing seems an excellent solution. Commented Jul 23, 2014 at 14:32
  • 1
    yes a ListView inside a GridViewColumn, then just hide the inner ListvView header and set its orientation to Horizontal. Commented Jul 23, 2014 at 14:39

1 Answer 1

2

Not sure this is what you expect, but this is what I can think of.

View

<ListView ItemsSource="{Binding Orders}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="State">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <ListView ItemsSource="{Binding States}">
                            <ListView.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"></StackPanel>
                                </ItemsPanelTemplate>
                            </ListView.ItemsPanel>
                            <ListView.Resources>
                                <Style TargetType="GridViewColumnHeader">
                                    <Setter Property="Visibility" Value="Collapsed" />
                                </Style>
                            </ListView.Resources>
                            <ListView.View>
                                <GridView>
                                    <GridViewColumn>
                                        <GridViewColumn.CellTemplate>
                                            <DataTemplate>
                                                <CheckBox IsChecked="{Binding Value}" Content="{Binding Text}" />
                                            </DataTemplate>
                                        </GridViewColumn.CellTemplate>
                                    </GridViewColumn>
                                </GridView>
                            </ListView.View>
                        </ListView>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="OrderNo" DisplayMemberBinding="{Binding OrderNo}" />
        </GridView>
    </ListView.View>
</ListView>

Code Behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Orders.Add(new Order { OrderNo = "Order001" });
        Orders.Add(new Order { OrderNo = "Order002" });
        Orders.Add(new Order { OrderNo = "Order003" });
    }
    private readonly ObservableCollection<Order> _orders = new ObservableCollection<Order>();
    public ObservableCollection<Order> Orders
    {
        get { return _orders; }
    }
}
public class Order
{
    public Order()
    {
        States.Add(new State { Text = "Is Paid", Value = false });
        States.Add(new State { Text = "Is Delivered", Value = false });
    }
    public string OrderNo { get; set; }
    private readonly ObservableCollection<State> _states = new ObservableCollection<State>();
    public ObservableCollection<State> States
    {
        get { return _states; }
    }
}
public class State
{
    public string Text { get; set; }
    public bool Value { get; set; }
}

Result

Result

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.