[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 вообще-то есть
две системы координат.
Нативные координаты считаются в пикселях. А вот диалоговые координаты
считаются в юнитах. Юнит же зависит от разрешения экрана и выбранного
размера шрифта.
Так что этот момент следует учитывать. В пиксилях считать сложнее, но в
основном, функции требуют именно их.
В юнитах считать проще, поскольку можно наплевать на размер шрифта и
разрешение. Но придётся переводить единицы туда и обратно, и можно
запутаться.
Вот, примерно, как-то так. Если я ошибаюсь, то поправьте меня!