Горячие клавиши в WPF

205
06 апреля 2018, 15:43

Вот коды к игре пятнашки, нужно чтобы при нажатии 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;
        }
    }
}
READ ALSO
Доступ к камере из различных мест приложения

Доступ к камере из различных мест приложения

Сообщение о разрешении доступа к камере и микрофону сейчас появляется при запуске приложенияСообщения были подключены при помощи возможности...

200
Глобальный метод в WPF

Глобальный метод в WPF

Как мне инициализировать глобальный метод в приложении WPFНаподобие program

145
c# excel определить последнюю ячейку

c# excel определить последнюю ячейку

Есть xls файл, созданный сторонней программойВ нем 28 030 подряд вниз заполненных ячеек в столбце

214
Использование postcss-loader без css-loader

Использование postcss-loader без css-loader

Обратил внимание, что при выстраивании цепочки style-loader, css-loader, postcss-loader файлы стилей 2 раза прогоняется через autoprefixer (css-loader и postcss-loader)Частично...

192