見出し画像

C# WPFで分割した画像に枠を付ける方法

1. はじめに

 今回は、C#とWPFを用いて32px x 32pxに分割した画像を表示し、選択した画像に選択枠を付ける方法を説明する。

 選択枠を表示する目的は、ユーザが画像を選択した際、何を選択したかわからなくなるためである。選択枠を表示することで、自分の意図した画像かどうかを一発で確認できる。下記が今回の完成イメージである(図1)。

図1 完成イメージ

 図1に示す通り、表示した画像に赤い選択枠が表示できていることがわかる。これをクリックした画像に合わせて表示する。

2. 作成済みの部分を作成する

 作成済みのプログラムを作成する。

・「ファイル」→「開く」を選択すると、画像を読み込み、32px * 32pxに分割して左側のエリアに表示する。 

 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="開く" 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>

 cs側は下記のように作成する。

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;
            }
        }
    }
}

 実行し、「ファイル」→「開く」を選択し、画像を読み込むと、下記の画面のとおり32px*32pxの画像が分割して表示される(図2)。

図2 実行結果

3. 選択した画像に枠を付ける

 今回は、分割した画像を選択すると、選択した画像に選択済み枠を付ける。これによって、ユーザ側がどの画像を選択したか把握することができる。

3.1 処理の流れ

 処理の流れを下記に示す。

(1) 予め選択済み枠を設定する。ただし、枠の太さを0ポイントとしてユーザに見えないようにする。
(2) ユーザが画像を選択したとき、クリックイベントを発行する。
(3) クリックイベント実行時に、選択済み枠を表示する。
(4) 別の画像を選択したときは、前回選択した選択枠の太さを0ポイントに戻す。

3.2 画面の作成

 画像を選択した際に、選択済み枠を付ける画面を作成する。枠を付けるには、<Border>を作成する。今回必要なプロパティは下記の2つである。

・BorderThickness:枠の太さ
・BorderBrush:枠の色

 BorderThicknessは、枠の太さを設定するプロパティである。これを増やすほど枠の太さが増していく。
 BorderBrushは、枠の色である。例えば、Redと設定しておくと、枠が赤色になる。

 上記を踏まえ、下記のコードに変更する。

<Border x:Name="ImageBorder" BorderThickness="0" BorderBrush="Red">
    <Image Name="LeftImage" Source="{Binding}" Width="32" Height="32" MouseUp="Image_MouseUp"></Image>
</Border>

 上記は、分割した画像に選択済み枠を表示するコードである。BorderThicknessを0と設定することで、ユーザが選択するまでは選択済み枠は表示されない。また、選択済み枠は、赤色で表示する。

 最終的な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="開く" 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>
                        <Border x:Name="ImageBorder" BorderThickness="0" BorderBrush="Red">
                            <Image Name="LeftImage" Source="{Binding}" Width="32" Height="32" MouseUp="Image_MouseUp"></Image>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
        <StackPanel Grid.Column="1" Margin="0,15,0,0">
            <Label Content="右側のエリア"></Label>
        </StackPanel>
    </Grid>
</Window>

3.3 処理の追加

 次は、cs側の処理を作成する。

        private void Image_MouseUp(object sender, RoutedEventArgs e)
        {
            //前回選択された枠をリセット
            if (_selectedBorder != null) {
                _selectedBorder.BorderThickness = new Thickness(0);
            }
            //選択した画像の選択済み枠を取得する
            var image = sender as Image;
            var border = (Border)image.Parent;

            //選択済み枠の太さを1に設定する
            border.BorderThickness = new Thickness(1);
            _selectedBorder = border;
        }

 処理の流れを下記に示す。

(1) 前回の選択済み枠の太さを0にして、表示しないようにする。
(2) 選択した画像のBorderプロパティを取得する。
(3) 選択済み枠1に設定し、枠を表示する。

 最終的なコードを下記に示す。

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
{
    public partial class MainWindow : Window
    {
        private Border _selectedBorder;

        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;
            }
        }
        private void Image_MouseUp(object sender, RoutedEventArgs e)
        {
            //前回選択された枠をリセット
            if (_selectedBorder != null) {
                _selectedBorder.BorderThickness = new Thickness(0);
            }
            //選択した画像の選択済み枠を取得する
            var image = sender as Image;
            var border = (Border)image.Parent;

            //選択済み枠の太さを1に設定する
            border.BorderThickness = new Thickness(1);
            _selectedBorder = border;
        }
    }
}

 実行すると、下記のように選択済みの画像に選択済み枠が表示されていることがわかる(図3)。

図3 実行結果

 図3と違う画像を選択すると、図4に示す通り選択した画像に選択枠が表示する。

図4 別の画像に選択枠が付く様子

4. 終わりに

 今回は、読み込んだ分割画像の選択時に赤い表示枠を表示するプログラムを作成した。表示枠があることでユーザがどの画像が選択したかわかるので、より便利になった。

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