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

目标是传入一张图片,然后将它分成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[]; //游戏地图 判断是否成功 /// <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 = ;
for (int i = ; i < ; i++)
{
for (int j = ; j < ; 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 / ;//每个拼图的宽度
double height = 1.0 / ;//每个拼图的高度
//row0
CreateImagePart( , , width, height);
CreateImagePart( width, , width, height);
CreateImagePart( * width, , width, height);
//row1
CreateImagePart( , height, width, height);
CreateImagePart( width, height, width, height);
CreateImagePart( * width, height, width, height);
//row2
CreateImagePart( , * height, width, height);
CreateImagePart( width, * 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();
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 = ; i < ; i++)
{
int index = ;
//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();
rectPart.HorizontalAlignment = HorizontalAlignment.Stretch;
rectPart.VerticalAlignment = VerticalAlignment.Stretch;
allocatedParts.Add(rectPart);
map[] = ;
} /// <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 - ]; //缺省方块 //得到白块和缺省方块的位置
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 - , Col = currentBlankCol });
posibilities.Add(new PossiblePositions { Row = currentBlankRow + , Col = currentBlankCol });
posibilities.Add(new PossiblePositions { Row = currentBlankRow, Col = currentBlankCol - });
posibilities.Add(new PossiblePositions { Row = currentBlankRow, Col = currentBlankCol + }); //检查该方块是否能点击(白块能移动到当前方块的位置)
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 * + currentTileCol;
int indexBlank = currentBlankRow * + 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 = ; i < ; 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 = / ;//默认屏幕宽高比
this.Width = ;
if (imgWidthHeightRatio > windowWidthHeightRatio)
{
GridImg.Width = ;
GridImg.Height = GridImg.Width / imgWidthHeightRatio;
this.Height = GridImg.Height + ;
}
else
{
this.Height = ;//button占了50
GridImg.Height = ;
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 = / ;//默认屏幕宽高比
this.Width = ;
if (imgWidthHeightRatio > windowWidthHeightRatio)
{
GridImg.Width = ;
GridImg.Height = GridImg.Width / imgWidthHeightRatio;
this.Height = GridImg.Height + ;
}
else
{
this.Height = ;//button占了50
GridImg.Height = ;
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="" Width="">
<Grid>
<Button x:Name="BtnPickImg" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="" 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

WPF学习系列 游戏-选张图片做成9宫格拼图的更多相关文章

  1. WPF学习笔记(8):DataGrid单元格数字为空时避免验证问题的解决

    原文:WPF学习笔记(8):DataGrid单元格数字为空时避免验证问题的解决 如下图,在凭证编辑窗体中,有的单元格不需要数字,但如果录入数字后再删除,会触发数字验证,单元格显示红色框线,导致不能执行 ...

  2. WPF学习系列之五(WPF控件)

    控件:    1.内容控件------这些控件能够包含嵌套的元素,为它们提供几乎无限的显示能力.内容控件包括Lable,Button 以及ToolTip类. 内容控件是更特殊的控件类型,它们可以包含( ...

  3. WPF学习系列 绘制旋转的立方体

    我是一年经验的web程序员,想学习一下wpf,比较喜欢做项目来学习,所以在网上找了一些项目,分析代码,尽量能够做到自己重新敲出来 第一个项目是 中间的方块会不停的旋转. 第一步,新建wpf项目 第二步 ...

  4. WPF学习系列之八(形状,画刷和变换)

    形状,画刷和变换   概述: 在许多用户界面技术中,普通控件和自定义绘图之间具有清晰的区别.通常来说,绘图特性只用于特定的应用程序--如游戏,数据可视化和物理仿真等.而WPF具有一个非常不同的原则.它 ...

  5. WPF学习系列 简单的窗体设置

    今天要学习的源码是一个窗体设置.效果如下,可拖拽.这让我想起了vs的启动界面 下面是源码的情况 项目结构: 窗体代码: cs代码 1.新建项目 略 2.设置窗体 AllowsTransparency= ...

  6. WPF学习系列之七 (样式与行为)

    样式(Styles)是组织和重用格式化选项的重要工具.不是使用重复的标记填充XAML,以设置诸如边距.颜色及字体等细节,而可以创建一系列封装所有这些细节的样式.然后可以在需要之处通过一个属性应用样式. ...

  7. WPF学习系列之六 (元素绑定)

    元素绑定 简单地说,数据绑定是一种关系,该关系告诉WPF从一个源对象提取一些信息,并使用这些信息设置目标对象的属性.目标属性总是依赖属性,并且通常位于WPF元素中. 一.将元素绑定到一起 <Wi ...

  8. WPF学习系列之四(WPF事件5大类)

    WPF最重要的5类事件: 生命周期事件:这些事件将在元素被初始化,加载或卸载时发生. 鼠标事件 这些事件是鼠标动作的结果. 键盘事件 这些事件是键盘动作的结果. 手写笔事件 这些事件是作用类似铅笔的手 ...

  9. WPF学习系列之二 (依赖项属性)

    依赖属性;(dependency property)  它是专门针对WPF创建的,但是WPF库中的依赖项属性都使用普通的.NET属性过程进行了包装.从而可能通过常规的方式使用它们,即使使用他们的代码不 ...

随机推荐

  1. 初识Servlet

    1.创建DispatcherServlet package myservlet; import java.io.IOException; import javax.servlet.ServletExc ...

  2. 深入理解DOM节点类型第二篇——文本节点Text

    × 目录 [1]特征 [2]空白 [3]属性[4]方法[5]性能 前面的话 文本节点顾名思义指向文本的节点,网页上看到的文字内容都属于文本节点.该节点简单直观,本文将详细介绍该部分内容 特征 文本节点 ...

  3. WEB基础原理——理论复习

    基本WEB原理 1. Internet同Web的关系 1.1互联网 全世界最大的局域网. 来源美国国防部的项目用于数据共享 没有TCP/IP之前最开始只能1000台电脑通信(军用协议) 1.2 万维网 ...

  4. H3 BPM初次安装常见错误详解5-7

    错误5:登陆无反应,F12查看后台网络请求错误如下图所示  错误原因:ISAPI未对相应的.net版本允许. 解决方法:IIS的根节点--右侧"ISAPI和CGI限制"打开--将相 ...

  5. 缓冲区溢出利用——捕获eip的傻瓜式指南

    [译文] 摘要:为一个简单的有漏洞程序写一个简单的缓冲区溢出EXP,聚焦于遇到的问题和关键性的教训,提供详细而彻底的描述 内容表:1. I pity the fool, who can't smash ...

  6. IT菜鸟的生存指南(三)流行还是经典

    经常被刚入行的新人请教,想学一门开发语言,最好又简单工资又高又有发展前途.那门语言最好这个话题能在程序员群里吵一下午,所以我也就不掀起战争了. 个人建议如下: 工资高不高不在于学那门语言,而在于你的行 ...

  7. 面向初学者之烦人的mainactivity启动前的actionBAR

    相信各位初学者的童鞋都遇到过一个问题,(大神们就别喷我哦,多多帮帮指正,嘿嘿)那就是当你点开你开发的软件或者是dome时,会发现这么一个问题: 你曾今以为你的软件点开的时候是这样的: 然而事实是残酷的 ...

  8. (十一)Maven远程仓库的各种配置

    1.远程仓库的配置 在平时的开发中,我们往往不会使用默认的中央仓库,默认的中央仓库访问的速度比较慢,访问的人或许很多,有时候也无法满足我们项目的需求,可能项目需要的某些构件中央仓库中是没有的,而在其他 ...

  9. ASP.NET MVC 5 05 - 视图

    PS: 唉,这篇随笔国庆(2015年)放假前几天开始的,放完假回来正好又赶上年底,公司各种破事儿. 这尼玛都写跨年了都,真服了.(=_=#) 好几次不想写了都. 可是又不想浪费这么多,狠不下心删除.没 ...

  10. Java 加解密 AES DES TripleDes

    package xxx.common.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypt ...