Здравствуйте, сделал элемент UserControl, который может иметь разные высоту и ширину. Необходимо создать алгоритм который будет при добавлении этого элемента в ScrollViewer(или другой элемент) располагать его в месте наиболее подходящем для него, т. е., например, вот тут:
было бы правильно, если бы "серый" элемент был между синим и зелёным. Ширина ScrollViewer фиксированная.
Ответ
У вас размещение элементов - часть бизнес-логики, поэтому алгоритм поместим в VM.
Я написал такой простой класс, представляющий ящик:
class BoxVm : Vm
{
public int Width { get; }
public int Height { get; }
public BoxVm(int width, int height)
{
Width = width;
Height = height;
}
}
И класс, представляющий размещение ящика:
class PlaceVm : Vm
{
public int X { get; }
public int Y { get; }
public BoxVm Box { get; }
public PlaceVm(int x, int y, BoxVm box)
{
X = x;
Y = y;
Box = box;
}
public bool IsIntersects(PlaceVm place)
{
return !(X >= place.X + place.Box.Width
|| X + Box.Width <= place.X
|| Y >= place.Y + place.Box.Height
|| Y + Box.Height <= place.Y);
}
}
Также в нем есть метод для определения пересекаются ли 2 размещения.
Теперь класс, представляющий склад:
class StorageVm : Vm
{
public int Width { get; }
public int Height { get; }
public StorageVm(int width, int height)
{
Width = width;
Height = height;
Places = new ObservableCollection
public ObservableCollection
public PlaceVm AddBox(BoxVm box)
{
for (int y = 0; y <= Height - box.Height; ++y)
for (int x = 0; x <= Width - box.Width; ++x)
{
var place = new PlaceVm(x, y, box);
if (!Places.Any(p => p.IsIntersects(place)))
{
Places.Add(place);
return place;
}
}
throw new InvalidOperationException("Невозможно разместить!");
}
}
Надеюсь, здесь вам всё понятно. Если у вас сетка не ограничена снизу, то просто замените цикл for по y на цикл while, ну и проверку, влазит ли элемент по ширине, надо будет вынести перед циклом и бросать исключение в ней, а не как у меня в конце.
Теперь главная VM:
class MainVm : Vm
{
int width;
public int Width
{
get => width;
set => Set(ref width, value);
}
int height;
public int Height
{
get => height;
set => Set(ref height, value);
}
public StorageVm Storage { get; }
public ICommand AddCommand { get; }
public MainVm()
{
Storage = new StorageVm(10, 10);
AddCommand = new DelegateCommand(_ => Storage.AddBox(new BoxVm(Width, Height)));
}
}
Здесь тоже всё просто - один склад и одна команда для добавления ящика на склад.
Теперь GUI. Нам потребуется конвертер из ячеек в экранные размеры, он очень простой:
class ScaleConverter : IValueConverter
{
public int Coeff { get; set; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (int)value * Coeff;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Он просто умножает входное значение на коэффициент.
Теперь разметка:
Я разместил ItemsControl в ViewBox, чтобы он был растянут на всё доступное пространство, вы же можете поместить его в ScrollViewer.
Комментариев нет:
Отправить комментарий