Залепа №6. Вечный календарь и пляска с бубном.

Ситуация:

Есть некоторое событие, достаточно жестко привязанное к конкретным датам. Например поездки с проверками некоего крутого начальника. И этому начальнику надо знать, в какие поездки брать с собой помощника, а в какие нет. Сознаюсь, задача слегка синтетическая, но поразмыслив немного, вы сами найдете кучу вполне реальных задач, сводящихся к этой.

Итак, секретарше в календаре надо видеть дни, в которые начальник выезжает и как-то особо метить дни, в которые начальник будет брать с собой помощника. Как это сделать программно? Да очень просто, ведь у нас есть супер-мега-контрол MonthCalendar.

Кроме всех "обычных" прелестей, эта мега-кульная штука умеет еще и метить дни. Причем метки можно ставить аж трех разных типов, включая ежемесячные события. К слову сказать, диапазон у этого календаря тоже удовлетворит любого - он умеет работать с датами до 9999 года, так что очередная "проблема 3000" нам уже не грозит. Можем писать софт даже не на века, а на тысячелетия! Действительно огромное достижение инженерной мысли.

Ну вы уже конечно догадались, что и здесь есть все то же пресловутое "НО".

На этот раз это "НО" вылилось в то, что все эти разнообразные отметки (напомню, что их аж три разных типа) на экране выглядят абсолютно одинаково. Причем не просто одинаково, а написаны тем же шрифтом, что и обычные даты, только слегка жирненьким.

Майкрософт напихало в этот контрол кучу свойств и методов, из которых на практике используется едва ли 5%, но пожалело ввести хотя бы одну переменную, чтобы задать цвет отмеченных дат. Жмоты! Правда, в вышеописанной задаче, одним цветом мы бы все равно не обошлись, но было бы хоть что-то. А так нет. :(

Теперь финальный аккорд.

Контрол MonthCalendar (кстати, как и многие другие в .NET) не имеет обработчика для собственного рисования дат. Этим МС вбило последний гвоздь в нашу попытку с помощью их мега-библиотеки решить поставленную (к слову сказать достаточно простую) задачу, по созданию удобного интерфейса.

Выход только один - писать свой контрол, со всеми вытекающими. :(


Залепа №5. Мы строили, строилаи и ... ничего не построили :(

Одна из особенностей .NET, которую создатели превозносят до небес - строгий контроль типов. Это значит, что теперь что попало чему попало не присвоишь. Хорошо или плохо это - тема отдельная и достаточно большая. Но я хочу поговорить немного о другом.

Если теперь система сама с точностью до запятой знает какой тип имеет каждая переменная, то зачем программисту вручную делать приведение типов? Почему компилятор не может взять эту рутину на себя?

Давайте рассмотрим пример.

Пример кода:
MyClass r = new MyClass();
Object o = r;
MyClass x = o; // (1)
MyClass x = (MyClass) o; // (2)

Вариант (1) не компилируется, в то время как (2) проходит компиляцию на ура. Но как работает в этом случае скомпилированная программа? А работает она так: когда дело доходит до преобразования Object в MyClass, CLR проверяет, соответствует ли значение в переменной о типу MyClass и, если это так, то производит присваевание. Иначе мы получим исключение.

Подчеркну, что это происходит во время выполнения, а не компиляции. Т.е. если во время разработки мы напишем что-нить типа: String x = (String) o;, то это тоже откомпилируется нормально и про ошибку мы узнаем только в рантайме.

Возникает вопрос:
если на момент компиляции мы можем написать любую чушь, и в откомпилированной версии все равно будет присутствовать проверка правильности типа, а ошибки начнут вылазить только когда CLR проверит эти самые типы, то почему нельзя просто записать MyClass x = o; вместо MyClass x = (MyClass) o; ведь результат компиляции и результат работы все равно будет одинаковым?...

Впрочем, глядя на патологическую любовь мелкомягких к длинным идентификаторам и десяткам дублирующих функций, можно с уверенностью сказать, что они все страдают графоманией - болезнью, которая заставляет пациента писать, писать и писать. При этом совершенно безразлично, что именно писать - повторные указания на преобразование типов, сотни одинаковых модификаторов доступа или out'ы и "ref'ы, которые совершенно бесполезны. Главное писать и побольше. Этой же болезнью они пытаются заразить и конечных пользователей своих супер-продуктов - нас с вами, выдавая свою патологию за мега-знание и ультра-крутость. :(


Залепа №4. Деревянный TreeView.

Как вы думаете, должно ли зависеть поведение контрола от его начального состояния? Не поняли что я имею в виду? Сейчас объясню.

Дамы и господа, встречайте! На сцене TreeView - самый деревянный контрол от майкрософта! Именно деревянный, с маленькой буквы и без кавычек! И вовсе не потому, что он рисует деревья, а целиком и полностью исходя из его поведения.

Итак, начальное состояние дерева в контроле - свернутое. Казалось бы разницы в этом нет - свернутое-развернутое - все равно и контрол должен вести себя одинаково. Для нас возможно и так, но вот майкрософт в этом наверное видит некий глубокий смысл, ибо их TreeView ведет себя абсолютно по-разному именно исходя из начальной степени свернутости дерева.

Но давайте все по-порядку.

Появилась у меня необходимость раскрасить надписи в TreeView. Задача эта тривиальна и встречается довольно часто. По крайней мере в грамотных интерфейсах. Событие для этого тоже предусмотрено - TreeView.DrawNode.

Итак, вот примерный обработчик (для простоты все лишнее я выбросил):

Пример кода:
private void Tree_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
  if ((e.State & TreeNodeStates.Selected) != 0)
  {
    e.Graphics.FillRectangle(SystemBrushes.HotTrack, e.Bounds);
    e.Graphics.DrawString(e.Node.Text, Tree.Font, 
        Brushes.Yellow, e.Bounds);
  }
  else
  {
    e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
    e.Graphics.DrawString(e.Node.Text, Tree.Font, Brushes.Red,
        e.Bounds);
  }
}

Запускаем и наслаждаемся разноцветными надписями. Вроде все работает правильно. Но...

Но это только до тех пор, пока вы не попытаетесь развернуть какой-либо из узлов дерева. При этом происходит что-то по-настоящему удивительное - названия вложенных пунктов из разворачиваемого узла пишутся ПОВЕРХ КОРНЕВОГО УЗЛА, т.е. в левом верхнем углу окна!

Этот эффект наблюдается только при ПЕРВОМ разворачивании любого узла. Последующие сворачивания/разворачивания этого же узла уже аномалий не проявляют. Если дерево изначально развернуто, то глюк не наблюдается вообще.

Прикольно? По-моему да! :)

Но это еще не все! Самое вкусное я приберёг напоследок.

Посмотрите внимательно на приведенный выше код. Видите, в нем до печати текста происходит стирание площадки под текст. Т.е. по логике вещей, если бы в событие просто были переданы неверные координаты области текста, то было бы все понятно - вывод идет не в то место окна и потому надписи появляются абы где. Но на практике все намного интереснее!

Дело в том, что СТИРАНИЯ площадки под надписью в левом верхнем углу почему-то НЕ ПРОИСХОДИТ!!!

Т.е. получается, что координаты получены верные и FillRectangle срабатывает правильно, а вот следующая за ним команда DrawString словно срабатывает дважды - первый раз пишет строку в правильных координатах, а потом каким-то магическим образом повторяет надпись, но уже в координатах 0,0 окна TreeView.

Объяснить это загадочное поведение я не берусь. Ибо в заклинаниях культа Ктулху я не силен, особенно когда эти заклинания наложены мега-пряморуким софтверным гигантом Microsoft.

[Дополнение от 14 декабря 2007]

Как показало время, вышеописанный глюк не так прост. Он оказывается имеет несколько разных обличий.

То, что написано выше наблюдалось на тестовом дереве, в котором было около десятка узлов. Когда же это количество достигло полу-сотни глюк внезапно исчез. От неожиданности я чуть было не усомнился в собственном рассудке.

Решив отложить звонок в психушку на потом, я, как заядлый трудоголик, продолжил добавлять узлы. И тут попался пункт, название которого вышло за правый край контрола. Естественно снизу тут же возник скрол-бар, который я сразу же решил обкатать. Ну не мог я себе отказать в таком удовольствии :)

И что же я увидел?

А увидел я, что глюк мой снова вернулся, но уже не в виде скромного бага, а превратился в сильно мутировавшего монстра. Надписи, которые уезжали за край контрола (когда я тянул бегунок скрол-бара) больше не выезжали назад! На экран вернулись только иконки узлов и символы "Плюс" и "Минус", обозначающие состояние свернутости. Ни фонов, ни надписей, пустота.... :(

Такого развития событий моя и так изрядно измотанная психика уже вынести не смогла. Истерически отхохотав, я решил посмотреть, что же будет дальше и снова взялся за пополнение горе-дерева....

...не знаю, как там будет дальше, но сейчас в дереве около 400 узлов - глюка не наблюдаю. Ни одного символа в коде не менял. Наверное пора мне к психиатру... :(


Fast: [10] [20]

Этот сайт полностью окупает себя, хотя его ТИЦ=10, а PR=2. Хотите знать, как он это делает? Хотите чтобы Ваш сайт чарез пол-часа тоже начал на полном автопилоте приносить деньги?
Регистрируйся здесь и здесь и начинай получать деньги со своего сайта!