Отправляет email-рассылки с помощью сервиса Sendsay

[prg] Re: Больной вопрос: координаты

Приветствую!

Сразу говорю, что я не гуру, но всё же, занимаюсь на практике. Опыта с gui
конечно мало. Да и много подвешенных вопросов.

Для разработки gui на .net windows forms, сделано много плюшек. Я
разобрался, далеко не со всеми, но всёже...

q: Как расчитать размеры контрола?
a: по большей части они сами всё считают. У них есть свойство AutoSize.

q: если надо приклеить контрол к краю или растянуть?
a: свойства Dock и Anchor, позволяют прикреплять контрол к родительскому
окну.

Главное окно, помогает сделать контрол ToolStripContainer. Этот контрол сам
позиционирует верхнюю часть (например меню), нижнюю часть (например строку
состояния), боковые части и главную центральную часть.
То есть нужно добавить контролы. Включить тем контролам Dock в состояние
Fill. Обычно проблем нет. Но это, если нужно чего-то простое, вроде
текстового редактора.

Если нужно разложить контролы, как бы в таблицу, то помогает
TableLayoutPanel. Каждый контрол, помещается в отдельную ячейку.
Контралам надо бы указать Dock = Dock.Fill и включить AutoSize.
Число рядов и столбцов настраивается.
Очень удобно делать какие-нибудь анкеты. В первом столбце Label, во-втором
TextBox. Выходит аккуратненько.

Если нужно накидать контролов и утрамбовать их, помогает контрол
FlowLayoutPanel. Каждый контрол размещается рядом с другим контролом.
Настраивается направление упорядочивания. Слева направо или наоборот. Сверху
вниз или наоборот.
У контролов опять надо выставить свойства полей Dock и Autosize.
Применение разнообразно. Общая группировка контролов в центральной части
окна. Неплохо получаются панельки с несколькими Button.

На счёт Dock, Anchor и AutoSize у меня остаются непонятки. Ибо страшная
магия и нужен бубен. Если кто пояснит конкретнее, буду благодарен.

Теперь об арефмитическом позиционировании. С этим мороки чуть больше, зато
меньше шаманской магии. Соответственно, поведение контролов более
предсказуемое.

Кстати, эта часть, касается так же windows api, который я обычно использую.

Особо хочу отметить, что ни в коем случае, нельзя прибивать контролы
гвоздями. Размеры окна меняются. Разрешение мониторов меняются. Если
программа на одном компьютере смотрится, то на другом превращается в
кошмарик. Чтобы прогибатся под изменчивый мир, только динамические
вычисления.

Для .net windows forms, я не до конца уверен. Хочется обрабатывать событие
Resize. Но это, как-бы нерекомендовано. Оно вообще для чего-то другого. Сам
же макет, нужно обрабатывать в событии Layout.
Для windows api обрабатываем сообщение WM_SIZE и нормуль.

В обработчике, первым делом узнаём размер клиентской области окна. Именно
клиентской, она чуть меньше чем общий размер окна. Всякие бордюры, остаются
снаружи. Ну и контролы позиционируются именно относительно клиентской
области.
Левый верхний угол, имеет координату 0, 0.
В .net размер получаем свойством формы ClientSize. Для windows api функцией
GetClientRect. В итоге мы узнаём width - ширину окна и height - высоту окна.
Можем их считать координатами правого нижнего угла. Ну и соответственно
будем отталкиватся от них.

Если нам нужно разделить окно на две равных части и поместить два контрола,
то используем простейшую математику.

// разделим высоту рабочей области попалам
int hcenter = static_cast< int >( work.height / 2 );

// первый дочерний контрол займёт верхнюю половину
child1.top = 0; // верхний край совпадает
child1.left = 0; // левый край совпадает
child1.width = work.width; // ширина совпадает
child1.height = hcenter; // а высота равна половине рабочей области

// второй дочерний контрол займёт нижнюю половину
child2.top = hcenter; // верхний край по центру рабочей области
child2.left = 0; // левый край совпадает
child2.width = work.width; // ширина совпадает
child2.height = hcenter; // до самого нижнего края

Таким же образом можно провести не одну, а несколько псевдолиний. Причём не
только по горизонтали, но и по вертикали. Разделили область экрана и
воткнули что нужно.

Понятно, что этого недостаточно, для красивого интерфейса. Ну, ладно, за
красивостями надо стучатся к дизайнерам. А нам хоть как-то сделать, чтоб
было аккуратно и более-менее нормально.

Растягивать можно соответствующие контролы, вроде списков, многостроковых
редакторов и так далее. А вот для кнопок и меток, надо бы высчитать
пропорцию.

В самом простом случае, можно расчитать стандартный размер кнопки и далее
его подставлять.
int x_button = work.width / 50;
int y_button = work.height / 20;
Кнопка с такими размерами, будет маштабироватся относительно окна.
Уменьшатся или увеличиваться. Вроде как дикость, но приемлимо. Пропорции
подбирайте сами. Я цифры взял с потолка.

Если хочется кинуть понты, то тут надо ориентироваться на размер шрифта. В
.net windows forms собственно AutoSize занимается этим самостоятельно.
А вот для windows api это огромный гемор. Нужно получить контекст окна GetDC
или аналогичную функцию. Затем указав текст, получить его визуальные размеры
с указанным шрифтом. Причём размеры с погрешностью. Так что прибавляем ещё
плюс лапоть.
Потом, этот лапоть колибруется, при помоще зрячего товарища. Но в итоге,
кнопка получает размер, наиболее оптимальный. И ей уже не страшны изменения
шрифта или разрешения экрана.

А дальше, опять математика, расчитываем, что и куда впихнуть на экране.
Часть контролов у нас имеют рекомендуемые размеры, а часть изменяемые.

int y_label = static_cast< int >( work.height / 50 ); // стандартная высота
метки, которую мы как либо вычислили

Теперь втиснем метку, над child1, ужав его соответствующим образом.

lbl1.top = 0; // верхний край совпадает
lbl1.left = 0; // левый край совпадает
lbl1.width = work.width; // ширина совпадает
lbl1.height = y_label; //стандартная высота метки

child1.top = y_label; // верхний край под меткой
child1.left = 0; // левый край совпадает
child1.width = work.width; // ширина совпадает
child1.height = hcenter - y_label; // а высота равна половине рабочей
области, причём уменьшенной на высоту метки

Вот, примерно как-то так. Понятно, что код далеко не реальный.
Позиционировать дочерний контрол для windows api, лучше функцией MoveWindow
или SetWindowPos.
А для .net windows forms задавать его в свойствах Size и Location.

Не забывайте учитывать бордюры у контролов. Они внутри!

А вот касательно отступов, между контролами.... Ну... Вопрос, наверное
релегиозный. Тут уже советовали делать отступ в 10 пикселей. Пологаю цифра
взята с потолка.
Я же с того же потолка, на отступы вообще плюнул. Но, в некоторых случаях,
зрячие товарищи, рекомендовали всё же увеличить отступ на пару пикселей.
Именно в некоторых, ткнув пальцем в контролы, которые нужно раздвинуть.

ЭЭм... Нда, следовало бы ещё уточнить, что для windows api вообще-то есть
две системы координат.
Нативные координаты считаются в пикселях. А вот диалоговые координаты
считаются в юнитах. Юнит же зависит от разрешения экрана и выбранного
размера шрифта.
Так что этот момент следует учитывать. В пиксилях считать сложнее, но в
основном, функции требуют именно их.
В юнитах считать проще, поскольку можно наплевать на размер шрифта и
разрешение. Но придётся переводить единицы туда и обратно, и можно
запутаться.

Вот, примерно, как-то так. Если я ошибаюсь, то поправьте меня!

Ответить   Tue, 22 Mar 2016 09:31:06 +0300 (#3384884)