Страницы

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

четверг, 2 апреля 2020 г.

WPF: замостить контейнер элементами

#c_sharp #wpf

                    
Здравствуйте, сделал элемент UserControl, который может иметь разные высоту и ширину.
 Необходимо создать алгоритм который будет при добавлении этого элемента в ScrollViewer(или
другой элемент) располагать его в месте наиболее подходящем для него, т. е., например,
вот тут:



было бы правильно, если бы "серый" элемент был между синим и зелёным. Ширина ScrollViewer
фиксированная.
    


Ответы

Ответ 1



У вас размещение элементов - часть бизнес-логики, поэтому алгоритм поместим в 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 Places { get; } 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(); } } Он просто умножает входное значение на коэффициент. Теперь разметка:

Комментариев нет:

Отправить комментарий