見出し画像

C# WPFで画像を分割して表示する

1. はじめに

 今回は、C# WPFを使用し、読み込んだ画像を縦32px、横32pxに分割して表示する方法をまとめる(図1)。

図1 完成イメージ

 図1は、今回の完成イメージである。任意の画像ファイルを入力すると、サイズを32px * 32pxに分割し、左側のエリアに表示する。なお、読み込む画像ファイルは、32px * 32pxの1枚の絵にしたものを対象としている。

 上記を実現するためのステップを下記に示す。

(1) png画像を入力する。
(2) 入力した画像を32px * 32pxに分割する。
(3) 分割した画像をリストに登録し、左側のエリアに表示する。

2. GUIの作成

 まず、完成イメージと同じGUIを作成する。

(1) メニューバーを作成する
 GUIにメニューバーを付け、「ファイル」→「開く」を表示する。
(2) 画面を縦列に2分割する
(3) スクロールバーを付ける

 上記をXAMLで記述すると、下記のコードとなる。

<Window x:Class="MapEditor.MainWindow"
        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"
        xmlns:local="clr-namespace:MapEditor"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Menu Grid.ColumnSpan="2">
            <MenuItem Header="ファイル">
                <MenuItem Header="開く"></MenuItem>
            </MenuItem>
        </Menu>

        <StackPanel Grid.Column="0" Margin="0,15,0,0">
            <Label Content="左側のエリア"></Label>
        </StackPanel>
        <StackPanel Grid.Column="1" Margin="0,15,0,0">
            <Label Content="右側のエリア"></Label>
        </StackPanel>
    </Grid>
</Window>

 実行すると、下記の画面となる(図2)。

図2 作成したGUI

 これで、GUIの作成が完了である。

3. png画像を入力する

 png画像の入力処理を作成する。

3.1 クリックイベントを追加する

 このGUIは、「ファイル」→「開く」ボタンを選択すると、pngファイルを読み込むこととする。追加するクリックイベントは、<MenuItem>に設定する。

・追加する箇所

<MenuItem Header="開く" Click="MenuItem_Click"></MenuItem>

 <MenuItem>にクリックイベントを設定した。これによって、ボタンをクリックしたときのイベントに合わせて処理が動作する。なお、この時点では処理の中身を記述していないため、何も動作しない。

<Window x:Class="MapEditor.MainWindow"
        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"
        xmlns:local="clr-namespace:MapEditor"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Menu Grid.ColumnSpan="2">
            <MenuItem Header="ファイル">
                <MenuItem Header="開く" Click="MenuItem_Click"></MenuItem>
            </MenuItem>
        </Menu>

        <StackPanel Grid.Column="0" Margin="0,15,0,0">
            <Label Content="左側のエリア"></Label>
        </StackPanel>
        <StackPanel Grid.Column="1" Margin="0,15,0,0">
            <Label Content="右側のエリア"></Label>
        </StackPanel>
    </Grid>
</Window>

 次に、.cs側にクリックイベント時の処理を記述する。追加する場所は、先ほど作成した"MenuItem_Click"である。

using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MapEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {

        }
    }
}

 クリックイベントは、<MenuItem>に記述した名称と同じものをメソッド名として記述する。試しに、クリックイベント内に、メッセージボックスを表示する。

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("開くボタンをクリックした。");
        }

 実行結果を図3、図4に示す。

図3 開くボタン

 開くボタンを選択すると、メッセージが表示されることがわかる。

図4 クリック処理

3.2 pngファイルを入力する

 pngファイルを読み込むためには、BitmapImageクラスを使用する。

//pngファイルを読み込む
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(openFileDialog.FileName);
bitmap.EndInit();

 まず、BitmapImageをインスタンス化し、UriSourceにファイルダイアログから開いたUriを設定する。下記が実装したコードである。

using Microsoft.Win32;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MapEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            //ファイルダイアログをオープンする
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "PNG files (*.png)|*.png";
            if (openFileDialog.ShowDialog() == true)
            {
                //pngファイルを読み込む
                BitmapImage bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.UriSource = new Uri(openFileDialog.FileName);

                bitmap.EndInit();
            }
        }
    }
}

 これで、ファイル→開くを押したときに、ファイルダイアログが現れ、選択した画像を読み込むことができる。

3.3 画面に読み込んだ画像を貼り付ける

 画面に読み込んだ画像を貼り付ける。

<Window x:Class="MapEditor.MainWindow"
        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"
        xmlns:local="clr-namespace:MapEditor"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Menu Grid.ColumnSpan="2">
            <MenuItem Header="ファイル">
                <MenuItem Header="開く" Click="MenuItem_Click"></MenuItem>
            </MenuItem>
        </Menu>

        <StackPanel Grid.Column="0" Margin="0,15,0,0">
            <Label Content="左側のエリア"></Label>
            <Image Name="LeftImage"></Image>
        </StackPanel>
        <StackPanel Grid.Column="1" Margin="0,15,0,0">
            <Label Content="右側のエリア"></Label>
        </StackPanel>
    </Grid>
</Window>

 Xamlの左側のエリアに、Imageタグを追加した。ここに先ほどの読み込んだ画像ファイルを設定すればよい。

using Microsoft.Win32;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MapEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            //ファイルダイアログをオープンする
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "PNG files (*.png)|*.png";
            if (openFileDialog.ShowDialog() == true)
            {
                //pngファイルを読み込む
                BitmapImage bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.UriSource = new Uri(openFileDialog.FileName);

                bitmap.EndInit();

                LeftImage.Source = bitmap;
            }
        }
    }
}

 実行結果は、図5となる。

図5 実行結果

 画像が非常に大きくなっているが、pngファイルを入力し、左側のエリアに表示できた。

3.3 32px * 32pxに分割して読み込む

 次は、入力した画像を32px * 32pxに分割して表示する。そのためには、Xaml側は、下記のように修正する。

(1) <ItemsControl>を追加し、複数の画像を表示できるようにする。
(2) <WrapPanel>を追加し、分割画像を横に折り返して表示する。

 <ItemsControl>は、リストを並べて表示するために使用する。今回は、画像ファイルを複数並べるため、リストに追加して表示する。

 <WrapPanel>は、コントロールを横方向に並べるために使用する。分割した画像は、横向きに並べて表示する。下記に一部抜粋したコードを示す。

        <StackPanel Grid.Column="0" Margin="0,15,0,0">
            <Label Content="左側のエリア"></Label>
            <ItemsControl Name="ImageListControl">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Horizontal"></WrapPanel>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Image Name="LeftImage" Source="{Binding}" Width="32" Height="32"></Image>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>

 <ItemsControl>の中に、WrapPanelを記述することで実現することができる。完成したコードを下記に示す。

<Window x:Class="MapEditor.MainWindow"
        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"
        xmlns:local="clr-namespace:MapEditor"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Menu Grid.ColumnSpan="2">
            <MenuItem Header="ファイル">
                <MenuItem Header="開く" Click="MenuItem_Click"></MenuItem>
            </MenuItem>
        </Menu>

        <StackPanel Grid.Column="0" Margin="0,15,0,0">
            <Label Content="左側のエリア"></Label>
            <ItemsControl Name="ImageListControl">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Horizontal"></WrapPanel>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Image Name="LeftImage" Source="{Binding}" Width="32" Height="32"></Image>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
        <StackPanel Grid.Column="1" Margin="0,15,0,0">
            <Label Content="右側のエリア"></Label>
        </StackPanel>
    </Grid>
</Window>

 次に、コード側は、下記のように修正する。
(1) 分割画像用のリストを定義する。
(2) 読み込んだ画像を32px * 32pxに分割する。
(3) 分割画像用のリストに設定し、画面に表示する。

//タイルのサイズを割ってインデックスを算出
int numTilesPerRow = bitmap.PixelWidth / 32;
int numTilesPerColumn = bitmap.PixelHeight / 32;

for (int y = 0; y < numTilesPerColumn; y++)
{
  for (int x = 0; x < numTilesPerRow; x++)
  {
    //32px * 32pxに分割する
    CroppedBitmap cb = new CroppedBitmap(bitmap, new Int32Rect(x * 32, y * 32, 32, 32));
                        
    //リストに追加する
    tiles.Add(cb);
  }
}
//画像を表示
ImageListControl.ItemsSource = tiles;

 上記は、読み込んだ画像を32px * 32pxに分割し、<ItemsControl>に表示するプログラムである。読み込んだpngファイルのサイズを32pxで分割し、配列のループ数を決定する。

 分割するためには、CroppedBitmapクラスを使用する。画像を分割後、リストに追加し、コントロールに貼り付ける。完成したコードを下記に示す。

using Microsoft.Win32;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MapEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            //ファイルダイアログをオープンする
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "PNG files (*.png)|*.png";
            if (openFileDialog.ShowDialog() == true)
            {
                //PNGファイルを読み込む
                BitmapImage bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.UriSource = new Uri(openFileDialog.FileName);
                bitmap.EndInit();

                //分割画像のリスト
                List<ImageSource> tiles = new List<ImageSource>();

                //タイルのサイズを割ってインデックスを算出
                int numTilesPerRow = bitmap.PixelWidth / 32;
                int numTilesPerColumn = bitmap.PixelHeight / 32;

                for (int y = 0; y < numTilesPerColumn; y++)
                {
                    for (int x = 0; x < numTilesPerRow; x++)
                    {
                        //32px * 32pxに分割する
                        CroppedBitmap cb = new CroppedBitmap(bitmap, new Int32Rect(x * 32, y * 32, 32, 32));
                        
                        //リストに追加する
                        tiles.Add(cb);
                    }
                }
                //画像を表示
                ImageListControl.ItemsSource = tiles;
            }
        }
    }
}

 上記を実行し、画像を入力すると、下記のように表示できる(図6)。

図6 画像の表示

 左側のエリアにキャラクター画像が32px*32pxに分割して表示されている。これで完成である。

この記事が気に入ったらサポートをしてみませんか?