Вот коды к игре пятнашки, нужно чтобы при нажатии ctrl + z происходил откат перемещения до самого первого, но как это сделать средствами wpf не знаю помогите.
//MainWindow.xaml.cs
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;
using System.IO;
namespace NumberPuzzle
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public partial class MainWindow : Window
{
private Board logicalBoard;
private string saveFileName = "NumberPuzzle.dat";
//add fields for menu operations here
public MainWindow()
{
InitializeComponent();
logicalBoard = new Board(saveFileName);
logicalBoard.OnBoardChanged += updateView;
//force update
updateView(logicalBoard, new BoardChangedEventArgs(null, null, true));
}
//mix/unmix board
private void MixButton_Click(object sender, RoutedEventArgs e)
{
logicalBoard.mixPuzzle();
}
//grow/shrink board
private void ShrinkBoard_Click(object sender, RoutedEventArgs e)
{
newLogicalBoard(logicalBoard.Width - 1);
}
private void GrowBoard_Click(object sender, RoutedEventArgs e)
{
newLogicalBoard(logicalBoard.Width + 1);
}
//exit game
private void Exit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
MessageBoxResult result =
MessageBox.Show("Save Game?", "Save Current Game", MessageBoxButton.YesNoCancel,
MessageBoxImage.Question, MessageBoxResult.No);
if (result == MessageBoxResult.Cancel) e.Cancel = true;
else if (result == MessageBoxResult.Yes)
{
logicalBoard.save(saveFileName, SaveOption.saveSettingsAndBoard);
}
else //don't save: save settings only
{
logicalBoard.save(saveFileName, SaveOption.saveSettingsOnly);
}
}
//drag board
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
//Drag/drop buttons (fields)
private bool dragging = false;
private Button dragee = null;
private Point dragOrigin;
private Direction allowedDragDirection; //based on logical board
const int dragDelta = 5; //use Windows constant when I figure out how
//drag/drop buttons (event handlers)
private void startDrag(object sender, MouseButtonEventArgs e)
{
Button button = sender as Button;
if (button != null)
{
Tile tile = button.Content as Tile;
if (tile != null)
{
//determine if drag is allowed and which direction
if (tile.canMove(Direction.down))
{ this.allowedDragDirection = Direction.down; }
else if (tile.canMove(Direction.up))
{ this.allowedDragDirection = Direction.up; }
else if (tile.canMove(Direction.left))
{ this.allowedDragDirection = Direction.left; }
else if (tile.canMove(Direction.right))
{ this.allowedDragDirection = Direction.right; }
else return;
//if drag allowed, set drag variables
this.dragging = true;
this.dragee = button;
this.dragOrigin = e.GetPosition(Application.Current.MainWindow);
}
}
}
private void currentlyDragging(object sender, MouseEventArgs e)
{
if (dragging)
{
Point currentPosition = e.GetPosition(Application.Current.MainWindow);
double deltaX = currentPosition.X - this.dragOrigin.X;
double deltaY = currentPosition.Y - this.dragOrigin.Y;
double maxDeltaX = this.dragee.ActualWidth;
double maxDeltaY = this.dragee.ActualHeight;
//render transform button to follow mouse
//only drags in allowed direction and not further than final resting place
switch (this.allowedDragDirection)
{
case Direction.up:
deltaX = 0;
deltaY = deltaY <= -MainWindow.dragDelta ? deltaY : 0;
deltaY = deltaY <= -maxDeltaY ? -maxDeltaY : deltaY;
break;
case Direction.down:
deltaX = 0;
deltaY = deltaY >= MainWindow.dragDelta ? deltaY : 0;
deltaY = deltaY >= maxDeltaY ? maxDeltaY : deltaY;
break;
case Direction.left:
deltaY = 0;
deltaX = deltaX <= -MainWindow.dragDelta ? deltaX : 0;
deltaX = deltaX <= -maxDeltaX ? -maxDeltaX : deltaX;
break;
case Direction.right:
deltaY = 0;
deltaX = deltaX >= MainWindow.dragDelta ? deltaX : 0;
deltaX = deltaX >= maxDeltaX ? maxDeltaX : deltaX;
break;
default: return;
}
dragee.RenderTransform = new TranslateTransform(deltaX, deltaY);
}
}
private void endDrag(object sender, MouseButtonEventArgs e)
{
if (dragging)
{
Button button = sender as Button;
if (button != null)
{
Tile tile = button.Content as Tile;
if (tile != null)
{
//determine whether to drop button in new location or snap back
//if more than half-way, make switch
Point endPosition = e.GetPosition(Application.Current.MainWindow);
double deltaX = endPosition.X - this.dragOrigin.X;
double deltaY = endPosition.Y - this.dragOrigin.Y;
double threshholdDeltaX = this.dragee.ActualWidth / 2;
double threshholdDeltaY = this.dragee.ActualHeight / 2;
switch (this.allowedDragDirection)
{
case Direction.up:
if (deltaY <= -threshholdDeltaY)
tile.move(Direction.up);
else button.RenderTransform = null;
break;
case Direction.down:
if (deltaY >= threshholdDeltaY)
tile.move(Direction.down);
else button.RenderTransform = null;
break;
case Direction.left:
if (deltaX <= -threshholdDeltaX)
tile.move(Direction.left);
else button.RenderTransform = null;
break;
case Direction.right:
if (deltaX >= threshholdDeltaX)
tile.move(Direction.right);
else button.RenderTransform = null;
break;
default: return;
}
}
}
}
this.dragging = false;
this.dragee = null;
}
//helper methods
//updates view
private void updateView(object sender, BoardChangedEventArgs e)
{
if (e.newBoard) resetView();
else swapTiles(e.tile1, e.tile2);
}
//used to update view
private void swapTiles(Tile tile1, Tile tile2)
{
if (tile1 != null && tile2 != null)
{
Button b1 = null, b2 = null;
//find buttons containing tiles
foreach (object o in grid.Children)
{
Button b = o as Button;
if (b != null)
{
if (b.Content == tile1) b1 = b;
else if (b.Content == tile2) b2 = b;
}
}
//switch button content, color, visibility
if (b1 != null && b2 != null)
{
//switch tile content
b1.Content = tile2;
b2.Content = tile1;
//switch button color if relevant
Brush temp = b1.Background;
b1.Background = b2.Background;
b2.Background = temp;
//switch visibility
System.Windows.Visibility temp2 = b1.Visibility;
b1.Visibility = b2.Visibility;
b2.Visibility = temp2;
//negate drag & drop render transform
b1.RenderTransform = null;
b2.RenderTransform = null;
}
}
}
//used to update view: re-creates view from logical board
private void resetView()
{
grid.Children.Clear();
int fontSize = logicalBoard.Height > 4 ? 66 - (logicalBoard.Height - 4) * 7 :
logicalBoard.Height < 4 ? 66 + (4 - logicalBoard.Height) * 10 :
66;
for (int i = 0; i < logicalBoard.Squares; ++i)
{
Tile t = logicalBoard[i];
Button b = new Button();
b.Content = t;
b.Foreground = new SolidColorBrush(Colors.Goldenrod);
//crimson/white checkered color scheme
int value = t.Value;
b.Background =
//in board with even width
((logicalBoard.Width % 2 == 0 &&
//odd-valued tiles in even starting rows or even-valued tiles in odd starting rows = white
((value % 2 != 0 && (value - 1) / logicalBoard.Width % 2 == 0)
|| (value % 2 == 0 && (value - 1) / logicalBoard.Width % 2 != 0)))
//board with odd width, odd-valued tiles = white
|| (logicalBoard.Width % 2 != 0 && value % 2 != 0)) ?
new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Crimson);
b.FontSize = fontSize;
b.FontWeight = FontWeights.Heavy;
b.PreviewMouseLeftButtonDown += startDrag;
b.PreviewMouseMove += currentlyDragging;
b.PreviewMouseLeftButtonUp += endDrag;
if (value == 0)
{
b.Visibility = Visibility.Hidden;
}
grid.Children.Add(b);
}
}
//used to grow/shrink board: re-creates logical board of new size
private void newLogicalBoard(int size = Board.DEFAULT_WIDTH)
{
logicalBoard = new Board(size, size);
logicalBoard.OnBoardChanged += updateView;
logicalBoard.unmixPuzzle();
}
}
}
//Board.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace NumberPuzzle
{
//Moves
public enum Direction : byte { left, right, up, down};
//Save Options
public enum SaveOption : byte { saveSettingsAndBoard, saveSettingsOnly, dontSave }
//Tiles
public class Tile
{
public readonly int Value;
//position in board
public int Index { get; set; } //needs bounds checking now that it's public
public int Row { get { return Index / this.board.Height; } }
public int Column { get { return Index % this.board.Width; } }
//check if tile can move
public bool canMove(Direction d) { return Board.canMove(this, d); }
public bool canMove()
{
return canMove(Direction.left) || canMove(Direction.right) ||
canMove(Direction.up) || canMove(Direction.down);
}
//move tile
public bool move(Direction d) { return Board.move(this, d); }
//internal use only
//tile can be used as Content property of button, ToString() displays .Value
public override string ToString() { return Value.ToString(); }
//internal use only
public readonly Board board;
public Tile(Board board, int index, int value)
{ this.board = board; Index = index; Value = value; }
}
public class Board
{
//constants
public const int DEFAULT_WIDTH = 4;
public const int DEFAULT_HEIGHT = 4;
public const int MIN_WIDTH = 2, MIN_HEIGHT = 2;
public const int MAX_WIDTH = 11, MAX_HEIGHT = 11;
public const SaveOption DEFAULT_SAVE_OPTION = SaveOption.saveSettingsOnly;
//public constructors
//from file
public Board(string path)
{
if (File.Exists(path))
{
try
{
using (FileStream fStream = File.OpenRead(path))
{
SaveOption saveOption = (SaveOption)(fStream.ReadByte());
switch (saveOption)
{
case SaveOption.dontSave: break;
case SaveOption.saveSettingsOnly:
case SaveOption.saveSettingsAndBoard:
int width = fStream.ReadByte();
int height = fStream.ReadByte();
initializeBoard(width, height);
if (saveOption == SaveOption.saveSettingsOnly)
{
unmixPuzzle();
return;
}
byte[] buffer = new byte[width * height];
fStream.Read(buffer, 0, buffer.Length);
int[] sequence = new int[buffer.Length];
for (int j = 0; j < sequence.Length; ++j)
{
sequence[j] = (int)(buffer[j]);
}
this.Sequence = sequence;
return;
default: break;
}
}
}
catch { }
}
//default on file not found or file fail or dontSave
initializeBoard(DEFAULT_WIDTH, DEFAULT_HEIGHT);
unmixPuzzle();
}
//creates unmixed board
public Board(int width = DEFAULT_WIDTH, int height = DEFAULT_HEIGHT)
{
initializeBoard(width, height);
unmixPuzzle();
}
//copy constructor
public Board(Board other)
{
initializeBoard(other.Width, other.Height);
for (int i = 0; i < Squares; ++i)
{
this[i] = new Tile(this, other[i].Index, other[i].Value);
}
}
//public properties
public int Width
{
get
{
return this.width;
}
private set
{
this.width =
value < MIN_WIDTH ? MIN_WIDTH : value > MAX_WIDTH ? MAX_WIDTH : value;
}
}
public int Height
{
get
{
return this.height;
}
private set
{
this.height =
value < MIN_HEIGHT ? MIN_HEIGHT : value > MAX_HEIGHT ? MAX_HEIGHT : value;
}
}
public int Squares { get { return Width * Height; } }
public int Tiles { get { return Squares - 1; } }
public int[] Sequence //get/set tiles as int[] of values
{
get
{
int[] sequence = new int[this.Squares];
for (int i = 0; i < Squares; ++i)
{
sequence[i] = tiles[i].Value;
}
return sequence;
}
set
{
if (value != null && value.Distinct<int>().Count<int>() == this.Squares
&& value.Max<int>() == this.Tiles && value.Min<int>() == 0)
{
for (int i = 0; i < Squares; ++i)
{
tiles[i] = new Tile(this, i, value[i]);
}
}
if (OnBoardChanged != null)
{
this.OnBoardChanged(this, new BoardChangedEventArgs(null, null, true));
}
}
}
public Tile this[int index] //0-based indexes; invalid index returns null
{
get
{
return (index < 0 || index >= this.Squares) ? null : this.tiles[index];
}
private set
{
if (index >= 0 && index < this.Squares) this.tiles[index] = value;
}
}
public Tile this[int row, int column] // 0-based indexes; invalid index returns null
{
get
{
int index = row * Width + column;
return (row < 0 || column < 0 || row >= Height || column >= Width) ?
null : this.tiles[index];
}
private set
{
int index = row * Width + column;
if (row >= 0 && column >= 0 && row < Height && column < Width
&& value != null
&& value.Value >= 0 && value.Value <= Tiles
&& value.board != null
&& value.Index == index)
{
this.tiles[index] = value;
}
}
}
//public methods
public void unmixPuzzle() { this.Sequence = orderedSequence(Squares); }
public void mixPuzzle()
{
Tile emptySquare = null;
//Find empty tile and get reference
for (int i = 0; i < Squares; ++i)
{
if (this.tiles[i].Value == 0)
{
emptySquare = this.tiles[i];
break;
}
}
if (emptySquare == null)
{
unmixPuzzle();
return;
}
//number of mixing moves proportional to board size
int moves = this.Width * this.Height * 10;
List<Direction> directions = new List<Direction>(4);
Direction[] fixedDirections =
new Direction[4] { Direction.left, Direction.up, Direction.right, Direction.down };
Random random = new Random();
int randomRange;
Direction direction;
//executes (moves) random moves of empty square
for(int move = 0; move < moves; ++move)
{
directions.AddRange(fixedDirections);
randomRange = 4;
//tries to move empty space in random directions until it runs out
while(!Board.move(emptySquare, (direction = directions[(random.Next(randomRange))]), true)
&& randomRange > 1 /*included to avoid infinite loop in case of error*/)
{
//if move fails, try another random direction from remaining directions
directions.Remove(direction);
--randomRange;
}
}
//update view
if(this.OnBoardChanged != null)
{
this.OnBoardChanged(this, new BoardChangedEventArgs(null, null, true));
}
}
//checks if a tile can move
public static bool canMove(Tile t, Direction d)
{
switch (d)
{
case Direction.left:
return t.Column > 0 && t.board[t.Row, t.Column - 1].Value == 0;
case Direction.right:
return t.Column < t.board.Width - 1 && t.board[t.Row, t.Column + 1].Value == 0;
case Direction.up:
return t.Row > 0 && t.board[t.Row - 1, t.Column].Value == 0;
case Direction.down:
return t.Row < t.board.Height - 1 && t.board[t.Row + 1, t.Column].Value == 0;
default: return false;
}
}
//makes move if legal; autoMove allows any tile switch with no view update
public static bool move(Tile t, Direction d, bool autoMove = false)
{
Tile other;
switch (d)
{
case Direction.left:
other = t.board[t.Row, t.Column - 1];
break;
case Direction.right:
other = t.board[t.Row, t.Column + 1];
break;
case Direction.up:
other = t.board[t.Row - 1, t.Column];
break;
case Direction.down:
other = t.board[t.Row + 1, t.Column];
break;
default: return false;
}
//autoMove swaps tiles and does not update view
if (other != null && (other.Value == 0 || autoMove))
{
Board.swap(t, other);
if (!autoMove && t.board.OnBoardChanged != null)
{
t.board.OnBoardChanged(t.board, new BoardChangedEventArgs(t, other));
}
return true;
}
return false;
}
public bool save(string path, SaveOption saveOption = DEFAULT_SAVE_OPTION)
{
try
{
switch (saveOption)
{
case SaveOption.saveSettingsAndBoard:
this.saveBoard(path, saveOption);
return true;
case SaveOption.saveSettingsOnly:
this.saveBoard(path, saveOption);
return true;
case SaveOption.dontSave:
this.saveBoard(path, saveOption);
return true;
default: goto case SaveOption.dontSave;
}
}
catch
{
try { this.saveBoard(path, SaveOption.dontSave); }
catch { try { deleteSaveFile(path); } catch { } }
return false;
}
}
//events
public event EventHandler<BoardChangedEventArgs> OnBoardChanged;
//private methods
private static int[] orderedSequence(int howMany)
{
int[] sequence = new int[howMany];
for (int i = 0; i < howMany - 1; ++i)
{
sequence[i] = i + 1;
}
sequence[howMany - 1] = 0;
return sequence;
}
//swaps 2 tiles in board; updates Tile indices
private static void swap(Tile t1, Tile t2)
{
if (t1.board == t2.board)
{
t1.board[t1.Index] = t2;
t2.board[t2.Index] = t1;
}
int temp = t1.Index;
t1.Index = t2.Index;
t2.Index = temp;
}
//saves game to file at path, no exception handling
private void saveBoard(string path, SaveOption saveOption)
{
using (FileStream fStream = new FileStream(path,
FileMode.Create, FileAccess.Write, FileShare.None))
{
fStream.WriteByte((byte)saveOption);
if (saveOption == SaveOption.dontSave) return;
fStream.WriteByte((byte)this.Width);
fStream.WriteByte((byte)this.Height);
if (saveOption == SaveOption.saveSettingsOnly) return;
byte[] buffer = new byte[this.Sequence.Length];
for (int i = 0; i < buffer.Length; ++i)
{
buffer[i] = (byte)(this.Sequence[i]);
}
fStream.Write(buffer, 0, buffer.Length);
}
}
//deletes files at path, no exception handling
private void deleteSaveFile(string path)
{
if (File.Exists(path))
{
File.Delete(path);
}
}
private void initializeBoard(int width, int height)
{
Width = width;
Height = height;
tiles = new Tile[Width * Height];
}
//private fields
private int width;
private int height;
private Tile[] tiles;
}
public class BoardChangedEventArgs : EventArgs
{
public readonly Tile tile1;
public readonly Tile tile2;
public readonly bool newBoard;
public BoardChangedEventArgs(Tile tile1, Tile tile2, bool newBoard = false)
{
this.tile1 = tile1;
this.tile2 = tile2;
this.newBoard = newBoard;
}
}
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Сообщение о разрешении доступа к камере и микрофону сейчас появляется при запуске приложенияСообщения были подключены при помощи возможности...
Как мне инициализировать глобальный метод в приложении WPFНаподобие program
Есть xls файл, созданный сторонней программойВ нем 28 030 подряд вниз заполненных ячеек в столбце
Обратил внимание, что при выстраивании цепочки style-loader, css-loader, postcss-loader файлы стилей 2 раза прогоняется через autoprefixer (css-loader и postcss-loader)Частично...