0

I have a 5 X 4 Grid (code below) which works as desired for the shape of my data. I recently discovered it is virtually impossible to pass a grid from the View Model to the View and bind it to another grid in XAML and still maintain the MVVM pattern - which is my goal.

The challenge here is that my presentation requires the children to be grouped in single cells with each having, 1 image, and two textblock UI elements.

DataTable, DataSet, GridView, List etc all seem to lack the ability to add multiple child elements to individual row/column cells for display. Unfortunately this is not just simply sticking an image in a column header.

Has anyone found another option for doing similar?

Thanks,

Glenn

Below is the sample grid and an image of the resulting view.

public void FormatGridCell()
{
   Random random = new Random();
   List<int> randomNumList = new List<int>();
   for (int i = 0; i < 50; i++)
      randomNumList.Add(random.Next(50));

   List<string> columHeader = new List<string>();
   columHeader.Add("Pitts");
   columHeader.Add("Vans");
   columHeader.Add("Lancair");
   columHeader.Add("Epic");

   List<string> rowHeader = new List<string>();
   rowHeader.Add("NORTH");
   rowHeader.Add("SOUTH");
   rowHeader.Add("EAST");
   rowHeader.Add("WEST");
   rowHeader.Add("CANADA");

   for (int x = 1; x < 5; x++)
   {
      var engineType= new TextBlock { Text = columHeader[x - 1] };
      engineType.SetValue(Grid.RowProperty, 0);
      engineType.SetValue(Grid.ColumnProperty, x);
      engineType.HorizontalAlignment = HorizontalAlignment.Center;
      this.airplaneGrid.Children.Add(engineType);

      for (int r = 1; r < 6; r++)
      {
         var dealerService = new TextBlock { Text = rowHeader[r - 1] };
         dealerService.SetValue(Grid.RowProperty, r);
         dealerService.SetValue(Grid.ColumnProperty, 0);
         dealerService.HorizontalAlignment = HorizontalAlignment.Center;
         this.airplaneGrid.Children.Add(dealerService);

         for (int i = 1; i < 6; i++)
         {
            // Bitmap path will be based on Type
            var modelImage = new Image { Width = 20, Height = 20 };
            var bitmapImage = new BitmapImage(new Uri(@"c:\personal\temp\dog.jpg"));
            modelImage.Source = bitmapImage;
            modelImage.SetValue(Grid.RowProperty, r);
            modelImage.SetValue(Grid.ColumnProperty, i);
            modelImage.HorizontalAlignment = HorizontalAlignment.Left;
            modelImage.VerticalAlignment = VerticalAlignment.Top;

            var mfgName = new TextBlock { Text = "Lancair IV" };
            mfgName.SetValue(Grid.RowProperty, r);
            mfgName.SetValue(Grid.ColumnProperty, i);
            mfgName.HorizontalAlignment = HorizontalAlignment.Center;

            var price = new TextBlock { Text = "$" + randomNumList[r + i] };
            price.SetValue(Grid.RowProperty, r);
            price.SetValue(Grid.ColumnProperty, i);
            price.HorizontalAlignment = HorizontalAlignment.Left;
            price.VerticalAlignment = VerticalAlignment.Center;
            price.Margin = new Thickness(30, 0, 0, 0);

            this.airplaneGrid.Children.Add(modelImage);
            this.airplaneGrid.Children.Add(mfgName);
            this.airplaneGrid.Children.Add(price);
          }
       }
    }
 }

This function is not mine. Sorry, forgot the named credit, but a fellow stackoverflow chap provided to this forum.

 public static class RandomExtensions
 {
    public static int NextDouble(
                    Random random,
                    double minValue,
                    double maxValue)
    {
       return random.Next(10, 50);
    }
 }

Sorry, I'm too low on the totem pole to submit an image, but run it for a full understanding of the intended layout.

here is the XAML to support the above.

 <Grid x:Name="airplaneGrid" ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="60" />
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="175"/>
            <ColumnDefinition Width="175"/>
            <ColumnDefinition Width="175"/>
            <ColumnDefinition Width="175"/>
            <ColumnDefinition Width="175"/>
        </Grid.ColumnDefinitions>
    </Grid>
user2284452
  • 115
  • 1
  • 11
  • `I recently discovered it is virtually impossible` - Post a screenshot of what you need. Im pretty sure there IS a way to do it while maintaining the cleanliness and separation MVVM provides. – Federico Berasategui Apr 16 '13 at 00:20
  • HighCore - Thanks, since I'm below 10 posts, I can't attach and image. If you add the above markup to your XAML, You'll see the results – user2284452 Apr 16 '13 at 00:24
  • I don't understand. Why don't you use a ListView with a custom CellTemplate for each column? – Federico Berasategui Apr 16 '13 at 01:48

2 Answers2

0

You could use another Grid (or stackpanel, or whatever type of panel) as the child element in each cell and then add the child elements to that panel

Kenneth
  • 28,294
  • 6
  • 61
  • 84
  • Kenneth - Thanks - That that could have potential, but how would adding another grid or StackPanel support the binding from the ViewModel? - Glenn – user2284452 Apr 16 '13 at 00:31
0

Ok. I placed your solution and mine side to side. It looks like this:

enter image description here

Admittedly, it requires a little bit more tweaking, but you get the main idea.

 <ListView ItemsSource="{Binding}" Grid.Column="1">
            <ListView.Resources>
                <DataTemplate x:Key="CellContentTemplate">
                    <Border BorderBrush="LightGray" BorderThickness="1">
                        <DockPanel>
                            <Image Height="20" Width="20" Source="{Binding ImageSource}" Margin="2"
                               DockPanel.Dock="Left"/>
                            <StackPanel>
                                <TextBlock Text="{Binding Value, StringFormat='${0}'}" Margin="2"/>
                                <TextBlock Text="{Binding Name}" Margin="2"/>
                            </StackPanel>
                        </DockPanel>
                    </Border>

                </DataTemplate>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    <GridViewColumn Header=""  DisplayMemberBinding="{Binding Name}"/>
                    <GridViewColumn Header="Pitts">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <ContentPresenter Content="{Binding Pitts}" ContentTemplate="{StaticResource CellContentTemplate}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Vans">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <ContentPresenter Content="{Binding Vans}" ContentTemplate="{StaticResource CellContentTemplate}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Lancair">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <ContentPresenter Content="{Binding Lancair}" ContentTemplate="{StaticResource CellContentTemplate}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Epic">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <ContentPresenter Content="{Binding Epic}" ContentTemplate="{StaticResource CellContentTemplate}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>

Code Behind (just random data):

 Random random = new Random();

        public GridSample() //Window Constructor
        {
            InitializeComponent();

            var names = new[] {"NORTH","SOUTH","EAST","WEST"};


            DataContext = names.Select(x => new GridViewModel()
                                                {
                                                    Name = x,
                                                    Epic = CreateRandomCell(),
                                                    Lancair = CreateRandomCell(),
                                                    Pitts = CreateRandomCell(),
                                                    Vans = CreateRandomCell()
                                                });
        }

        private CellViewModel CreateRandomCell()
        {
            return new CellViewModel
                       {
                           Name = "Cell" + random.Next(0, 100),
                           ImageSource = "/ChessPieces/BlackBishop.png",
                           Value = (decimal) random.Next(0, 100)
                       };
        }

ViewModels:

  public class GridViewModel
    {
        public string Name { get; set; }

        public CellViewModel Pitts { get; set; }

        public CellViewModel Vans { get; set; }

        public CellViewModel Lancair { get; set; }

        public CellViewModel Epic { get; set; }
    }

    public class CellViewModel
    {
        public string Name { get; set; }

        public string ImageSource { get; set; }

        public decimal Value { get; set; }
    }

See? pure MVVM, clean, beautiful solution.

Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154