Страницы

Поиск по вопросам

четверг, 5 декабря 2019 г.

Как по координатам найти объект на Canvas'е и переместить его?

#c_sharp #wpf


На интерфейсе располагается в центре кнопка, вокруг него элементы типа 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. Буду признателен, если озвучите более просто подход выполнения этой задумки.
    


Ответы

Ответ 1



Давайте напишем класс, представляющий "плитку": 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 tiles; public ObservableCollection Tiles { get => tiles; set => Set(ref tiles, value); } RelayCommand turnCommand; public ICommand TurnCommand => turnCommand; public MainVM() { Tiles = new ObservableCollection { new Tile { Caption="1", X = 0, Y = 0 }, new Tile { Caption="2", X = 1, Y = 0 }, new Tile { Caption="3", X = 2, Y = 0 }, new Tile { Caption="4", X = 2, Y = 1 }, new Tile { Caption="5", X = 2, Y = 2 }, new Tile { Caption="6", X = 1, Y = 2 }, new Tile { Caption="7", X = 0, Y = 2 }, new Tile { Caption="8", X = 0, Y = 1 }, }; 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, Tuple> transitions = new Dictionary, Tuple> { [Tuple.Create(0, 0)] = Tuple.Create(0, 1), [Tuple.Create(1, 0)] = Tuple.Create(0, 0), [Tuple.Create(2, 0)] = Tuple.Create(1, 0), [Tuple.Create(2, 1)] = Tuple.Create(2, 0), [Tuple.Create(2, 2)] = Tuple.Create(2, 1), [Tuple.Create(1, 2)] = Tuple.Create(2, 2), [Tuple.Create(0, 2)] = Tuple.Create(1, 2), [Tuple.Create(0, 1)] = Tuple.Create(0, 2), }; foreach (var tile in Tiles) { var coords = transitions[Tuple.Create(tile.X, tile.Y)]; tile.X = coords.Item1; tile.Y = coords.Item2; } */ } } В общем-то все просто, коллекция с "плитками" и команда для их перемещения по кругу. Причем в этом примере использовать 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(); } } В них определено публичное свойство, которое будет устанавливаться из разметки. Теперь сама разметка окна, в качестве примера я использовал этот ответ: