пятница, 14 июня 2013 г.
Закладки из ToolStrip-а
воскресенье, 2 июня 2013 г.
Как сравнить строки с "*" и "?"
Зачастую встает задача сравнить две строки с использованием так называемых "wildcard" - спецсимволов "*" (произвольное количество любых символов) и "?" (один любой символ). Конечно, есть регулярные выражения. Но что если маску должен задавать пользователь, а в сложных выражениях просто нет необходимости. На этот случай я сделал себе маленький класс-помощник. Выкладываю тут - вдруг, еще кому пригодится.
public static class StringHelper
{
static bool CompareWithPart( string source, string part )
{
if( source.Length != part.Length )
return false;
for( int i = 0; i < source.Length; i++ )
if( source[i] != part[i] && part[i] != '?' )
return false;
return true;
}
static bool ConsumePart( ref string source, string part, bool first, bool last )
{
if( part == string.Empty )
{
if( last ) source = "";
return !(first && last);
}
if( last )
{
var subStr = first ? source : source.Substring( Math.Max( 0, source.Length - part.Length ) );
source = "";
return CompareWithPart( subStr, part );
}
if( first )
{
var len = Math.Min( part.Length, source.Length );
var subStr = source.Substring( 0, len );
source = source.Substring( len );
return CompareWithPart( subStr, part );
}
for( int i = 0; i <= source.Length - part.Length; i++ )
if( CompareWithPart( source.Substring( i, part.Length ), part ) )
{
source = source.Substring( i + part.Length );
return true;
}
return false;
}
public static bool CompareWildcard( this string source, string mask )
{
if( source == null )
throw new ArgumentNullException( "source" );
if( mask == null )
throw new ArgumentNullException( "mask" );
var parts = mask.Split( '*' );
for( int i = 0; i < parts.Length; i++ )
if( !ConsumePart( ref source, parts[i], i == 0, i == parts.Length - 1 ) )
return false;
return true;
}
public static bool CompareWildcard( this string source, string mask, bool ignoreCase )
{
if( source == null )
throw new ArgumentNullException( "source" );
if( mask == null )
throw new ArgumentNullException( "mask" );
return ignoreCase
? source.ToLower().CompareWildcard( mask.ToLower() )
: source.CompareWildcard( mask );
}
}Использовать этот класс очень просто. Например так: Console.WriteLine( "Мама моет раму".CompareWildcard( "*раму" ) ); Console.WriteLine( "Мама моет раму".CompareWildcard( "Мама*" ) ); Console.WriteLine( "Мама моет раму".CompareWildcard( "*моет*" ) );Есть вопросы? Пишите в комментарии.
суббота, 11 мая 2013 г.
Ветвление проекта, конфигурации и AssemblyName
Постановка задачи
Рассмотрим классическую задачу. Предположим, вы ведете проект средних размеров на платформе .Net. Ведете достаточно давно и ваша команда проделала приличную работу. По сути, ваш проект уже на стадии тестирования - дописываются "фантики", исправляются "баги". И вдруг! приходит команда сверху - нужна демо-версия! Другими словами, вам теперь придется сопровождать два варианта проекта, отличающиеся между собой мелкими деталями. Естественно вас не устроит вариант поддерживать две совершенно независимые разработки. Рассмотрим два варианта решения данной задачи.Классический вариант. Два Application
Предположим, что наше решение имеет более-менее классический вид. Т. е. состоит из N-го количества библиотек (Class Library) и одного собирающего проекта (Application). Наиболее естественным решением нашей задачи будет создание двух собирающих (Application) проектов, в которых и будет находиться вся логика ветвления. При этом все общие части будут располагаться в библиотеках. Однако, такой подход потребует существенного рефакторинга всего решения - придется четко разделить код на общий и зависимый от версии. При этом, в Application должен будет остаться только второй вариант. Не могу сказать, что данные сложности непреодолимы, но их наличие мотивирует рассматривать и другие подходы тоже.
Рассмотрим другой подход - конфигурации. Для начала откроем Configuration Manager и добавим конфигурацию
Далее расставляем директивы процессора везде, где это требуется. Например, так:
1. В секции PropertyGroup объявляем какую-нибудь переменную
Ну и, конечно же, надо помнить, что сама студия по-прежнему не поддерживает наши добавки. Поэтому, запустить демо-версию из-под нее не удастся. Зато, компилятор отработает правильно и пакетная компиляция даст нам сразу обе версии - основную и демо.
Ветвление на уровне конфигураций (Configuration)
Предположим, что у нас обычная команда кодеров, а не супер-слаженная группа гениальных программистов. Т. е. код структурирован плохо, библиотеки ссылаются друг на друга, в главном окне программы откуда-то взялась библиотечная логика. А про внедрение зависимостей (DI) половина нашей группы вообще не слышала. К сожалению, такая ситуация встречается сплошь и рядом и отрефакторить такое решение весьма не просто.Рассмотрим другой подход - конфигурации. Для начала откроем Configuration Manager и добавим конфигурацию
Затем, идем в свойства проекта, в секцию "Build" и добавляем константу компилятора для нашей конфигурации
#if DEMO
Console.WriteLine( "Это демонстрационная версия" );
#endif
Или так#if DEMO [assembly: AssemblyTitle( "ConsoleTest.Demo" )] [assembly: AssemblyDescription( "Это демонстрационная версия проекта" )] #else [assembly: AssemblyTitle( "ConsoleTest" )] [assembly: AssemblyDescription( "Это основная версия проекта" )] #endif [assembly: AssemblyConfiguration( "" )]Ну и последний штрих - это название конечного продукта. Естественно, название конечного exe-файла для демо-версии должно отличаться от штатного названия. Иначе, мы будем просто путаться. Для этого нам нужно, что бы AssemblyName в разных конфигурациях был разным. Однако, Visual Studio не предоставляет нам такой возможности. Тем не менее, компилятор считывает название из файла проекта (.csproj), который мы можем править вручную в любом текстовом редакторе. Каждая конфигурация будет соответствовать секции
<PropertyGroup ...>
...
</PropertyGroup>
Поэтому, самое простое решение будет найти секцию, соответствующую нашей демо-версии и вписать внутрь элемент AssemblyName<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Demo|AnyCPU'">
...
<AssemblyName>ConsoleTest.Demo</AssemblyName>
...
</PropertyGroup>
После этого достаточно запустить VS, открыть наше решение и скомпилировать все пакетным построением, отметив галочками основную и демо конфигурации. Однако, читая форумы, я обнаружил, что некоторые версии VS данный подход воспринимают плохо. Если у вас тоже возникли сложности, то можно использовать более длинный вариант:1. В секции PropertyGroup объявляем какую-нибудь переменную
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Demo|AnyCPU'">
...
<isDemo>true</isDemo>
...
</PropertyGroup>
2. После всех PropertyGroup добавляем секцию Choose, в которой и задаем наш AssemblyName<Choose>
<When Condition=" '$(isDemo)' == 'true' ">
<PropertyGroup>
<AssemblyName>$(AssemblyName).Demo</AssemblyName>
</PropertyGroup>
</When>
</Choose>
В некоторых случаях, такой подход работает стабильнее.Ну и, конечно же, надо помнить, что сама студия по-прежнему не поддерживает наши добавки. Поэтому, запустить демо-версию из-под нее не удастся. Зато, компилятор отработает правильно и пакетная компиляция даст нам сразу обе версии - основную и демо.
суббота, 27 апреля 2013 г.
WiX. Минимальный набор диалоговых окон
На днях у меня возникла задача написать простейший инсталятор к простенькой програмульке. Решил использовать WiX - благо, со студией интегрируется и, вроде как, все должно быть легко. Почесав затылок, подумал, что не плохо было бы прикрутить стандартные окошки - "приветствие" и "выбор папки". Больше, в принципе, ничего не надо. Добавил в References WixUIExtension.dll, начал разбираться со стандартными наборами диалогов и с ходу напоролся на неприятный момент - в классический набор запихнули диалог с лицензий. Так уж сложилось, что авторы WiX-а убеждены, что лицензия вещь офигенно важная и без нее ну никак. Для моей же задачки лицензия была, как бельмо на причинном месте. Пришлось "лечить". Как ни странно, простого решения не нашлось - никаких настроек и/или флажков. Пришлось лезть в исходники и править ручками файлик WixUI_InstallDir.wxs. Дабы не было конфликтов, предварительно переименовал его в WixUI_Simple.wxs. Затем вычистил все, что касается лицензии и аккуратненько перекинул ссылочки. Получилось вполне прилично. Для использования достаточно добавить в проект WixUI_Simple.wxs и добавить пару строчек в Product.wxs:
Скачать WixUI_Simple.wxs
... <property id="WIXUI_INSTALLDIR" value="INSTALLFOLDER"></property> <uiref id="WixUI_Simple"/>INSTALLFOLDER - это идентификатор конечной директории. Т. е. он прописывается в теге Directory
... <Directory Id="INSTALLFOLDER" Name="ProductName">
Скачать WixUI_Simple.wxs
пятница, 26 апреля 2013 г.
Первое знакомство с VS2012
Время летит и прогресс не стоит на месте. Технологии развиваются с бешеной скоростью. Пытаюсь угнаться. Пару месяцев назад дошли руки до Visual Studio 2012. Установил. Запускаю. С замиранием сердца жду чуда. Супер-новый интерфейс, новые возможности, новый дизайн.. Но, что это? Все такое серенькое и невзрачное. Но почему? Ок. Наверное, яркий дизайн просто вышел из моды. Будем знать - надо будет учесть в своих проектах это "ноу-хау".
Хорошо. Менюшку привели в "божеский" вид. Начинаем работать.
волшебный бубен сторонний продукт под названием "WiX". Полноценную документацию на родном языке найти не удалось.. как, впрочем и удобного UI. Пришлось снова курить форумы и лазить по буржуйским сайтам. Через 2 дня дистрибутив таки был собран. Не могу сказать, что WiX мне не понравился. Но все его возможности можно вкуривать ни один месяц.
CAPS в меню
Тут мой взгляд остановился на главном меню. Тут определенно было что-то не так. Меню резало глаз. После десяти секунд одупления до меня дошло - большие буквы. Ужас! Ок. Я спокоен и гугл мне поможет. Пара минут поиска выдала пару статей и проблема решилась простенькой настройкой реестра.Для возврата старого меню внесите следующие изменения в реестр Windows:(если лень копаться в реестре вручную, то можно скачать файлик SuppressUppercaseConversion.reg и просто запустить его)
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0\General\SuppressUppercaseConversion REG_DWORD value: 1
Хорошо. Менюшку привели в "божеский" вид. Начинаем работать.
Макросы
Но что это? Не работают горячие клавиши? Да нет - некоторые работают. Не перенеслись настройки из предыдущей студии? Тоже как-то странно. Еще пара минут плясок с бубном и до меня доходит - ПЕРЕСТАЛИ РАБОТАТЬ МАКРОСЫ!!! Т. е. совсем перестали - даже редактор макросов из меню исчез. Активно гуглю и выясняю, что макросы реально убрали. Убрали совсем, безвозвратно и надежды на сервиспаки нет никакой. Печально.Скорость
А вот скорость порадовала - тупить стало меньше. Я, по-началу, по своей наивности даже подумал, что "мелкомягкие" научились оптимизировать код. Однако, после пары дней более пристального изучения, понял, что нет - оптимизации как таковой нет. Просто тяжелые задачи распараллелили по потокам. Т. е. теперь, пока грузится дизайнер, основной поток не висит и можно что-то делать. Ну хоть так - все же, лучше, чем было.Инсталяшки
Итак, я написал свой первый проект. К этому времени я уже успел привыкнуть к новому дизайну и он мне даже стал нравиться. Пора собирать дистрибутив. Проект простенький, без заморочек, поэтому на дистрибутив я выделил 1.5 часа - как мне тогда казалось "с приличным запасом". Но тут меня ждал новый сюрприз - старого доброго конструктора дистрибутивов больше нет. Снова лезу в гугл, листаю статьи, курю форумы. Выясняю, что теперь для сбора инсталяшки народ используетИтог
А зори здесь тихие...
Подписаться на:
Сообщения (Atom)


