简体   繁体   中英

How to dynamically change grid rows and columns according to window size in WPF

I'm making an image gallery of sorts, and I'm currently loading the images into a grid dynamically via C#. If I shouldn't be using a grid to display the images, I'm more than willing to change it to a better-suited control, but it needs to be window-resizing-friendly.

When I resize the window, I want the following things to happen:

  • The images all stay the exact same size. They already fit into the grid slots while maintaining their aspect ratio
  • The grid spaces themselves are also not resized at all
  • The grid contains more or less rows/columns depending on how many can fit into the window .

This would mean that, if the window was resized to be very thin and very high, the grid would contain one column (or two, or however many are needed) and many rows to display the images in.

If it was very wide, but not high, then it would only have one (or two, or however many are needed) row(s) and many columns. Etc.

Not sure if it's needed but my code to display the images is:

        for (int i = 0; i < ImageGrid.RowDefinitions.Count; i++)
        {
            for (int j = 0; j < ImageGrid.ColumnDefinitions.Count; j++)
            {
                Image img = new Image();
                BitmapImage bitmap = new BitmapImage();

                using (var fs = new FileStream(path, FileMode.Open)) // open the image
                {
                    bitmap.BeginInit();
                    bitmap.StreamSource = fs;
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;
                    bitmap.EndInit();
                }

                bitmap.Freeze(); // bitmap isn't properly cleaned up and memory leaks can occur if this isn't here

                img.Source = bitmap;

                img.Margin = new Thickness(10);

                img.Stretch = Stretch.Uniform;

                Grid.SetRow(img, i);
                Grid.SetColumn(img, j);

                ImageGrid.Children.Add(img);
            }
        }

And my XAML for the grid is:

    <Grid ShowGridLines="True" Background="DimGray">
        <Grid.ColumnDefinitions>
            <ColumnDefinition MinWidth="175" Width="0"/>
            <ColumnDefinition MinWidth="755" Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Label Name="lblWinSize" Content="Width, Height" 
HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
        <TextBox Name="txtnbox" Style="{StaticResource CustomTxtBox}" 
TextWrapping="NoWrap" Width="Auto" Height="25" VerticalAlignment="Top" 
Margin="10, 20, 10, 0"/>

        <Separator Style="{StaticResource VerticalSeparator}" 
HorizontalAlignment="Right" Height="Auto" Width="2" Margin="0,10,0,10"/>
        <CheckBox Style="{StaticResource CustomCheckBox}" 
HorizontalAlignment="Left" Margin="10,50,0,0" VerticalAlignment="Top"/>
        <Grid Name="ImageGrid" Grid.Column="1" Margin="10,10,0,10" 
ShowGridLines="True" Background="Gray">
            <!--this is the grid where the images would go-->
        </Grid>
    </Grid>

Sorry about the indentation with that XAML, I couldn't get it to format right.

The rows/columns are defined elsewhere in the code, but I don't think I need to paste that considering it'll be replaced.

Again, if some other control is better suited, I can change to that.

You will want to use a wrap panel.

<WrapPanel Orientation="Horizontal">
    <Image Width="50" Height="50" Source="bla.png" />
    <Image Width="50" Height="50" Source="bla.png" />
    <Image Width="50" Height="50" Source="bla.png" />
    <Image Width="50" Height="50" Source="bla.png" />
</WrapPanel>

Use a WrapPanel to make layout of items fill the width of the window before adding a new row, inside of a ScrollViewer to allow scrolling vertically so the contents don't run out of viewable area.

<ScrollViewer>
  <WrapPanel HorizontalAlignment="Center">
    <!-- your items go here -->
  </WrapPanel>
</ScrollViewer>

If your images are being added dynamically, hopefully you're adding them to a collection that your view can bind to instead of directly adding them to the view in your codebehind... Use an ItemsControl and define your own template for items and container:

<ScrollViewer>
  <ItemsControl ItemsSource="{Binding Images}" HorizontalAlignment="Center">
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <Image Source="{Binding ...}" Height="150" Width="150"/>
      </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <WrapPanel />
      </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
  </ItemsControl>
</ScrollViewer>

c# :

// here set number of images you want to parse
        int qtyOfImages = 10;

        for (int i = 0; i < qtyOfImages; i++) {
            Image img = new Image();
            BitmapImage bitmap = new BitmapImage();

            using (var fs = new FileStream(path, FileMode.Open)) // open the image
            {
                bitmap.BeginInit();
                bitmap.StreamSource = fs;
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.EndInit();
            }

            bitmap.Freeze(); // bitmap isn't properly cleaned up and memory leaks can occur if this isn't here

            img.Source = bitmap;

            img.Margin = new Thickness(10);

            img.Stretch = Stretch.Uniform;

            //Grid.SetRow(img, i);
            //Grid.SetColumn(img, j);

            // set width and height so the images stay at the same size
            img.Width = 300;
            img.Height = 300;

            ImageGrid.Children.Add(img);
        }

while your xaml would be like:

 <Grid ShowGridLines="True" Background="DimGray">
        <Grid.ColumnDefinitions>
            <ColumnDefinition MinWidth="175" Width="0"/>
            <ColumnDefinition MinWidth="755" Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Label Name="lblWinSize" Content="Width, Height" 
        HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
        <TextBox Name="txtnbox"  Style="{StaticResource CustomTxtBox}"
            TextWrapping="NoWrap" Width="Auto" Height="25" VerticalAlignment="Top" 
            Margin="10, 20, 10, 0"/>

        <Separator Style="{StaticResource VerticalSeparator}" 
            HorizontalAlignment="Right" Height="Auto" Width="2" Margin="0,10,0,10"/>
        <CheckBox Style="{StaticResource CustomCheckBox}"
            HorizontalAlignment="Left" Margin="10,50,0,0" VerticalAlignment="Top"/>
        <!--<Grid Name="ImageGrid" Grid.Column="1" Margin="10,10,0,10" 
                    ShowGridLines="True" Background="Gray">
                --><!--this is the grid where the images would go--><!--
            </Grid>-->


        <!-- set size of wrap panel to your window acutal width -->
        <WrapPanel Orientation="Horizontal" Name="ImageGrid" Width="{Binding ElementName=Window, Path=ActualWidth}">

        </WrapPanel>

    </Grid>

dont forget to put Name="Window" at your window for this code to work

Hope that helps

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