• WPF学习系列 游戏-选张图片做成9宫格拼图


    今天要学习一个拼图项目。

    目标是传入一张图片,然后将它分成9份,去掉一份,鼠标点击进行拼图。

    源文件结构很简单

    第一步、新建项目

    这一步没什么好说的,新建一个项目就跟源文件结构一样了

     第二步、页面布局(.xaml文件)

    看下源文件

    控件有 DockPanel Grid Button三个然后设置了Grid有三列和三行。DockPannel暂时不知道有什么用,所以我先不忙加。然后我就报错了

    原来 xaml是用的xml格式。button外面没有双标签包围,不能识别,所以报错。所以外面再加个标签包裹就行了,如果加DockPanel标签就和源文件一样了,此处为了明白DockPane有什么用,所以还是用Grid,看等会儿会不会报错。我现在的代码是

    第二步、编写点击按钮选图片的功能

     这个帖子上周就开始写了,但是做了一半又去研究c++了。c++研究了一段时间,忽然明白我为什么要编程了。我编程不是对计算机有兴趣,不是为了0和1。我学计算机和程序只是为了做东西。所以又回过头来继续写这个系列,之后的内容我不会再抓细节,有些东西,能看懂就行了。记不住也没关系,要用的时候再查就是了。将项目做出来之后,我还要将它做成我喜欢的样子,而不是做成跟源代码一样。

     点击按钮要做两件事

    1、弹出文件选择对话框,选择图片。

    2、选择图片后生成拼图

    下面是选择图片的代码

    OpenFileDialog ofd = new OpenFileDialog();                          // 需要引用Microsoft.Win32.
    ofd.Filter      = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG";  //支持的图片格式
    ofd.Multiselect   = false;                                   //不允许多选
    if (ofd.ShowDialog()!=true)                                   //ofd.ShowDialog()可能有三个值 true flase null
    {
      return;
    }
    try
    {
    
      BitmapImage image = new BitmapImage(new Uri(ofd.FileName, UriKind.RelativeOrAbsolute));
      Image img = new Image { Source = image }; 
    
      //这里写创建拼图的代码 
    }
    catch { MessageBox.Show("Couldnt load the image file " + ofd.FileName); }
    选择图片

      

    生成拼图 第一步是把图片分成9块,并填充相应区域的图像

    这个有点复杂,源码用了很多方法,我习惯拆出来作为一个类单独写。

       /// <summary>
        /// 拼图生成类
        /// 传入一张图片 生成一个9*9的图片集合
        /// </summary>
        public class PuzzleForImage
        {
            BitmapImage     _image;                                             //原图片
            public List<Rectangle> initialUnallocatedParts = new List<Rectangle>();//要返回的拼图集合
            /// <summary>
            /// 新建对象时传入原图片
            /// </summary>
            /// <param name="image"></param>
            public PuzzleForImage(BitmapImage image)
            {
                _image = image;
           //创建拼图 }   }

    第一步:写个子方法,根据起点和图片宽高绘制矩形。然后调用9次,得到整个拼图集合

    第二步:将9张中的8张拼图随机排列,这里选前八张

    第三步:再添加块空白的拼图

    第四步:添加鼠标点击移动事件

    到这一步,源码部分就结束了,自己添加了个判断成功的代码 在方块中的点击事件中执行。

    下面是我的代码。

    /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// 挑选图片生成拼图
            /// </summary>
            private void BtnPickImg_Click(object sender, RoutedEventArgs e)
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter         = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG";//支持的图片格式
                ofd.Multiselect    = false;//不允许多选
    
                if (ofd.ShowDialog() != true)
                {
                    return;
                }
                try
                {
                    BitmapImage    image  = new BitmapImage(new Uri(ofd.FileName, UriKind.RelativeOrAbsolute));
                    //Image img = new Image { Source = image };
                    PuzzleForImage puzzle = new PuzzleForImage(image);//创建拼图
                    puzzle.SetGrid(GridImg);
                }
                catch
                {
                    MessageBox.Show("不支持该文件: " + ofd.FileName);
                }
            }
        }
    MainWindow.xaml.cs
        /// <summary>
        /// 拼图生成类
        /// 传入一张图片 生成一个9*9的图片集合
        /// </summary>
        public class PuzzleForImage
        {
            BitmapImage            _image;                                         //原图片
            List<Rectangle>        initialUnallocatedParts = new List<Rectangle>();//要返回拼图集合
            public List<Rectangle> allocatedParts = new List<Rectangle>();         //被打乱后的图片
            int[] map = new int[9];                                                //游戏地图 判断是否成功
    
            /// <summary>
            /// 新建对象时传入原图片
            /// </summary>
            /// <param name="image"></param>
            public PuzzleForImage(BitmapImage image)
            {
                _image = image;
                CreatePuzzleForImage();
            }
    
            /// <summary>
            /// 将拼图放到UI中
            /// </summary>
            /// <param name="GridImg"></param>
            public void SetGrid(Grid GridImg)
            {
                GridImg.Children.Clear();
                GridImg.Height = _image.Height / _image.Width * GridImg.Width;
                int index = 0;
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        allocatedParts[index].SetValue(   Grid.RowProperty, i);
                        allocatedParts[index].SetValue(Grid.ColumnProperty, j);
                        GridImg.Children.Add(allocatedParts[index]);
                        index++;
                    }
                }
            }
    
            /// <summary>
            /// 创建拼图
            /// </summary>
            private void CreatePuzzleForImage()
            {
                #region 拼图容器初始化
                initialUnallocatedParts.Clear();
                allocatedParts.Clear();
                #endregion
    
                #region 创建前8个拼图
                double width  = 1.0 / 3;//每个拼图的宽度
                double height = 1.0 / 3;//每个拼图的高度
                //row0
                CreateImagePart(         0,          0, width, height);
                CreateImagePart(     width,          0, width, height);
                CreateImagePart( 2 * width,          0, width, height);
                //row1
                CreateImagePart(         0,     height, width, height);
                CreateImagePart(     width,     height, width, height);
                CreateImagePart( 2 * width,     height, width, height);
                //row2
                CreateImagePart(         0, 2 * height, width, height);
                CreateImagePart(     width, 2 * height, width, height);
                //CreateImagePart( 2 * width, 2 * height, width, height);
                #endregion
                
                //随机排列
                RandomizeTiles();
    
                //创建白色方块
                CreateBlankRect();
            }
    
            /// <summary>
            /// 绘制一个矩形
            /// 创建一个图像画刷使用X / Y /宽度/高度参数创建时,只显示图像的一部分。这是ImageBrush用来填充矩形,添加到未分配的矩形的内部表
            /// </summary>
            /// <param name="x">起点x</param>
            /// <param name="y">起点y</param>
            /// <param name="width"></param>
            /// <param name="height"></param>
            private void CreateImagePart(double x, double y, double width, double height)
            {
                #region 定义笔刷
                ImageBrush ib   = new ImageBrush();
                //ib.Stretch = Stretch.UniformToFill;                  //裁剪已适应屏幕 这个不能要,会造成图片不拉伸对不上
                ib.ImageSource  = _image;
                ib.Viewbox      = new Rect(x, y, width, height);
                ib.ViewboxUnits = BrushMappingMode.RelativeToBoundingBox; //按百分比设置宽高
                ib.TileMode     = TileMode.None;                          //按百分比应该不会出现 image小于要切的值的情况
                #endregion
    
                #region 定义矩形
                Rectangle rectPart           = new Rectangle();
                rectPart.Fill                = ib;
                rectPart.Margin              = new Thickness(0);
                rectPart.HorizontalAlignment = HorizontalAlignment.Stretch;
                rectPart.VerticalAlignment   = VerticalAlignment.Stretch;
                rectPart.MouseDown          += new MouseButtonEventHandler(rectPart_MouseDown);//添加矩形点击事件
                #endregion
                initialUnallocatedParts.Add(rectPart);                                         //将矩形添加到拼图集合
            }
    
            /// <summary>
            /// 将 图片打乱
            /// </summary>
            private void RandomizeTiles()
            {
                Random rand = new Random();
                for (int i = 0; i < 8; i++)
                {
                    int index = 0;
                    //if (initialUnallocatedParts.Count > 1)
                    //{
                    //    index = (int)(rand.NextDouble() * initialUnallocatedParts.Count);
                    //}
                    index = (int)(rand.NextDouble() * initialUnallocatedParts.Count);
                    while(initialUnallocatedParts[index] == null)
                    {
                        index = (int)(rand.NextDouble() * initialUnallocatedParts.Count);
                    }
                    allocatedParts.Add(initialUnallocatedParts[index]);
                    //initialUnallocatedParts.RemoveAt(index); // 移除图片
                    initialUnallocatedParts[index] = null;
                    map[i] = index;                          // 添加地图
                }
            }
    
            /// <summary>
            /// 再创建一个空白矩形
            /// </summary>
            private void CreateBlankRect()
            {
                Rectangle rectPart           = new Rectangle();
                rectPart.Fill                = new SolidColorBrush(Colors.White);
                rectPart.Margin              = new Thickness(0);
                rectPart.HorizontalAlignment = HorizontalAlignment.Stretch;
                rectPart.VerticalAlignment   = VerticalAlignment.Stretch;
                allocatedParts.Add(rectPart);
                map[8] = 8;
            }
    
            /// <summary>
            /// 方块点击事件
            /// </summary>
            private void rectPart_MouseDown(object sender, MouseButtonEventArgs e)
            {
                //get the source Rectangle, and the blank Rectangle
                //NOTE : Blank Rectangle never moves, its always the last Rectangle
                //in the allocatedParts List, but it gets re-allocated to 
                //different Gri Row/Column
                Rectangle rectCurrent = sender as Rectangle;                        //当前方块
                Rectangle rectBlank   = allocatedParts[allocatedParts.Count - 1];   //缺省方块
    
                //得到白块和缺省方块的位置
                int currentTileRow  = (int)rectCurrent.GetValue(Grid.RowProperty);
                int currentTileCol  = (int)rectCurrent.GetValue(Grid.ColumnProperty);
                int currentBlankRow = (int)rectBlank.GetValue(Grid.RowProperty);
                int currentBlankCol = (int)rectBlank.GetValue(Grid.ColumnProperty);
    
                //白块能移动的四个位置
                List<PossiblePositions> posibilities = new List<PossiblePositions>();
                posibilities.Add(new PossiblePositions { Row = currentBlankRow - 1, Col = currentBlankCol });
                posibilities.Add(new PossiblePositions { Row = currentBlankRow + 1, Col = currentBlankCol });
                posibilities.Add(new PossiblePositions { Row = currentBlankRow, Col = currentBlankCol - 1 });
                posibilities.Add(new PossiblePositions { Row = currentBlankRow, Col = currentBlankCol + 1 });
    
                //检查该方块是否能点击(白块能移动到当前方块的位置)
                bool validMove = false;
                foreach (PossiblePositions position in posibilities)
                    if (currentTileRow == position.Row && currentTileCol == position.Col)
                        validMove = true;
    
                //only allow valid move
                if (validMove)
                {
                    //交换位置
                    rectCurrent.SetValue(   Grid.RowProperty, currentBlankRow);
                    rectCurrent.SetValue(Grid.ColumnProperty, currentBlankCol);
    
                    rectBlank  .SetValue(   Grid.RowProperty, currentTileRow);
                    rectBlank  .SetValue(Grid.ColumnProperty, currentTileCol);
    
                    //更新地图
                    int indexCur   = currentTileRow * 3 + currentTileCol;
                    int indexBlank = currentBlankRow * 3 + currentBlankCol;
                    int temp = map[indexCur];
                    map[indexCur] = map[indexBlank];
                    map[indexBlank] = temp;
    
                    //判断是否成功
                    if (isSuccess())
                    {
                        MessageBox.Show("成功了,好棒啊!");
                    }
                }
            }
    
            /// <summary>
            /// 验证是否游戏成功
            /// </summary>
            /// <returns></returns>
            private bool isSuccess()
            {
                for (int i = 0; i < 9; i++)
                {
                    if(map[i]!=i)
                    {
                        return false;
                    }
                }
                return true;
            }
        }
    
        /// <summary>
        /// Simply struct to store Row/Column data
        /// </summary>
        struct PossiblePositions
        {
            public int Row { get; set; }
            public int Col { get; set; }
        }
    PuzzleForImage.cs

    运行效果:

    还存在的问题:

    1、现在图片会被拉伸,暂时没想到好的办法。

    2、会随机一些拼不出来的拼图

    ------------------------------分割线2016-8-3 16:47---------------------------------

    图片拉伸问题已经解决

    @曙光闪现
    [quote]图片拉伸应该只能通过限制窗体的缩放按图片的比例缩放了。之前搞过9path原理就是类似这个九宫格[/quote]

    下面是代码

          /*------------------------------------------------------------------
                     *如果选择的图片宽高比例比屏幕的宽高比例大,则窗体和图片区域宽度采用600,高度等比缩放
                     * 反之,图片区域高度采用600,宽度等比算出,但是窗体宽度就不等比缩放了(照顾button)
                     ------------------------------------------------------------------*/
                    double imgWidthHeightRatio    = image.Width / image.Height;//图片 宽高比
                    double windowWidthHeightRatio =  600/ 600;//默认屏幕宽高比
                    this.Width                    = 600;
                    if (imgWidthHeightRatio > windowWidthHeightRatio)
                    {
                        GridImg.Width = 600;
                        GridImg.Height = GridImg.Width / imgWidthHeightRatio;
                        this.Height = GridImg.Height + 50;
                    }
                    else
                    {
                        this.Height = 650;//button占了50
                        GridImg.Height = 600;
                        GridImg.Width = GridImg.Width * imgWidthHeightRatio;
                    }
    设置宽高

    更新了MainWindow.xaml.cs文件 和 MainWindow.xaml文件

    下面是最新的代码

    using Microsoft.Win32;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    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 Puzzle
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// 挑选图片生成拼图
            /// </summary>
            private void BtnPickImg_Click(object sender, RoutedEventArgs e)
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter         = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG";//支持的图片格式
                ofd.Multiselect    = false;//不允许多选
    
                if (ofd.ShowDialog() != true)
                {
                    return;
                }
                try
                {
                    BitmapImage    image  = new BitmapImage(new Uri(ofd.FileName, UriKind.RelativeOrAbsolute));
                    #region 设置宽高
                    /*------------------------------------------------------------------
                     *如果选择的图片宽高比例比屏幕的宽高比例大,则窗体和图片区域宽度采用600,高度等比缩放
                     * 反之,图片区域高度采用600,宽度等比算出,但是窗体宽度就不等比缩放了(照顾button)
                     ------------------------------------------------------------------*/
                    double imgWidthHeightRatio    = image.Width / image.Height;//图片 宽高比
                    double windowWidthHeightRatio =  600/ 600;//默认屏幕宽高比
                    this.Width                    = 600;
                    if (imgWidthHeightRatio > windowWidthHeightRatio)
                    {
                        GridImg.Width = 600;
                        GridImg.Height = GridImg.Width / imgWidthHeightRatio;
                        this.Height = GridImg.Height + 50;
                    }
                    else
                    {
                        this.Height = 650;//button占了50
                        GridImg.Height = 600;
                        GridImg.Width = GridImg.Width * imgWidthHeightRatio;
                    }
                    #endregion
                    //Image img = new Image { Source = image };
                    PuzzleForImage puzzle = new PuzzleForImage(image);//创建拼图
                    puzzle.SetGrid(GridImg);
                }
                catch
                {
                    MessageBox.Show("不支持该文件: " + ofd.FileName);
                }
            }
        }
    }
    MainWindow.xaml.cs
    <Window x:Class="Puzzle.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="650" Width="600">
        <Grid>
            <Button x:Name="BtnPickImg" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="560" RenderTransformOrigin="-0.459,-0.974" Click="BtnPickImg_Click"/>
            <Grid x:Name="GridImg" Margin="0,50,0,0" Background="#eee">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
            </Grid>
        </Grid>
    </Window>
    MainWindow.xaml

    PuzzleForImage.cs没变还是上面那个

    --20180419 发现一个功能多的:http://download.microsoft.com/download/B/2/5/B25C4C6A-97FE-4014-9D4B-B39607BA9A12/wpf_samples/15Puzzle.exe

  • 相关阅读:
    Kafka生产者Producer配置 ,及C#中使用学习资料连接
    Oracle expdb异地备份
    查询redis当前连接数据和当前信息
    Oracle在sqldeveloper中按格式显示日期数据
    DB行转列
    2019.9.10面试反思
    配置webpack4
    代理
    es6 promise
    es6 symbol
  • 原文地址:https://www.cnblogs.com/tanl/p/5710842.html
Copyright © 2020-2023  润新知