среда, 26 августа 2009 г.

Windows.Forms: ложимся в трей

Не так давно встала передо мной задача - уложить программу на C# в трей. При разработке использовались стандартный Windows.Forms из .net 3.5, хотя со времен .net 2.0 он практически не изменился. На моё счастье все оказалось очень просто. Попытаюсь объяснить основные шаги.
1. Размещаем компонент NotifyIcon на форме. У меня в приложении одна форма главная, остальные даже в таскбаре не отображаются, на главную форму и положил. NotifyIcon находится в ToolBox, вместе со всеми контролами... Параметры этого NotifyIcon говорят сами за себя, правда если не задать иконку, то в трее вообще ничего не появится :)
2. Маленько изменяем поведение формы. Для программ размещающихся в трее принято при сворачивании убирать окно из таскбара, а при щелчке на иконке в таскбаре - разворачивать. Поэтому пишем следующие обработчики:
- для сворачивания формы (эвент Form.Resize)
  1. private void MainForm_Resize(object sender, EventArgs e)
  2. {
  3.     if (FormWindowState.Minimized == WindowState) Hide();
  4. }
* This source code was highlighted with Source Code Highlighter.
- и для клика по иконке в трее (эвент NotifyIcon.Click)
  1. private void notifyIcon_Click(object sender, EventArgs e)
  2. {
  3.     Show();
  4.     WindowState = FormWindowState.Normal;
  5. }
* This source code was highlighted with Source Code Highlighter.
3. Изменим поведение при попытке юзера закрыть окно по крестику. Для этого используем обработчик эвента Form.Closing:
  1. private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  2. {
  3.  if (e.CloseReason == CloseReason.UserClosing)
  4.  {
  5.     e.Cancel = true;
  6.     Hide();
  7.     WindowState = FormWindowState.Minimized;
  8.  }
  9. }
* This source code was highlighted with Source Code Highlighter.
То есть, при щелчке по крестику наша форма просто свернется вниз, а завершение программы будет отменено. Правда на текущий момент эту программу можно только прибить из менеджера задач, что не очень хорошо. Поэтому:
4. Сделаем контекстное меню для иконки в трее. Компонент ContextMenuStrip размещаем на нашей форме, создаем в нем пункт "Выход" (и какие ещё необходимо). Затем скажем нашему NotifyIcon, что у него есть контекстное меню с помощью свойства NotifyIcon.ContextMenuStrip. А обработчик пункта "Выход" у нас будет таким:
  1. private void miClose_Click(object sender, EventArgs e)
  2. {            
  3.     Application.Exit();
  4. }
* This source code was highlighted with Source Code Highlighter.
То есть CloseReason в данном случае будет ApplicationExitCall.
Вот и всё. Приложение должно вполне успешно справляться со своей задачей "лежания" в трее.

пятница, 21 августа 2009 г.

Генераторы в C# или "бесполезное" yield

Язык о котором пойдёт речь вышел достаточно давно, однако, судя по реальному опыту общения, мало кто пользуется всеми имеющимися возможностями. Этот пост я посвящу ключевому слову yield в языке C# 2.0.
Сначала несколько слов о тенденциях развития С#. Вспоминая каким был C# 1.0, могу сказать, что это была калька с Java2SE, причем не сказать что удачная. Чистый ООП язык, практически слизаный с Java, без какой-либо собственной красоты и лоска, без своего шарма. Однако уже вторая версия очень сильно порадовала своими возможностями, а именно некоторым движением в сторону функционального программирования. Эта же тенденция существенно продолжается и в третьей версии (VS2008), в C# 4.0 (VS2010) функциональные возможности будут расширены ещё больше, при этом Microsoft добавляет в платформу .NET полноценный функциональный язык, который много лет разрабатывался Microsoft Research. По-моему говорит это о многом.
А теперь к делу!
Ключевое слово yield изначально было предназначено для облегчения разработки классических итераторов .NET, а именно классов, реализующих интерфейсы IEnumerator<T> и IEnumerable<T>. То есть, разработка своей коллекции, или способа обращения к существующей коллекции требовала разработки как минимум одного класса - итератора с интерфейсом IEnumerator<T>, по одному классу на каждый способ прохода коллекции. Довольно муторное и однообразное занятие.
И тут к нам на помощь приходит yield!
Небольшой пример. Пусть нам хочется получить все элементы списка находящиеся в диапазоне [3;5]. Вместо стандартного for напишем следующий кусок:

IEnumerator<int> GetIntervalled(List<int> Collection)
{
for(int i=0;i<Collection.Count;i++)
{
if(Collection[i]>=3 && Collection[i]<=5) yield return Collection[i];
}
}

Использовать только что написанный код можно как и обычно используются итераторы – foreach. При очередном выполнении yield return функция вернет управление вызывающей, и передаст соответствующий элемент. На самом деле компилятор по этому коду создаст виртуальный класс реализующий интерфейс IEnumeratot<t>, но нас это не должно интересовать. Подобная конструкция есть первый, маленький шажок в сторону функционального программирования – это один из вариантов продолжения (continuations), потому что визуально код, при повторном вызове, продолжает выполняться с того места где выполнение было окончено. Подобная конструкция в некоторых языках программирования (откуда она и была заимствована) называется генератором, отсюда и название заголовка :)
Конечно, приведенный выше пример абсолютно неинтересен, и ничем не отличается по смыслу от примера, приводимого в документации. Понять, зачем применять столь хитрую конструкцию крайне тяжело. Для того, чтобы стало немножко больше понятно зачем нужны такие вещи приведу пример из реального кода.
В формочку (Windows.Forms) выводятся некоторые сущности, а пользователь отмечает галочками те, которые ему необходимы. Мне хотелось получить отмеченные галочкой сущности, yield пригодился как нельзя кстати:

public IEnumerable<DatabaseImportCase> SelectedImportCases
{
get
{
for (int i = 0; i < lbImportPackages.CheckedIndices.Count; i++)
{
yield return ShownImportCases[i];
}
}
}

ShownImportCases – это список отображаемых сущностей, lbImportPackages – листбокс с галочками (CheckedListBox), в котором отображаются эти сущности. Естественно предполагается, что индексы отображаемых на форме и хранимых в памяти сущностей совпадают. Операции по получению элементов происходят с помощью «ленивых вычислений», то есть получение очередного элемента произойдет только тогда, когда вы его потребуете. Никаких виртуальных списков в памяти не строится. Итак, yield – это универсальный, очень удобный способ получения новых, произвольных по сложности итераторов. Основное его преимущество – уменьшение количества не имеющего смысловой нагрузки кода. То есть, использование этого приема, это просто syntax sugar, но вряд ли кто откажется от сладкого :) Безусловно появление LINQ, а именно LINQ to Objects существенно снизило ценность yield, поскольку практически все итераторы можно получить с помощью запросов LINQ, однако понимание работы такого рода продолжений крайне необходимо для понимания более сложных конструкций функционального программирования. Которые я постараюсь рассмотреть в одном из следующих постов.

понедельник, 17 августа 2009 г.

Восстанавливаем Grub после установки Windows

На днях решил попробовать установить Windows 7, и, вполне естественно столкнулся с проблемой. Сама Windows 7 встала без проблем, установка существенно проще чем в ХР (Vista не видел - ничего сказать не могу). Но, вполне в традициях Microsoft, семёрка не замечает никаких операционных систем на компьютере кроме Windows. Естественно мне хотелось сохранить всё, что у меня есть, а именно Windows XP и KUbuntu 9.04, ну и естественно настроить мультизагрузку.
Как и ожидалось после установки семерки Grub был затёрт напрочь, хотя выбор между XP и Windows 7 все же был. Ситуация осложняется ещё и тем, что у меня 2 жестких диска, и я абсолютно не помню как там у меня что загружается. Стандартное решение выдаваемое гуглом в поиске, а именно "grub-install" не прокатывает. По неизвестным мне причинам ни в одном блоге, который приводит grub-install как решение не написано что нужно передавать параметр - диск, на который ставить grub. Погуглив ещё малец, я нашел решение, которое сам когда-то использовал, но успешно забыл :)
Итак:
1. Загружаемся с Live CD
2. Монтируем раздел с линуксом на винте, примерно так:
cd /mnt
sudo mkdir linux
sudo mount /dev/sda1 /mnt/linux
sudo mount --bind /mnt/linux/boot /boot
3. Заходим в консоль команд Grub:
sudo grub
Дальше команды выполняются в консоли grub
4. Ищем где у нас стоит Stage1:
find /boot/grub/stage1
Вернется что-то типа (hdx, y), вот эти магические x,y и используем:
5. Говорим Grub'у где у него Stage1, и куда ему поставится:
root (hdx,y)
setup (hdx)

Итак grub мы восстановили. Осталось только добавить Windows 7 в список загрузки. Идем в /boot/grub/menu.lst, и смотря на описание загрузки Windows XP сооружаем примерно то же самое, сделав поправку "на ветер". У меня получилось вот такое:
title Windows 7 RC
root (hd1,5)
chainloader +1
makeactive

Всё. Пользуемся 3 ОС одновременно. Но лично я собираюсь перебираться из XP в семерку :)

пятница, 1 мая 2009 г.

Новая фишечка от Google в поиске

Надо было сегодня кое-что погуглить, и обратил внимание на некоторе обновление страницы поиска в гугле. Фактически добавили комментарии к результатам поиска, и возможность перемещать/удалять результаты поиска для повторного использования. Интересно будет посмотреть насколько это будет удобно в реальности, и как быстро подобная вещь появится у яндекса :)
И очень интересно когда же это было добавлено. Учитывая мою клиническую невнимательность к мелочам - вполне возможно пару месяцев назад :)


среда, 8 апреля 2009 г.

Наследование шаблонов в Django

В питоновском веб-фреймворке django встроен механизм наследования шаблонов страниц. Лично я считаю, что это очень спорная фишка и должна применяться с большой аккуратностью. Сейчас попробую объяснить почему.
Сначала определимся с терминологией. Этот механизм является именно наследованием, тем самым, которое используется в ООП. Этот вывод я сделал проанализировав получающиеся конструкции. Ведь фактически шаблон-наследник знает о структуре шаблона предка, хотя может и не знать детали реализации, ведь для того, чтобы переопределить (или доопределить) часть исходного шаблона нам потребуется указать имена тех частей, которые необходимо переопределить. Итак, рассмотрим маленкий пример:
Родительский шаблон:
<html>
<head>
<meta equiv="Content-type" content="text/html; charset=utf-8" />
<link type="text/css" rel="stylesheet" href="/static/Styles.css" />
{% block extrahead %} {% endblock %}
<title>{% block title %} {% endblock %}</title>
</head>
<body>
{% block content %} {% endblock %}
</body>
</html>
Шаблон наследник:
{% extends "base.html" %}

{% block extrahead %}
<script type="text/javascript" src="additionalscript.js"></script>
{% endblock %}

{% block title %}
Супер заголовок
{% endblock %}

{% block content %}
...
{% endblock %}

Казалось бы все по джанговски и замечательно. Но! Работа с шаблонами реализованная таким образом таит немало неприятностей, но самая главная из них - шаблон более низкого уровня по иерархии знает о структуре шаблонов более высокого уровня. И не просто знает (что не стало бы проблемой при использовании подходящих конвенций), а просто обязан использовать эти сведения. Получается что шаблон более низкого уровня сильно связан с более абстрактным шаблоном, что не есть правильно и лишает подобную структуру гибкости.
Выход? Есть выход! Наследование шаблонов безусловно мощная вещь и позволяет избежать дублирования кода, но использовать её необходимо с аккуратностью, особенно в больших проектах. Использовать также, как и наследование ООП, проверяя действие принципов используемых в ООП. Лично я для себя проверяю возможность наследования отношением "является", наследование можно применять в случае, если в человеческом языке корректной будет фраза "<наследник> является <предком>". Таким образом, приведенный выше пример корректен, так как шаблон-предок, фактически, является "обобщенной страницей", а шаблон наследник - "конкретной страницей". Но в случаях добавления дополнительных элементов на страницу, контролов и прочего, на мой взгляд более правильным и менее пахнущим будет использование агрегации с использованием механизмов custom tags.

понедельник, 6 апреля 2009 г.

Как подружить Mail.RU Agent и Kopete

Возникла необходимость подружить популярный в россии мессенджер Mail.RU Agent (MRA) с Linux. Так уж сложилось, что использую я KDE4, соответственно мессенджер у меня - Kopete. То есть требуется сделать так, чтобы используя Kopete можно было легко и непринужденно общаться с контактами использующими Mail.RU Agent. Ведь не всякую девушку убедишь перейти на "идеологически правильный" Jabber :)
Малец погуглив нашлось вот это, но люди, считающие себя очень умными, зачастую не могут объяснить по человечески :) Я вот вроде бы не чайник, но фраза: "зарегестрируйся сначало на jabber.ru , потом войди туда, и в обзоре сервисов введи jabber.ru и там в списке будет mrim.jabber.ru и на нем и регистрируй транспорт" для меня загадка.
Делюсь своим решением, постараюсь объяснить кратко, но понятно. Сначала оговорюсь, что использую я KUbuntu Linux 8.10, и Kopete из его репозиториев.
Решение поставленной задачи заключается в использовании сервера jabber.ru как шлюза для передачи сообщений из/в MRA. Откуда шаг номер раз:
1. Регистрируемся на jabber.ru
Шаг номер два не менее очевиден, исходя из краткого описания предложенного решения:
2. Добавляем вновь созданную учётку в Kopete
Выглядеть должно примерно так (красным выделена вновь созданная учётка):А вот теперь настало время для расшифровки той фразы. Третий шаг следует как раз из нее:
3. Необходимо добавить сервис mrim.jabber.ru
Для этого в окне контакт-листа ищем кнопку , и смело и решительно жмём её. Должно выползти следующее:
Правой кнопочкой щёлкаем на учётке Jabber.RU, и выбираем пункт "Службы". Появляется диалоговое окошко, в котором сначала жмём "Запросить сервер", и если всё до этого было сделано правильно, то появится список сервисов, из которых нам интересен mrim.jabber.ru. Выбираем его, снова клацаем правой кнопочкой, "Зарегистрировать". Закрываем окошко, и нам приходит сообщение что mrim.jabber.ru добавил нас в свой контакт-лист. Замечательно! Добавим и мы его. Как только он появится в вашем контакт листе на вас посыпятся сообщения о том, что вас добавили те, кто находится в вашем контакт-листе в MRA. Всё. С этого момента должно работать. Заметьте, что у вас появилась ещё одна учётка:

Поехали!

Вообще говоря я достаточно давно думал завести свой блог, посвященный различным вопросам IT-сферы, и вот всё таки разродился. Постараюсь выкладывать тут материалы, которые мне показались интересными, и могут кому-то оказаться полезными.
Мои интересы в IT-сфере - это в первую очередь программирование с использованием различных инструментов, для различных платформ и целевых аудиторий; интересуюсь также Linux'ом, и общими вопросами IT.
Буду рад осмысленным комментариям, критике и корректным дискуссиям. Кащенизм и прочие проявления интеллектуальной импотенции будут нещадно удаляться.