понедельник, 12 ноября 2012 г.

Project codekeeper - борьба с "красявостями"

Когда только начал разработку CodeKeeper'а, хотел сделать его в mac'овском стиле, однако понял, что в результате погони за "красявостими" начинает страдать функционал, а это считаю недопустимым. В итоге решил воспользоваться стандартными контролами, которые, впрочем, обеспечивают бОльшую функциональность, нежели, опять же, красивые контролы. В частности на замену sidebar'у собственного производства пришел стандартный TreeViewControl, который, несмотря на свою стандартность, может обеспечивать неограниченную степень вложенности, что для категорий актуально. К примеру, разработчик занимается созданием сайта, у него есть набор сниппетов как в PHP, так и в JavaScript, так и в HTML. В дальнейшем свои наработки он планирует использовать где-либо еще. Соответственно, ему потребуется примерно следующая структура:

Web-Shop
+ HTML
|+Template
||- Meta
||- Header
||- Body
||- Navigation
||- Footer
|+ Chunks
||- Breadcrumbs
||- SearchForm
|+ JavaScript
||+ jQuery
|||- Slider
|||- AjaxSearch
+ PHP
|- Front-end production editor
|- Price uploader
|- XML Parser


То есть, число вложенных категорий не должно быть фиксированным. Это первый момент. Второй момент - это диалоговые окна. Дело в том, что функция |form|.ShowDialog() подразумевает то, что программа запрашивает у пользователя некие данные, и до момента их ввода не позволяет пользователю работать с другими частями программы, кроме того функция |form|.ShowDialog() способна возвращать в функцию, ее вызвавшую данные, которые пользователь ввел в |form|. В случае же с использованием UserControl я пока не знаю, как создать подобный функционал, но больший вопрос в целесообразности "придумывания велосипеда". Все уже есть. Так что, возвращаемся к истокам, старому доброму "венику"...

суббота, 29 сентября 2012 г.

Project CodeKeeper - диалоги о диалогах (часть 1)

Доброго времени суток! Проект CodeKeeper продолжается, и сейчас я начинаю создавать диалоговые окна. На самом деле я большой поклонник Mac'ов, и мне очень нравится способ вызова модальных диалоговых окон в среде Mac OS X (они плавно "выезжают" из-под заголовка окна, и "заезжают" обратно после выбранного пользователем действия, будь то Оk, Cancel, или что-либо еще). Итак, определимся с задачами:

1. Поскольку "окна" напрямую взаимодействуют с родительской формой, то я решил эти окна сделать не окнами а UserControl'ами (их проще разместить на родительской форме в соответствии с ее координатами).

2. Должно быть две публичных процедуры: "выезда" и "заезда" контрола, причем они должны быть адаптированы к любому размеру контрола вплоть до 640х480 (минимальный размер окна программы)

3. Должна быть процедура центровки, которая динамически корректировала бы положение "окна" во время изменения размеров родительской формы.

4. Код функции самого диалогового окна будет содержаться внутри самого диалогового окна

Хотел было сделать затемнение типа Apple'овских фото-галерей, но полу-прозрачность в среде winforms невозможна, а переносить проект на WPF-платформу не получится во-первых потому, что компонент для отображения текста с подсветкой синтаксиса у меня есть только для WinForms, а во-вторых слишком незначительная деталь, чтобы задумываться о переносе проекта на другую платформу. Итак, продолжим. На данный момент программа выглядит так:


Начинаем "войну" с диалоговыми окнами :).

пятница, 28 сентября 2012 г.

Project CodeKeeper - тонкости перевода

Столкнулся с неожиданной проблемой: дело в том, что приведенная ранее процедура и структура текстового файла позволяли менять лишь одно свойства элемента формы - текст. Но как быть, к примеру, с всплывающими подсказками? Придется изменить структуру текстового файла и процедуру локализации, а именно:

Текстовой файл:

<имя контрола><Tab>Параметр><Tab><Значение>, где
<имя контрола> - имя элемента управления формы
<Параметр> - то, что нужно изменить, может быть:

  • Text (Текст)
  • ToolTip (Всплывающая подсказка)
  • NullValuePrompt (Приглашение пустого текстового поля (фишка DevExpress))
<Значение> - Локализованный параметр
<Tab> - символ табуляции

Текст процедуры:
      Public Sub translate(Language As String)
        Dim langFile As String = Application.StartupPath + "\lang\" + Language + ".txt"

        If System.IO.File.Exists(langFile) Then
            'Формирование лингвистического массива
            Dim lines() As String = System.IO.File.ReadAllLines(langFile)
            Dim langArray(lines.Length - 1)() As String
            For f As Integer = 0 To lines.Length - 1
                Dim tempArray() As String = lines(f).Split(vbTab)
                langArray(f) = {tempArray(0), tempArray(1), tempArray(2)}
            Next

            'Поиск контролов формы
            For f As Integer = 0 To langArray.Length - 1
                'форма
                If langArray(f)(0) = "formName" Then
                    Me.Text = langArray(f)(2)
                Else
                    Dim c() As Control = Me.Controls.Find(langArray(f)(0), True)
                    If c.Count > 0 Then
                        Select Case c(0).GetType
                            Case GetType(TextEdit)
                                'Текстовое поле
                                Dim tc As TextEdit = c(0)
                                Select Case langArray(f)(1)
                                    Case "Text" : tc.Text = langArray(f)(2)
                                    Case "NullValuePrompt" : tc.Properties.NullValuePrompt = langArray(f)(2)
                                    Case "ToolTip" : tc.ToolTip = langArray(f)(2)
                                End Select
                                'Кнопка
                            Case GetType(SimpleButton)
                                Dim tc As SimpleButton = c(0)
                                Select Case langArray(f)(1)
                                    Case "Text" : tc.Text = langArray(f)(2)
                                    Case "ToolTip" : tc.ToolTip = langArray(f)(2)
                                End Select
                                'Кнопка со стрелкой
                            Case GetType(DropDownButton)
                                Dim tc As DropDownButton = c(0)
                                Select Case langArray(f)(1)
                                    Case "Text" : tc.Text = langArray(f)(2)
                                    Case "ToolTip" : tc.ToolTip = langArray(f)(2)
                                End Select
                                'Раскрывающийся список
                            Case GetType(ComboBoxEdit)
                                Dim tc As ComboBoxEdit = c(0)
                                Select Case langArray(f)(1)
                                    Case "Text" : tc.Text = langArray(f)(2)
                                    Case "ToolTip" : tc.ToolTip = langArray(f)(2)
                                End Select
                            Case Else
                                'Другое
                                c(0).Text = langArray(f)(2)
                        End Select
                    End If
                End If
            Next
        Else
            MsgBox("Файл языка не найден")
        End If
    End Sub

Причем я слегка изменил стартер:
    Private Sub starter() Handles MyBase.Load
        Dim localizations() As String = System.IO.Directory.GetFiles(Application.StartupPath + "\lang\")
        Me.selLanguage.Properties.Items.Clear()
        For f As Integer = 0 To localizations.Length - 1
            Dim fileInfo As New System.IO.FileInfo(localizations(f))
            Me.selLanguage.Properties.Items.Add(fileInfo.Name.Substring(0, fileInfo.Name.Length - 4))
        Next
        If My.Settings.lang <> "" Then
            translate(My.Settings.lang)
            Me.selLanguage.Text = My.Settings.lang
        Else
            translate("English")
        End If
    End Sub

Таким образом, список локализаций теперь не фиксированный, а подгружается динамически в зависимости от содержимого папки lang в папке программы.

Project CodeKeeper - начало



Всем привет! Выдалось свободное время для творчества (:)), решил создать для себя утилу, с помощью которой я смогу: во-первых, хранить сниппеты различных языков программирования и разметки, а во-вторых, на ней отрабатывать эти сниппеты. Итак, проект CodeKeeper запущен. Прежде всего хотел сделать приложение локализуемым (многоязычным), причем расширяемым, то есть, чтобы пользователь при наличии знания языка  смог самостоятельно создать локализацию.

Попытка номер раз:
Задаем свойство isLocalizable=true для формы, параметр Language переставляем на English (изменений не видать), перебиваем заголовки кнопок, групп и прочих элементов управления, все вроде бы ничего, затем возвращаем Language на Default. Вернулись русские метки. Переключили на English - английские метки. Все зашибись (вроде бы). Теперь задача сделать runtime локализацию (то есть переключение между языками программы во время ее работы). Добавляем на форму ComboBoxEdit (используется набор DevExpress XtraEditors, но стандартный набор тоже подойдет). Прописываем код:


Private Sub languageSelector() handles ComboBoxEdit1.SelectedIndexChanged
Select Case ComboBoxEdit.SelectedIndex
Case 0 'Выбран английский язык
Threading.Thread.CurrentThread.CurrentCulture=new Globalization.CultureInfo("en-US")
Threading.Thread.CurrentUIThread.CurrentCulture=new Globalization.CultureInfo("en-US")

Case1 'Выбран русский язык
Threading.Thread.CurrentThread.CurrentCulture=new Globalization.CultureInfo("ru-RU")
Threading.Thread.CurrentUIThread.CurrentCulture=new Globalization.CultureInfo("ru-RU")
End Select
End Sub


Запускаем проект. И сколько бы не выбирали язык - результат - ноль...

Попытка номер два - плюнул на встроенные возможности Visual Studio по локализации, и сделал финт ушами. Для начала создал небольшой текстовой файл такого типа:
<имя контрола>=<локализованное название>, затем:

Private Sub languageSelector() handles ComboBoxEdit1.SelectedIndexChanged
Translate(ComboBoxEdit1.Text)
 'Сохраняем установки языка (для начала - Проект->Свойства проекта->Параметры, там создаем строковой параметр Lang)
My.Settings.Lang=ComboBoxEdit1.Text
 My.Settings.Save
End Sub

'Делаем подпрограмму доступной из других классов инструкцией Public
Public Sub Translate(Language as String)

Dim langFile as String=Application.StartupPath+"\lang\"+Language+".txt"
If System.IO.File.Exists(langFile)
Dim Lines() as String=System.IO.File.ReadAllLines(langFile)
'Формирование локализационного массива
Dim Lang as new HashTable
For f as Integer=0 to Lines.length-1
Dim pair() as string=Lines(f).Split("=")
Lang(pair(0))=pair(1)
Next
'Поиск контрола по имени и присваивание ему значения текста
dim keys as Icollection=Lang.keys
for f as Integer=0 to keys.Count-1

'Параметр True подтверждает поиск по всей иерархии
Dim c() as Control=Me.Controls.Find(keys(0),True)
c(0).Text=Lang(keys(f))
c(0).Update()
Next
Else
MessageBox.Show("Файл языка не найден!","Ошибка")
End If

End Sub

И вуаля! Заработало!

Теперь при вызове дочерних форм делаем следующее:
Private Sub starter() Handles MyBase.Load
 Fotm1.Translate(My.Settings.Lang)
End Sub






среда, 20 июня 2012 г.

Ajax и с чем его едят




Не так давно начал тестить новое для себя понятие - Ajax. Javascript, совмещенный с PHP дает очень интересные возможности программирования веб-приложений, но до недавнего времени работа с ним для меня была как дайвинг в болоте - вроде бы работает, но непонятно, как, порой возникают ошибки, лечатся, но опять же, не пойми как. И вот, более-менее все стало по своим местам. Итак, обо всем по порядку: Архитектура Клиент-Сервер-Клиент: подразумевает следующее: пользователь(клиент) отправляет запрос на сервер, там его запрос обрабатывается, и возвращается результат обработки введенных данных. Обычно для реализации этой архитектуры достаточно самого что ни на есть обыкновенного PHP. Это сценарий, выполняемый на стороне сервера, куда отправляются данные с локального(клиентского) компьютера. Все бы хорошо, но работа скрипта подразумевает перезагрузку страницы, что влечет за собой обнуление сохраненных Java-переменных, нарушение восприятия, ну и вообще просто неприятно. И тут как раз и приходит на выручку Ajax. Ajax позволяет запускать PHP-скрипт не перегружая страницу, и возвращать результат обработки непосредственно в Javascript. Итак:


<script>
xHR=new XMLHttpRequest();
</script>


 В переменную xHR мы записываем экземпляр класса XMLHttpRequest, который и обеспечивает обмен данными между Javascript и PHP, и все бы хорошо, если бы не треклятый ослик, который и тут нам пихает палки в колеса. Дело в том, что Internet Explorer не умеет работать с объектом XMLHttpRequest, для этого у него есть специализированная "примочка" - ActiveX. Итак, чтобы Ajax заработал и на осле, модифицируем наш скрипт:

<script>
if(window.XMLHttpRequest){
xHR=new XMLHttpRequest();
}else{
if(window.activeXObject){
xHR=new activeXObject("Microsoft.XMLHTTP");
}else{
alert("Ваш браузер не поддерживает Ajax!");
}
}
</script>

Инструкция "if(window...)" означает - "если сможешь, сделай так...". Итак, начало положено, что же дальше? А дальше нам предстоит обеспечить передачу данных PHP-скрипту, для чего мы сделаем следующее:

<script>
if(window.XMLHttpRequest){
xHR=new XMLHttpRequest();
}else{
if(window.activeXObject){
xHR=new activeXObject("Microsoft.XMLHTTP");
}else{
alert("Ваш браузер не поддерживает Ajax!");
}
}

xHR.open("POST","myScript.php",false);
xHR.send("myVar=myVarValue");
</script>

В строке /xHR.open("POST","myScript.php",false);/ мы инициировали передачу данных серверному сценарию myScript.php, передача будет осуществляться методом POST, false указывает на то, что асинхронный режим использоваться не будет. Далее, строкой /xHR.send("myVar=myVarValue");/ мы отправили данные указанному выше скрипту, а именно в переменную myVar мы записали значение myVarValue. Теперь, чтобы получить эти данные в PHP-скрипте нам достаточно воспользоваться глобальным массивом $_POST:

<?
echo "Моя переменная равна:".$_POST['myVar'];
?>

Если бы PHP-скрипт запускался напрямую, он бы выдал надпись "Моя переменная равна: myVarValue", однако, поскольку скрипт был инициирован из Javascript, результат его работы возвращается туда же, в переменную responseText или responseXML (в случае, если результатом является XML-файл). В нашем случае это responseText. Итак, финал:

<script>
if(window.XMLHttpRequest){
xHR=new XMLHttpRequest();
}else{
if(window.activeXObject){
xHR=new activeXObject("Microsoft.XMLHTTP");
}else{
alert("Ваш браузер не поддерживает Ajax!");
}
}

xHR.open("POST","myScript.php",false);
xHR.send("myVar=myVarValue");
alert(xHR.responseText);
</script>

 И все бы хорошо, пока я не наткнулся на такую ошибку: origin /хост/ is not allowed by access-control-allow-origin. Связано это с тем, что для сервера http://domain.com и http://www.domain.com - это разные сервера, как следствие может возникнуть ситуация, воспринимаемая сервером, как попытка запуска Javascript'ом одного домена PHP-скрипта другого. Лечится все достаточно просто: лучше использовать абсолютные пути при указании расположения скриптов, или же использовать HTML-директиву <base href="...">.


понедельник, 2 апреля 2012 г.

С# - первое знакомство

С недавних пор начал искать более шустрые альтернативы платформе VB.NET. (Согласен, с одной стороны при прямых руках все будет работать быстро на любой платформе, но в данный момент мне нужно было найти быстрое решение для поставленных мною задач). Итак, начальный выбор мой пал на C#, как на наиболее благоприятную платформу с точки зрения перехода с VB.NET. Первоначальное впечатление - достаточно благоприятное, но обо всем по порядку:

Различия в синтаксисе порадовали тем, что они относительно небольшие:

Определение переменных:
VB.NET:
Dim A as String="Hello, World!"
С#:
string a="Hello, World!";

Операторы сравнения:
VB.NET:
If A<>"" Then
    MessageBox.Show("Hello, World!")
EndIf
C#:
if(A!=""){
    MessageBox.Show("Hello, World!");
}
И все бы ничего, пока не начались казусы, которые в VB.NET показались бы гм... непонятными с точки зрения причины возникновения ошибки:

VB.NET:
Dim val as String="Ячейка таблицы: "+Me.DataGridView1.SelectedRows(0).Cells("Customer").Value.ToString()
C#:
String val="Ячейка таблицы: "+this.DataGridView1.SelectedRows[0].Cells['Customer'].Value.ToString();

Вроде бы все одинаково, однако в C# возникает ошибка: "Невозможно преобразовать группу методов ToString" в тип, не являющийся делегатом string. Предполагается ли вызвать объект?"

Что самое потрясающее - это вопрос в... всплывающей подсказке!
В итоге, погуглив немного, решение нашлось:
С#:
String val="Ячейка таблицы: "+Convert.ToString(this.DataGridView1.SelectedRows[0].Cells['Customer'].Value);
Но, согласитесь, как минимум, странно. Но что меня добило, это то, что для очистки неиспользуемых ссылок (которая в VB.NET запускается одной кнопкой) в C# предлагается внешняя программа за $350! В общем, благоприятные впечатления от C# постепенно начали рассеиваться. И далее по нарастающей:

Для создания эвента в VB.NET в коде формы создается конструкция типа:

VB.NET:
Private Sub starter() Handles MyBase.Load
    ...
End Sub

Для C# нужно не только написать обработчик эвента в коде формы:

С#:
void  Form1_Load (Object sender, EventArgs e){
    ...
}

 но и прописать его в коде From Designer'а:

C#:
this.Load += new System.EventHandler(this.Form1_Load);

А как быть к примеру для назначения эвентов для двойного клика внутри строки DataGrid?. И если таких эвентов не один и не два? В конечном итоге: C# - это безусловно решение для профессионалов, однако есть ряд моментов, которые вызывают в лучшем случае изумленную улыбку непонимания. У C# есть безусловный плюс в том, что его можно применять не только в среде WinForms, но и в веб- и WPF-приложениях, но те же возможности есть и у VB.NET. Однако проблем при написании - много больше. Вывод прост - пока остаюсь на VB.NET.