1
votes

I am using C# and am curious to know if there is a special algorithm to implement the functionality in the picture below.

enter image description here

When you click a button, all other buttons must be shifted into adjacent (some invisible) cells, indication a direction you want something to go in.

I can only think to use a grid with 9 image controls and create an event for each image clicked, but this seems a little much for something that's seemingly so simple.

EDIT

Behavior for clicking buttons:

button 1 -> Shift all cells 1 cell up and to the left

button 2 -> Shift all cells 1 cell up

button 3 -> Shift all cells 1 cell up and to the right

button 4 -> Shift all cells 1 cell to the left

button 5 -> Nothing happens

button 6 -> Shift all cells 1 cell to the right

button 7 -> Shift all cells 1 cell down and to the left

button 8 -> Shift all cells 1 cell down

button 9 -> Shift all cells 1 cell down and to the right

1
all other arrows are shifted into adjacent (invisible) cells - I don't understand what this means. To me there are just 9 cells, all of them are visible. So it's hard to know which are invisible at the time of clicking on one of the cells. The only way to understand this is try this feature of Photoshop directly.King King
See the edits to my post. I tried to explain the functionality a little better.user1618054
there is a simple approach here in which you just need to translate the whole 9 cells (and these should be put in such as a UniformGrid). The wrapper element should cut off the shifted part easily. This can even be improved to add translation effect. The disadvantage here is it requires to know the fixed size of the square beforehand. That's of course acceptable, we should not make the square's size dynamic at runtime in this case.King King
What's the advantage of using a UniformGrid vs a Grid?user1618054
In this case the 9 cells are perfectly arranged in a uniform grid. You can then simply add 9 items to the UniformGrid with 3 columns, then it will auto arrange the items for you. Using Grid requires you to declare 3 rowdefinitions and 3 column defintions or some other composite layout (such as use 3 rowdefinitions with each StackPanel or DockPanel in each row, ...). It is of course more complicated. In code behind you even need to set the attached property Grid.Row or Grid.Column. When using UniformGrid, you simply use Children.Add to add items in the correct order.King King

1 Answers

1
votes

I recently designed a UserControl that handles desired functionality and enables you to get/set a Direction property indicating the current position. I'm sure the code could be refined a little better but here it is:

DirectionPad.xaml.cs:

using System.Windows;
using System.Windows.Controls;

namespace MyNamespace
{
    public partial class DirectionPad : UserControl
    {
        #region Dependency Properties

        public static DependencyProperty DirectionProperty = DependencyProperty.Register("Direction", typeof(Direction), typeof(DirectionPad), new FrameworkPropertyMetadata(Direction.Origin, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDirectionChanged));
        public Direction Direction
        {
            get
            {
                return (Direction)GetValue(DirectionProperty);
            }
            set
            {
                SetValue(DirectionProperty, value);
            }
        }
        private static void OnDirectionChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
        {
            DirectionPad Pad = Object as DirectionPad;
            Pad.SetPositions(Pad.Direction);
        }

        #endregion

        #region DirectionPad

        public DirectionPad()
        {
            InitializeComponent();
        }

        #endregion

        #region Methods

        public void SetPositions(Direction Direction)
        {
            int StartRow = 0, StartColumn = 0;
            switch (Direction)
            {
                case Direction.NW:
                    StartRow = 0;
                    StartColumn = 0;
                    break;
                case Direction.N:
                    StartRow = 0;
                    StartColumn = 1;
                    break;
                case Direction.NE:
                    StartRow = 0;
                    StartColumn = 2;
                    break;
                case Direction.W:
                    StartRow = 1;
                    StartColumn = 0;
                    break;
                case Direction.Origin:
                    StartRow = 1;
                    StartColumn = 1;
                    break;
                case Direction.E:
                    StartRow = 1;
                    StartColumn = 2;
                    break;
                case Direction.SW:
                    StartRow = 2;
                    StartColumn = 0;
                    break;
                case Direction.S:
                    StartRow = 2;
                    StartColumn = 1;
                    break;
                case Direction.SE:
                    StartRow = 2;
                    StartColumn = 2;
                    break;
                //Direction.Origin
            }
            int i = StartRow, j = StartColumn;
            foreach (UIElement CurrentButton in this.Grid.Children)
            {
                if (!(CurrentButton is Button)) continue;
                if (j < StartColumn + 3)
                {
                    Grid.SetRow(CurrentButton, i);
                    Grid.SetColumn(CurrentButton, j++);
                    if (j == (StartColumn + 3))
                    {
                        j = StartColumn;
                        i++;
                    }
                }
            }
        }

        public void ShiftPositions(Direction Direction, int Row, int Column)
        {
            bool ShiftUp = false, ShiftDown = false, ShiftLeft = false, ShiftRight = false;
            switch (Direction)
            {
                case Direction.NW:
                    if (Column - 1 < 0 || Row - 1 < 0) return;
                    ShiftUp = true;
                    ShiftLeft = true;
                    break;
                case Direction.N:
                    if (Row - 1 < 0) return;
                    ShiftUp = true;
                    break;
                case Direction.NE:
                    if (Column + 1 > 4 || Row - 1 < 0) return;
                    ShiftUp = true;
                    ShiftRight = true;
                    break;
                case Direction.W:
                    if (Column - 1 < 0) return;
                    ShiftLeft = true;
                    break;
                case Direction.E:
                    if (Column + 1 > 4) return;
                    ShiftRight = true;
                    break;
                case Direction.SW:
                    if (Column - 1 < 0 || Row + 1 > 4) return;
                    ShiftDown = true;
                    ShiftLeft = true;
                    break;
                case Direction.S:
                    if (Row + 1 > 4) return;
                    ShiftDown = true;
                    break;
                case Direction.SE:
                    if (Column + 1 > 4 || Row + 1 > 4) return;
                    ShiftDown = true;
                    ShiftRight = true;
                    break;
                //Direction.Origin
                default:
                    return;
            }
            foreach (UIElement CurrentButton in this.Grid.Children)
            {
                if (CurrentButton is Button)
                {
                    int CurrentRow = Grid.GetRow(CurrentButton), CurrentColumn = Grid.GetColumn(CurrentButton);
                    Grid.SetRow(CurrentButton, ShiftUp ? CurrentRow - 1 : ShiftDown ? CurrentRow + 1 : CurrentRow);
                    Grid.SetColumn(CurrentButton, ShiftLeft ? CurrentColumn - 1 : ShiftRight ? CurrentColumn + 1 : CurrentColumn);
                }
            }
            this.UpdateDirection();
        }

        public void UpdateDirection()
        {
            if (Grid.GetRow(this.OriginButton) == 1)
            {
                if (Grid.GetColumn(this.OriginButton) == 1)
                {
                    this.Direction = Direction.NW;
                }
                else if (Grid.GetColumn(this.OriginButton) == 2)
                {
                    this.Direction = Direction.N;
                }
                else if (Grid.GetColumn(this.OriginButton) == 3)
                {
                    this.Direction = Direction.NE;
                }
            }
            else if (Grid.GetRow(this.OriginButton) == 2)
            {
                if (Grid.GetColumn(this.OriginButton) == 1)
                {
                    this.Direction = Direction.W;
                }
                else if (Grid.GetColumn(this.OriginButton) == 2)
                {
                    this.Direction = Direction.Origin;
                }
                else if (Grid.GetColumn(this.OriginButton) == 3)
                {
                    this.Direction = Direction.E;
                }
            }
            else if (Grid.GetRow(this.OriginButton) == 3)
            {
                if (Grid.GetColumn(this.OriginButton) == 1)
                {
                    this.Direction = Direction.SW;
                }
                else if (Grid.GetColumn(this.OriginButton) == 2)
                {
                    this.Direction = Direction.S;
                }
                else if (Grid.GetColumn(this.OriginButton) == 3)
                {
                    this.Direction = Direction.SE;
                }
            }
        }

        #endregion

        #region Events

        private void _Click(object sender, RoutedEventArgs e)
        {
            FrameworkElement Element = sender as FrameworkElement;
            int Row = Grid.GetRow(Element);
            int Column = Grid.GetColumn(Element);
            string Tag = Element.Tag.ToString();
            this.ShiftPositions(Tag.ParseEnum<Direction>(), Row, Column);
        }

        #endregion
    }
}

DirectionPad.xaml:

<UserControl x:Class="MyNamespace.DirectionPad"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Height="Auto"
        Width="Auto">
    <Grid x:Name="Grid" ClipToBounds="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0"/>
            <ColumnDefinition Width="24"/>
            <ColumnDefinition Width="24"/>
            <ColumnDefinition Width="24"/>
            <ColumnDefinition Width="0"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="0"/>
            <RowDefinition Height="24"/>
            <RowDefinition Height="24"/>
            <RowDefinition Height="24"/>
            <RowDefinition Height="0"/>
        </Grid.RowDefinitions>
        <Button Grid.Row="1" Grid.Column="1" Tag="NW" ToolTip="Top Left" Click="_Click"/>
        <Button Grid.Row="1" Grid.Column="2" Tag="N" ToolTip="Top" Click="_Click"/>
        <Button Grid.Row="1" Grid.Column="3" Tag="NE" ToolTip="Top Right" Click="_Click"/>
        <Button Grid.Row="2" Grid.Column="1" Tag="E" ToolTip="Left" Click="_Click"/>
        <Button Grid.Row="2" Grid.Column="2" Tag="Origin" ToolTip="Origin" Click="_Click"/>
        <Button Grid.Row="2" Grid.Column="3" Tag="W" ToolTip="Right" Click="_Click"/>
        <Button Grid.Row="3" Grid.Column="1" Tag="SW" ToolTip="Bottom Left" Click="_Click"/>
        <Button Grid.Row="3" Grid.Column="2" Tag="S" ToolTip="Bottom" Click="_Click"/>
        <Button Grid.Row="3" Grid.Column="3" Tag="SE" ToolTip="Bottom Right" Click="_Click"/>
    </Grid>
</UserControl>

Direction Enum:

namespace MyNamespace {
    public Direction {
        N, 
        NE, 
        NW, 
        Origin, 
        E, 
        W, 
        SW, 
        SE, 
        S
    }
}

ParseEnum Extension:

public static T ParseEnum<T>(this string ToParse)
{
    return (T)Enum.Parse(typeof(T), ToParse, true);
}

And finally, the result looks like this:

enter image description here