суббота, 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