На интерфейсе располагается в центре кнопка, вокруг него элементы типа Image. С виду это выглядит как квадрат 3 на 3. При клике на кнопку окружающие картинки должны сместится на одну клетку при каждом клике. Например, картинка которая находится (объясняю на языке двухмерного массива), которая располагается на позиции 1 0 от кнопки, должна сместится на позицию 0 0, а другая картинка с позиции 0 0, на позицию 0 1.
При помощи такого подхода:
image10.BeginAnimation(MarginProperty, new ThicknessAnimation
{
From = new Thickness(99, 66, 361, 212),
To = new Thickness(99, 24, 361, 254),
Duration = TimeSpan.FromSeconds(0.85)
});
мне удается выполнить такое действие до второго клика. После второго клика картинки возвращаются в начальные положения и повторяют действие первого клика, а нужно чтобы при каждом клике ничего не сбивалось и картинки перемещались по кругу.
Чтобы реализовать такой подход, я понял, что строго к элементам обращаться не получится, ибо выходит то, что написал выше.
Поэтому вопрос таков, как при помощи координат в коде C# мне обратится к объекту, который располагается в данном месте и анимированно переместить в другое место?
Как я прочитал в интернете для поиска и перемещения нужно, чтобы все объекты располагались на канвасе. Однако скажу сразу, что я пытался при помощи такого подхода получить координаты.
Canvas.SetLeft(image10, 0);
Canvas.SetTop(image10, 0);
Но при помощи такого подхода не выходит прицепить анимацию перемещения, а она очень важна.
P.S. Буду признателен, если озвучите более просто подход выполнения этой задумки.
Ответ
Давайте напишем класс, представляющий "плитку":
class Tile : Vm
{
int x;
public int X
{
get => x;
set => Set(ref x, value);
}
int y;
public int Y
{
get => y;
set => Set(ref y, value);
}
string caption;
public string Caption
{
get => caption;
set => Set(ref caption, value);
}
}
Вы в своем классе будете хранить картинку или ссылку на нее, я использую просто строковую надпись string Caption
Теперь главная ViewModel
class MainVM : Vm
{
ObservableCollection
RelayCommand turnCommand;
public ICommand TurnCommand => turnCommand;
public MainVM()
{
Tiles = new ObservableCollection
turnCommand = new RelayCommand(_ => TurnTiles());
}
void TurnTiles()
{
// Здесь используются фичи C# 7.0 и .NET Framework 4.7
Dictionary<(int, int), (int, int)> transitions = new Dictionary<(int, int), (int, int)>
{
[(0, 0)] = (0, 1),
[(1, 0)] = (0, 0),
[(2, 0)] = (1, 0),
[(2, 1)] = (2, 0),
[(2, 2)] = (2, 1),
[(1, 2)] = (2, 2),
[(0, 2)] = (1, 2),
[(0, 1)] = (0, 2),
};
foreach (var tile in Tiles)
(tile.X, tile.Y) = transitions[(tile.X, tile.Y)];
/* Вариант "по-старинке", для тех, у кого по каким-то причинам не работает вариант выше
Dictionary
В общем-то все просто, коллекция с "плитками" и команда для их перемещения по кругу. Причем в этом примере использовать ObservableCollection даже не обязательно, вы можете использовать обычный List
transitions - это словарь переходов, ключ словаря - начальные координаты плитки, значение - конечные
Теперь займемся представлением.
Напишем конвертеры координат VM => View
class XToLeftConverter : IValueConverter
{
public double TileWidth { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value * TileWidth;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
И
class YToTopConverter : IValueConverter
{
public double TileHeight { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value * TileHeight;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
В них определено публичное свойство, которое будет устанавливаться из разметки.
Теперь сама разметка окна, в качестве примера я использовал этот ответ
Немного кривовато, но пока так.
В принципе это уже работает, но, пока, без анимации. При клике по кнопке в центре плитки перепрыгивают по кругу.
Теперь это все дело нужно анимировать, я использовал этот ответ. Я пока не знаю, как использовать этот класс-хелпер для анимации двух свойств, поэтому завел два класса с одинаковым содержимым (как появится информация - обновлю ответ): AnimatableLeftHelper и AnimatableTopHelper
Теперь используем эти хелперы чтобы анимировать свойства:
Готово!
Здесь я использовал "стандартные" классы для MVVM WPF
Vm
abstract class Vm : INotifyPropertyChanged
{
protected bool Set
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
RelayCommand
class RelayCommand : ICommand
{
protected readonly Predicate
Комментариев нет:
Отправить комментарий