locked
How to create a sparsely populated GridView?

    Question

  • I'm trying to create a game which involves some tiles that can be moved around on a board, similar to checkers. I'm having some trouble constructing a board that works.

    The GridView control is pretty close to what I want, but it lays all the items in it out in a clump the upper left. I want to be able to lay them out sparsely, and for users to be able to drag pieces around and have them remain where they were dropped (but snapped into the grid). I figured I could do this by setting up a GridView with its ItemsPanel set to a Grid with its rows and columns defined, and then use some code-behind in the drag and drop events to set the Grid.Column and Grid.Row attached properties according to where they were dropped. I created a proof-of-concept with the Grid.Column and Grid.Row properties set statically in XAML, but alas, all the tiles appeared in row 0, column 0.

    The following code demonstrates the problem (MainPage.xaml from an otherwise unmodified Blank C# Windows store app created in VS2013 called 'repro4'):

    <Page
        x:Class="repro4.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:repro4"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="100" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <TextBlock Height="100"  FontSize="24">Actual</TextBlock>
            <GridView Grid.Column="0" Grid.Row="1">
                <GridView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid> <!--Is there a more efficient way to do this? -->
                            <Grid.RowDefinitions>
                                <RowDefinition Height="200" />
                                <RowDefinition Height="200" />
                                <RowDefinition Height="200" />
                                <RowDefinition Height="200" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="200" />
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </GridView.ItemsPanel>
                <GridView.Items>
                    <Polygon Grid.Row="0" Grid.Column="0" Stroke="White" Fill="Blue" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="0" Stroke="White" Fill="Green" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="1" Stroke="White" Fill="Purple" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon> 
                </GridView.Items>
            </GridView>
            <TextBlock Height="100"  FontSize="24" Grid.Column="1">Expected</TextBlock>
            <Grid Grid.Column="1" Grid.Row="1"> 
                <Grid.RowDefinitions>
                    <RowDefinition Height="200" />
                    <RowDefinition Height="200" />
                    <RowDefinition Height="200" />
                    <RowDefinition Height="200" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="200" />
                </Grid.ColumnDefinitions>
                    <Polygon Grid.Row="0" Grid.Column="0" Stroke="White" Fill="Blue" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="0" Stroke="White" Fill="Green" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="1" Stroke="White" Fill="Purple" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon> 
            </Grid>
    
        </Grid>
    </Page>
    

    Thursday, September 25, 2014 6:59 AM

Answers

  • The GridView simply doesn't work like this - as I said, it's designed for similar items to be grouped together, and the drag/drop mechanism works off of the idea as well.   This isn't like HTML where you spit out controls and then they render in the browser.  These controls are meant to go together. 

    I think you're hung up on the idea of doing it in a way that makes sense to you, but the problem is that the code just doesn't work in that manner. If it's fine with me if you want to prove me wrong, but it's your time that you're taking away from making code that actually works.

    Here's an implementation that uses blank squares, but it's a little flaky. You can work the bugs out if you wish:

     <GridView x:Name="BlockGridView" Width="600" Height="600" CanDragItems="True" CanReorderItems="True" AllowDrop="True" BorderThickness="1" BorderBrush="Coral"  SelectionMode="Single" >
                        <GridView.ItemTemplate>
                            <DataTemplate>
                                <Grid Height="185" Width="185">
                                    <Polygon Stroke="{Binding StrokeColor}" Fill="{Binding BlockColor}" StrokeThickness="2" Points="10,10 10,100 100,100 100,10" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                </Grid>
                            </DataTemplate>
                        </GridView.ItemTemplate> 
                    </GridView>
    public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
    
                ObservableCollection<DraggableBlock> Blocks = new ObservableCollection<DraggableBlock>();
                Blocks.Add(new DraggableBlock(Colors.Red));
                Blocks.Add(new DraggableBlock(Colors.Blue));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Purple));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Green));
                BlockGridView.ItemsSource = Blocks;
            }
            
        }
    
    
        public class DraggableBlock
        {
            public SolidColorBrush StrokeColor 
            {
                get
                {
                    if (BlockColor != null && blockColor != Colors.Black) return new SolidColorBrush(Colors.White);
                    else return new SolidColorBrush(Colors.Black);
                }
            }
    
            private Color blockColor;
            public SolidColorBrush BlockColor { 
                get
                {
                    return new SolidColorBrush(blockColor);
                }
            }
    
            public DraggableBlock (Color BlockColor)
            {
                blockColor = BlockColor;
            }
        }



    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Friday, October 3, 2014 7:56 PM
    Moderator

All replies

  • Gridview is definitely the wrong control to use - it's a control meant to display a collection of similar items and is meant to be viewed as a whole.

    Instead, create a collection of items (pieces), add them to the grid, assign them to different grid locations.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Thursday, September 25, 2014 7:54 PM
    Moderator
  • I realize I can do that (note that I already did), but then I wouldn't get the press, drag, and drop events. I want this to be interactive, so that a user can pick up a tile and put it somewhere else, I created a static example to demonstrate the layout engine not doing what I expect. I obviously am not trying to figure out how to create the thing on the right hand side of my example, I'm trying to create something that allows drag and drop events, and otherwise behaves like an ItemsControl, but lays its items out like my example on the right. 

    Thursday, September 25, 2014 9:36 PM
  • I guess you can go about this one of two ways:

    1) Create and handle your own drag/drop events

    2) Put items in your list which are meant to be blank and have visibility=hidden. This will allow the gridview to look like there are spaced-out items.


    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Friday, September 26, 2014 1:25 PM
    Moderator
  • I suppose that would work, but if that's what I have to do, what's the point of the ItemsPanel property of ItemsControls? It looks to me like it's for using a nonstandard panel to change the layout of the items in the control, but that doesn't work and apparently isn't the right way to do it. In what sort of situation would I use it?

    Also, I've been mucking around with a bit and discovered that the reason it isn't working is because the GridView creates a GridViewItem to contain each item in the GridView. If I modify the GridView.ItemContainerStyle to be something that sets Grid.Column, I get all the squares on the left laid out in that column. I then tried to create a data binding to the GridViewItem's Content.(Grid.Column) property, but that doesn't seem to be working. I don't really understand how data binding paths work and what (if anything) I should set Source and RelativeSource to be evaluating from the correct place (the GridViewItem that is being styled).

    Here is my latest attempt:

    <Page
        x:Class="repro4.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:repro4"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="100" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <TextBlock Height="100"  FontSize="24">Actual</TextBlock>
            <GridView Grid.Column="0" Grid.Row="1">
                <GridView.ItemContainerStyle>
                    <Style TargetType="GridViewItem">
                        <Setter Property="Grid.Column" Value="{Binding Content.(Grid.Column)}"></Setter>
                        <Setter Property="Grid.Row" Value="0"></Setter>
                    </Style>
                </GridView.ItemContainerStyle>
                <GridView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid> <!--Is there a more efficient way to do this? -->
                            <Grid.RowDefinitions>
                                <RowDefinition Height="200" />
                                <RowDefinition Height="200" />
                                <RowDefinition Height="200" />
                                <RowDefinition Height="200" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="200" />
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </GridView.ItemsPanel>
                <GridView.Items>
                    <Polygon Grid.Row="0" Grid.Column="0" Stroke="White" Fill="Blue" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="0" Stroke="White" Fill="Green" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="1" Stroke="White" Fill="Purple" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon> 
                </GridView.Items>
            </GridView>
            <TextBlock Height="100"  FontSize="24" Grid.Column="1">Expected</TextBlock>
            <Grid Grid.Column="1" Grid.Row="1"> 
                <Grid.RowDefinitions>
                    <RowDefinition Height="200" />
                    <RowDefinition Height="200" />
                    <RowDefinition Height="200" />
                    <RowDefinition Height="200" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="200" />
                </Grid.ColumnDefinitions>
                    <Polygon Grid.Row="0" Grid.Column="0" Stroke="White" Fill="Blue" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="0" Stroke="White" Fill="Green" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon>
                    <Polygon Grid.Row="1" Grid.Column="1" Stroke="White" Fill="Purple" StrokeThickness="2" Points="10,10 10,100 100,100 100,10"></Polygon> 
            </Grid>
    
        </Grid>
    </Page>
    

    • Edited by MagusOTB Saturday, September 27, 2014 11:27 PM
    Saturday, September 27, 2014 7:41 PM
  • The GridView simply doesn't work like this - as I said, it's designed for similar items to be grouped together, and the drag/drop mechanism works off of the idea as well.   This isn't like HTML where you spit out controls and then they render in the browser.  These controls are meant to go together. 

    I think you're hung up on the idea of doing it in a way that makes sense to you, but the problem is that the code just doesn't work in that manner. If it's fine with me if you want to prove me wrong, but it's your time that you're taking away from making code that actually works.

    Here's an implementation that uses blank squares, but it's a little flaky. You can work the bugs out if you wish:

     <GridView x:Name="BlockGridView" Width="600" Height="600" CanDragItems="True" CanReorderItems="True" AllowDrop="True" BorderThickness="1" BorderBrush="Coral"  SelectionMode="Single" >
                        <GridView.ItemTemplate>
                            <DataTemplate>
                                <Grid Height="185" Width="185">
                                    <Polygon Stroke="{Binding StrokeColor}" Fill="{Binding BlockColor}" StrokeThickness="2" Points="10,10 10,100 100,100 100,10" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                </Grid>
                            </DataTemplate>
                        </GridView.ItemTemplate> 
                    </GridView>
    public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
    
                ObservableCollection<DraggableBlock> Blocks = new ObservableCollection<DraggableBlock>();
                Blocks.Add(new DraggableBlock(Colors.Red));
                Blocks.Add(new DraggableBlock(Colors.Blue));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Purple));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Black));
                Blocks.Add(new DraggableBlock(Colors.Green));
                BlockGridView.ItemsSource = Blocks;
            }
            
        }
    
    
        public class DraggableBlock
        {
            public SolidColorBrush StrokeColor 
            {
                get
                {
                    if (BlockColor != null && blockColor != Colors.Black) return new SolidColorBrush(Colors.White);
                    else return new SolidColorBrush(Colors.Black);
                }
            }
    
            private Color blockColor;
            public SolidColorBrush BlockColor { 
                get
                {
                    return new SolidColorBrush(blockColor);
                }
            }
    
            public DraggableBlock (Color BlockColor)
            {
                blockColor = BlockColor;
            }
        }



    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Friday, October 3, 2014 7:56 PM
    Moderator