Технические приемы
Небольшой пост-шпаргалка. Буду предельно краток.
Допустим, у нас есть некий перечисляемый тип. Например:
TFieldType = (ftUnknown, ftString, ftSmallint, ftInteger, ftWord, // 0..4
ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, // 5..11
ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo, // 12..18
ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString, // 19..24
ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob, // 25..31
ftVariant, ftInterface, ftIDispatch, ftGuid, ftTimeStamp, ftFMTBcd, // 32..37
ftFixedWideChar, ftWideMemo, ftOraTimeStamp, ftOraInterval, // 38..41
ftLongWord, ftShortint, ftByte, ftExtended, ftConnection, ftParams, ftStream, //42..48
ftTimeStampOffset, ftObject, ftSingle); //49..51
Нам необходимо каким-то образом вывести тип поля из реального набора данных. Очевидно, проще всего название типа получить в виде строки. Для этого мы можем использовать следующую конструкцию:
GetEnumName(TypeInfo(TFieldType), Ord(aDataSet.Fields[I].DataType));
На выходе получаем строку, содержащую название типа для поля aDataSet.Fields[I].
Одним из главных преимуществ Berlin‘а, является визуальный редактор ListView Item. За три месяца с момента выпуска продукта о нём не написал только ленивый. И он действительно удобен. Он экономит “массу времени, сил и духовной энергии…” (с). Но есть одна проблема, с которой столкнулся я, и судя по записям в многочисленных форумах, не я один. Как только свойство Item Appearance принимает значение DynamicAppearance, при работе с представлениями формы начинается чехарда.
К сожалению, времени на всё запланированное банально не хватает. Смена хостера, обновление движка Delphifeeds и многое другое. Тем не менее, начал писать подробный мануал по созданию полноценного мобильного приложения и даже собрал команду болельщиков в этом начинании. Но быстро сказка сказывается, да не быстро дело делается. А пока решил сделать несколько постов в новую рубрику. Как следует из названия, здесь будут публиковаться “зарубки на память”, небольшие лайфхаки и прочие материалы, которые назвать оригинальными можно с натяжкой, но под рукой иметь полезно. Для начала маленький лайфхак для начинающих разработчиков.
Представьте, что в режиме проектирования вам нужно клонировать на форме какой-то сложный объект, например, DataSet. Казалось бы всё просто. Выделяете объект и копируете его в буфер обмена (Ctrl+C). Затем вставляете его на форму и переименовываете новый объект. Но здесь есть один не совсем приятный момент. Если мы откроем редактор полей, то обнаружим, что поля вновь созданного объекта не имеют осмысленных названий, а называются примерно так, как показано на рисунке.
Кроме типа поля из названия смысловой информации мы не получим. Конечно, в данном случае есть простое решение. Удаляем все поля, затем копируем все поля из исходного DataSet’а и вставляем в новый DataSet. Все поля приобретут осмысленные названия. которые мы сможем использовать в коде.
Но что делать, если копирование полей не поддерживается, как, например в TdxMemData от DevExpress? Конечно, можно создать заново все поля вручную, но значительно проще отредактировать код формы. Для этого я использую Notepad++. В принципе подойдёт любой редактор, с поддержкой глобальной замены текста. Всё просто. В Delphi IDE мы выбираем текстовое представление формы. Копируем и переносим код интересующего нас объекта вместе с вложенными объектами в редактор и производим замену по шаблону. После этого вставляем код нового объекта в исходный код формы. Что бы не возиться с описаниями объекта в pas-модуле, просто вырежьте и снова вставьте его на форму.
Создание клиентского приложения
Поскольку в нашем случае речь идёт о мобильном приложении, клиентской приложение будет создано с помощью FireMonkey.
- Создаем новое Multi-Device приложение (настройки не критичны);
Я создаю клиентское приложение в группе, что дает возможность параллельно отлаживать и серверное приложение.
Создание отчётов в приложениях никогда не относилось к категории моих любимых занятий. Тем не менее, так или иначе, этим приходится заниматься. В своем рабочем проекте, о котором я уже не раз рассказывал, я перевёл практически все отчеты с Rave на FastReport. Последний инструмент сейчас является безусловным лидером “отчётостроения” и если есть необходимость поддерживать проект в управляемом состоянии, то подобное преобразование просто необходимо выполнить. Естественно, при этом хотелось максимально эффективно использовать возможности FastReport и оптимизировать во всех отношениях старые отчеты. Одной из типичных проблем отчётах всегда была проблема отображения Memo полей. Они могут содержать достаточно объемный текст, а могут, напротив, иметь пустые значения. Если такие данные отображать в столбце рядом с обычными данными, то визуально это будет выглядеть малопривлекательно. Читабельность отчета снижается и данные воспринимаются значительно хуже. Проблема довольно типичная, поэтому я решил сделать небольшой пост на эту тему.
На уровне логики всё решается достаточно просто. Значения Memo полей можно опционально выводить под строкой, содержащей основные данные. Примерно так:
Решение простое и логичное. При этом обратите внимание на то, что в случае отсутствия данных в Memo поле, не остается пустого места между строками. Читать далее
Прежде чем продолжить рассказ о таймере – две новости.
Во-первых, вышел первый апдейт XE7. По традиции он доступен зарегистрированным пользователям. Список исправленных багов вы можете найти здесь. Мне хотелось посмотреть как поведет себя приложение в обновленной среде. Собственно, никаких исправлений вносить не пришлось, хотя поле для экспериментов осталось.
Вторая новость. Действие специальных предложений Embarcadero продлено до конца года:
- возможен апгрейд с любой старой версии на новую версию XE7: Delphi, C++ Builder и RAD Studio.
- покупатели редакций Enterprise, Ultimate и Architect получают бесплатную лицензию Rapid SQL XE6.
Ну, а теперь непосредственно к теме поста. В принципе, все, что нам осталось – попытаться запустить уже созданное приложение под Android. Для это используем то, о чем я писал в предыдущих постах. А именно новый FireUI. Я отлаживал данное приложение на Nexus 7, соответственно добавил представление Android 7″ Tablet. Дизайн пришлось “подрихтовать” лишь самую малость.
В прошлой части мы практически полностью реализовали логику приложения. Сейчас я хочу рассказать о, возможно, самой интересной части разработки приложения. Построении пользовательского интерфейса. Если вы еще не в курсе, что такое FireUI, то, вероятно удивитесь, что большая часть работы по построению и отладке GUI Android приложения будет производится в Windows, без использования мобильного устройства. Субъективно, я в восторге. Это быстро, удобно и во всех отношениях легко.
Приступим…
Итак, у нас есть чистая (в смысле используемых стилей) форма, с четырьмя кнопками и меткой TLabel. Конечно у меня, изначально было искушение бросить на форму TStyleBook, а метку отобразить каким-нибудь диковинным шрифтом. Посмотрев на приложение Владимира Тимофеева я отказался от этой идеи. Подобную красоту придется рисовать руками. Или немного схитрить, используя возможности библиотеки классов FireMonkey. Я выбрал второй вариант. Немного погуглив по слову Scoreboard, я нашел на коммерческом стоке графических изображений симпатичный алфавит, дополненный цифрами.
Так выглядит выбранный мною клипарт. Вооружившись чувствительной мышкой и терпением я вырезал из него цифры. Читать далее
Наверное свой таймер не писал только ленивый. А в контексте поддержки разработки для мобильных платформ, задачу написания таймера на Delphi вообще можно считать культовой. Вот я и подумал, почему бы в качестве примера разработки FireMonkey приложения не разобрать именно таймер. Под Android, естественно. Конечно же, это будет именно мой взгляд на задачу, которая, хотя и не особо сложная, все же имеет свои нюансы. Возможно, у вас возникнут какие-то замечания, или предложения, было бы замечательно обсудить их в комментариях. Я отнюдь не эксперт в области написания мобильных приложений, поэтому любой ваше замечание будет для меня ценно.
Разрабатывать мы будем именно таймер, в англоязычном понимании этого термина. Т. е. на экране будет отображаться циферблат и четыре кнопки – “Пуск”, “Пауза”, “Стоп” и “Отмена”. Отсчет будет производиться в прямом направлении (т. е. время будет увеличиваться). Вариант, при котором задается время и идет обратный отсчет в англоязычной терминологии называется Stop Watch, возможно я попробую реализовать его позже. То, приложение, которым займемся мы, по функциональности ближе к секундомеру.
Delphi XE7, позволяет значительно упростить процесс разработки, за счет того, что теперь мы можем создать и отладить реальное приложение для Win32, а затем просто добавить представления форм для необходимых мобильных устройств и, немного подкорректировав их, получить рабочее мобильное приложение. Звучит слишком красиво, что бы быть правдой? Возможно. Но проверить данное утверждение я и хочу, реализовав поставленную задачу. Читать далее
Технология App Tethering появилась еще в прошлой версии Delphi. И рассказать о ней я собирался еще после выхода XE6, однако, как это часто бывает “не дошли руки”. Этот механизм предназначен для взаимодействия приложений на разных устройствах. Примечательно, что он поддерживается как в VCL, так и в FireMonkey. В XE7 в App Tethering были добавлены новые возможности, что для меня стало очередным поводом разобраться и сделать пару примеров. Обдумывать идеи для тестового приложения долго не пришлось. Так получилось, что сейчас у меня на рабочем столе стоит два ПК и, время от времени появляется планшет и смартфон (оба под управлением Android). Работаю я с этими устройствами практически одинаково интенсивно и, соответственно, возникает необходимость оперативно обмениваться не только файлами, но и текстовыми фрагментами, ссылками и т. д.. Конечно, существуют сотни программ, решающих это задачу, но коль скоро у нас имеется инструмент, то грех не написать собственное приложение, реализующее данный функционал.
В идеале мне бы хотелось бы создать некоторое приложение, работающее как под Windows, так и под Android, которое позволяло бы:
- осуществлять обмен файлами между устройствами;
- осуществлять обмен содержимым буфера обмена.
При этом хотелось бы исключить сложную процедуру настройки соединения для различных устройств.
Поскольку изобилия русскоязычных материалов по App Tethering пока не наблюдается, я буду перемежать рассказ о процессе разработки приложения выдержками из документации.
Разработку начнем с реализации простейшего функцонала – обмена файлами между ПК. Для того, что бы упростить понимание принципов работы App Tethering создадим два приложения – приложение, передающее файлы (“передатчик”) и приложение, принимающее файлы (“приемник”). Во избежание путанницы я сознательно не стану использовать термины “клиент”, “сервер” и т.д. “Передатчик” реализуем на VCL, а “приемник” – на FireMonkey. Опять же все это из соображений наглядности. В идеале это должно быть одно мультиплатформенное приложение, совмещающее в себе функции приема и передачи файлов. К этому вопросу, надеюсь, мы вернемся чуть позже.
Итак создадим два приложения,VCL и FireMonkey(Multi-device application), назовем их PrjSender и PrjReceiver и объеденим их в одной группе проектов. Механизм App Tethering в Delphi реализуют всего два компонента TTetheringManager и TTetheringAppProfile (менеджер и профиль). Размещаем их на главной (и пока единственной) форме обоих приложений. В обоих случаях для TetheringAppProfile устанавливаем значение свойства TetheringManager – TetheringManager1, таким образом связываем менеджер с профилем.
Для чего нужны эти компоненты? Документация гласит следующее:
Менеджер может обнаруживать и попарно связываться с другими менеджерами, представляющие удаленные профили, которые могут “расшаривать” данные для зарегистрированных профилей вашего менеджера. Менеджер может быть связан с одним или несколькими профилями. Основные функции менеджера:
- Менеджер использует адаптеры, для поиска других менеджеров. Например, менеджер использует сетевой адаптер для подключения к другим менеджерам по сети;
- Менеджер устанавливает связь с удаленными менеджерами (сопряжение);
- Два парных менеджера позволяют своим профилям узнать о профилях других менеджеров, таким образом профили могут обмениваться данными друг с другом;
- Менеджер предоставляет информацию от своих адаптеров протоколам своих профилей таким образом, что бы эти профили могли обмениваться данными друг с другом, используя собственные протоколы.
Все это звучит немного сумбурно, но попробуем разобраться во всем на практике. Прежде всего, нам нужно обеспечить соединение между двумя приложениями. В верхней части главной формы приложения-приемника разместим компонент ListBox (lbSenders) и кнопку (btnGetAvailableSenders). По нажатию на кнопку отобразим список доступных удаленных менеджеров. Для этого определим обработчик события OnEndManagersDiscovery следующим образом:
procedure TfRecMain.TetheringManager1EndManagersDiscovery(const Sender: TObject; const ARemoteManagers: TTetheringManagerInfoList); var I: Integer; begin lbSenders.Clear; for I := 0 to aRemoteManagers.Count - 1 do begin lbSenders.Items.Add(ARemoteManagers[i].ManagerText); end; end;
А по нажатию на кнопку вызовем метод DiscoverManagers компонента TetheringManager.
procedure TfRecMain.btnGetavailableSendersClick(Sender: TObject); var i: integer; begin for I := TetheringManager1.PairedManagers.Count - 1 downto 0 do TetheringManager1.UnPairManager(TetheringManager1.PairedManagers[I]); lbSenders.Clear; TetheringManager1.DiscoverManagers; end;
Предварительно здесь мы очищаем список и разрываем все связи (метод UnPairManager). Читать далее
В одном из постов, если не ошибаюсь в блоге у Всеволода Леонова, довелось мне прочесть про забавный кейс. В программе было предусмотренно ограничение длины поля ввода пароля. При том, ограничивалось оно тихо, просто “съедая” лишние символы. Естественно, что после этого с паролями возникла неразбериха. В принципе, вопрос ограничения длины поля ввода сам по себе является философским, а возможно даже теологическим, но просто взять и “откусить хвост” вводимому тексту, на мой взгляд – не совсем правильно. Однако, в большинстве случаев, если задать свойство MaxLength для контрола, отвечающего за ввод текста, именно так и произойдет.
Компонент TcxTextEdit и другие компоненты из набора DevExpress могут красиво решать данную проблему благодаря событию OnValidate.
Код, который я использую выглядит примерно так:
procedure TfrmEditBenefit.txtBenefitNamePropertiesValidate(Sender: TObject; var DisplayValue: Variant; var ErrorText: TCaption; var Error: Boolean); begin if Length(txtBenefitName.Text)>125 then begin ErrorText:= 'Benefit name value is too long.'; Error:= True; end; end;
Для наглядности можно показать значок ошибки рядом с полем ввода.
txtBenefitName.Properties.ValidationOptions:= [evoRaiseException, evoShowErrorIcon];
Выглядеть это будет примерно так:
Ошибка генерируется если текст длиннее допустимого значения при попытке передать фокус другому контролу. При этом пользователь зразу видит что и где не так. По-моему очень удобно и наглядно.
Другие статьи серии:
DevExpress. TIPS & TRICKS #0
DevExpress. TIPS & TRICKS #1
DevExpress. TIPS & TRICKS #2
DevExpress. TIPS & TRICKS #3
DevExpress. TIPS & TRICKS #4
DevExpress. TIPS & TRICKS #5
DevExpress. TIPS & TRICKS #5.5
DevExpress. TIPS & TRICKS #6