简体   繁体   中英

Creating and filling a NxN grid in UWP Xaml

I am trying to create a UWP puzzle game, I want to cut the picture into n parts and then show the pieces in a grid.

My problem is, how to force a certain NxN style. Right now I have to maximize the window in order to see a 3x3 grid, if I shrink either side, it will converge to a 2 column, 1 column grid. Is there a way to handle this?

This is what I have done, I know the RowDefinition is manually right now, until I figure out a better way to do that.

<UserControl
    x:Class="PictureSplitter.Views.PictureView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">


        <GridView ItemsSource="{Binding Splitter.PuzzlePositions}">

            <GridView.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Red" BorderThickness="2">
                    <Grid x:Name="picGrid">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"></RowDefinition>
                            <RowDefinition Height="Auto"></RowDefinition>
                            <RowDefinition Height="Auto"></RowDefinition>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <Image Source="{Binding Piece.ImageSource}" />
                    </Grid>
                </Border>
                </DataTemplate>
            </GridView.ItemTemplate>

        </GridView>

</UserControl>

This are two example images: 想要网格式

不想要gridstyle

There are probably couple of ways to do that, here is another one. I've modified the UserControl so that it automatically adjusts items size to show them as square grid, when page size changes and/or collection changes.

The UserControl XAML code:

<UserControl
    x:Class="MyControls.MyUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyControls"
    Name="myControl">

    <GridView Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" ItemsSource="{Binding ElementName=myControl, Path=Items}"
              Width="{Binding ElementName=myControl, Path=CurrentWidth}" HorizontalAlignment="Center"
              Height="{Binding Width, RelativeSource={RelativeSource Self}}">
        <GridView.ItemContainerStyle>
            <Style TargetType="GridViewItem">
                <Setter Property="Margin" Value="0"/>
            </Style>
        </GridView.ItemContainerStyle>
        <GridView.ItemTemplate>
            <DataTemplate>
                <Border Padding="10" Width="{Binding ElementName=myControl, Path=ElementSize}" Height="{Binding ElementName=Width, RelativeSource={RelativeSource Self}}">
                    <Border BorderBrush="Red" BorderThickness="3">
                        <Image Source="ms-appx:///Assets/StoreLogo.png" Stretch="UniformToFill"/>
                    </Border>
                </Border>
            </DataTemplate>
        </GridView.ItemTemplate>
    </GridView>
</UserControl>

UserControl code behind:

public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    public IList Items
    {
        get { return (IList)GetValue(ItemsProperty); }
        set { SetValue(ItemsProperty, value); }
    }

    public static readonly DependencyProperty ItemsProperty =
        DependencyProperty.Register("Items", typeof(IList), typeof(MyUserControl),
            new PropertyMetadata(0, (s, e) =>
            {
                if (Math.Sqrt((e.NewValue as IList).Count) % 1 != 0)
                    Debug.WriteLine("Bad Collection");
            }));

    public void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (Math.Sqrt(Items.Count) % 1 != 0) Debug.WriteLine("Bad Collection");
        RaiseProperty(nameof(ElementSize));
    }

    private double currentWidth;
    public double CurrentWidth
    {
        get { return currentWidth; }
        set { currentWidth = value; RaiseProperty(nameof(CurrentWidth)); RaiseProperty(nameof(ElementSize)); }
    }

    public double ElementSize => (int)(currentWidth / (int)Math.Sqrt(Items.Count)) - 1;

    public MyUserControl()
    {
        this.InitializeComponent();
    }
}

The MainPage XAML:

<Grid>
    <local:MyUserControl x:Name="myControl" Items="{Binding MyItems}"/>
    <Button Content="Add" Click="Button_Click"/>
</Grid>

MainPage code behind:

public sealed partial class MainPage : Page
{
    private ObservableCollection<int> myItems = new ObservableCollection<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
    public ObservableCollection<int> MyItems
    {
        get { return myItems; }
        set { myItems = value; }
    }

    public MainPage()
    {
        this.InitializeComponent();
        DataContext = this;
        MyItems.CollectionChanged += myControl.Items_CollectionChanged;
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        myControl.CurrentWidth = Math.Min(availableSize.Height, availableSize.Width);
        return base.MeasureOverride(availableSize);
    }

    private void Button_Click(object sender, RoutedEventArgs e) => MyItems.Add(3);
}

The program starts with "Bad Collection" - there are 8 items, so you can't make a square grid from them, but as soon as you click the provided button - the collection's count changes to 9 and the grid should update itself.

It looks like you are doing this by way of MVVM, so I think you need to have a property for your Rows and Columns from your ViewModel. And then you need to have a Converter to supply the coordinate for your pieces .... OR an Attached property.

This will give you an idea:

<Window.Resources>
    <System:Int64 x:Key="X">3</System:Int64>
    <System:Int64 x:Key="Y">3</System:Int64>
</Window.Resources>

<Grid x:Name="myGrid" Loaded="Grid_Loaded">
    // You can bind column and row
    // <Button Content="image1" Grid.Column="{Binding column}" Grid.Row="{Binding row}"/>

    <Button Content="image1" Grid.Column="0" Grid.Row="0"/>
    <Button Content="image2" Grid.Column="1" Grid.Row="0"/>
    <Button Content="image3" Grid.Column="2" Grid.Row="0"/>

    <Button Content="image4" Grid.Column="0" Grid.Row="1"/>
    <Button Content="image5" Grid.Column="1" Grid.Row="1"/>
    <Button Content="image6" Grid.Column="2" Grid.Row="1"/>

    <Button Content="image7" Grid.Column="0" Grid.Row="2"/>
    <Button Content="image8" Grid.Column="1" Grid.Row="2"/>
    <Button Content="image9" Grid.Column="2" Grid.Row="2"/>
</Grid>

private void Grid_Loaded(object sender, RoutedEventArgs e)
{
    Int64 X = (Int64) this.FindResource("X");
    Int64 Y = (Int64) this.FindResource("Y");

    for (Int64 i = 0; i < X; i++)
    { 
        ColumnDefinition c = new ColumnDefinition();
        myGrid.ColumnDefinitions.Add(c);
    }
    for (Int64 i = 0; i < (int)Y; i++)
    {
        RowDefinition r = new RowDefinition();
        myGrid.RowDefinitions.Add(r);
    }
}

I have used a ListView with GridView as it's View property. And it is working fine.

<ListView x:Name="ImageList" Width="210" Height="210">
    <ListView.View>
       <GridView>
          <GridView.ColumnHeaderContainerStyle>
            <Style TargetType="Control">
                <Setter Property="Visibility" Value="Collapsed"/>
            </Style>
          </GridView.ColumnHeaderContainerStyle>
          <GridViewColumn>                            
             <GridViewColumn.CellTemplate>
                  <DataTemplate>
                       <Image Source="{Binding sq1}"/>
                  </DataTemplate>
             </GridViewColumn.CellTemplate>
           </GridViewColumn>
           <GridViewColumn >
             <GridViewColumn.CellTemplate>
                   <DataTemplate>
                       <Image Source="{Binding sq2}"/>
                   </DataTemplate>
              </GridViewColumn.CellTemplate>
            </GridViewColumn>

            <GridViewColumn >
                <GridViewColumn.CellTemplate>
                     <DataTemplate>
                         <Image Source="{Binding sq3}"/>
                     </DataTemplate>
                 </GridViewColumn.CellTemplate>
             </GridViewColumn>    
       </GridView>
  </ListView.View>

var imgBox = new BitmapImage(new Uri(@"/images/cellbkg.jpg", UriKind.Relative));
var source = new[] { new { sq1 = imgBox, sq2 = imgBox, sq3 = imgBox }, new { sq1 = imgBox, sq2 = imgBox, sq3 = imgBox }, new { sq1 = imgBox, sq2 = imgBox, sq3 = imgBox } };    
ImageList.ItemsSource = source;

This code produces below output, and don't get collapsed if you reduce window size :

3x3矩阵显示

If this is what you want, you can add columns dynamically using below approach. For NxN matrix, you have to add only N columns, binding will take care of rest :

        GridView view = (GridView)ImageList.View;
        view.Columns.Add(new GridViewColumn());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM