今天要学习一个拼图项目。
目标是传入一张图片,然后将它分成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、会随机一些拼不出来的拼图