Monthly Archives: June 2008

Как сделать чтоб WPF Popup был не поверх всех окон (или Win32 API живет до сих пор)

Уже несколько раз подряд за последние дни упираюсь в “пределы” WPF. Причём задачи были отнюдь не экзотические.

Класс Popup генерирует окно, которое располагается поверх всех окон в системе. Если вы хотите изменить это вам поможет только Win32 API:

[DllImport(“user32”, EntryPoint = “SetWindowPos”)]

private static extern int SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int cx, int cy, UInt32 wFlags);

 

const UInt32 SWP_NOSIZE = 0x0001;

const UInt32 SWP_NOMOVE = 0x0002;

const UInt32 SWP_NOACTIVATE = 0x0010;

static readonly IntPtr HWND_BOTTOM = new IntPtr(1);

static readonly IntPtr HWND_TOP = new IntPtr(0);

 

public static void Popup_SetZOrderTop(Popup pop)

{

IntPtr hwnd = ((HwndSource)PresentationSource.FromVisual(pop.Child)).Handle;

SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);

}

 

С помощью этой функции также можно регулировать какое из окон находиться наверху в данный момент.

Получение текущих координат мыши в WPF возможно только с указанием контрола, относительно которого вам нужны эти координаты. А что делать если этот самый контрол нужно двигать?

public struct POINT

{

public int x;

public int y;

};

 

[DllImport(“user32.dll”)]

static extern bool GetCursorPos(out POINT point);

Еще пара полезных функций (поиск окна и получение его положения):

[DllImport(“user32.dll”, SetLastError = true)]

static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

 

[DllImport(“user32.dll”)]

static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

 

public struct RECT

{

public int left;

public int top;

public int right;

public int bottom;

};

Правильная локализация

Водится за мной такой недостаток, люблю изобрести велосипед… Временами это плохо отражается на результате, но ничего не могу с собой поделать. С другой стороны, иногда, велосипед дорабатывается до мощного мотоцикла…

Недавно делал локализацию (поддержку многих языков в приложении) и понял что делал ее неверно, даже несмотря на то что с самого начала разработки софта продумал как она будет сделана. Казалось бы, что может быть проще, но только теперь для меня стало понятно как она должна выглядеть. Можно уложить это в небольшой список рекомендаций:

Структура:

  1. Локализованные (переведённые) строки должны храниться во внешнем файле открытого формата (txt или xml например)
  2. Новый язык нужно мочь подключать без перекомпиляции кода
  3. Приложение должно уметь на любом этапе исполнения переключать язык, перечитать строки из файла и “перерисовать” весь интерфейс
  4. Любая функция по выводу текста должна вызывать функцию вида GetLocalText(string Token), где токен – идентификатор выводимой строчки
  5. Необязательно: Хорошо бы разбить строки по группам и написать маленькую утилитку для их быстрого редактирования и перевода.

Для чего все это нужно, если есть стандартные методы локализации? Не вижу смысла в файлах ресурсов, в формате непонятном простым смертным пользователям, которые могут захотеть перевести ваш продукт на свой язык. Любой пользователь должен уметь перевести интерфейс и видеть изменения, которые он делает, сразу в интерфейсе, без перекомпиляции и перезапусков. Конечно лучше воспользоваться услугами профессиональных переводчиков, но это не всегда реально.

Актуальны еще рекомендации по языку ресурсных строчек, которые можно найти в MSDN.

Язык:

  1. Не разбивайте фразы на куски, переводчику будет трудно понять как именно эти куски переводить
  2. Аналогично нельзя использовать отдельно многозначные термины или слова, которые могут быть глаголом и существительным одновременно
  3. Учтите, что при переводе размер надписей может увеличиться процентов на 50-75%. Это самая существенная проблема в компактных интерфейсах.

Эти проблемы как раз и призвана решить возможность оперативного просмотра результатов своей работы переводчиком.

Сортировка директорий на C#

Оказывается метод, который возвращает список директорий (GetDirectories) иногда может их не сортировать. Вроде как это происходит на винтах под FATом. Поэтому если Вы где-то визуализируете список директорий, то рекомендую Вам вставить туда алгоритм ручной сортировки, ибо сделать это легко с помощью Array.Sort:

DirectoryInfo[] dirs = dir.GetDirectories();

        

//Сортировочка:

Array.Sort(dirs, 0, dirs.Length, new DirectorySort());

Сам принцип сортировки нужно оформить ввиде класса реализующего интерфейс IComparer:

  class DirectorySort : System.Collections.IComparer

  {

    public int Compare(object x, object y)

    {

      DirectoryInfo d1 = x as DirectoryInfo;

      DirectoryInfo d2 = y as DirectoryInfo;

      return String.Compare(d1.Name, d2.Name, true, CultureInfo.CurrentCulture);

    }

  }

 

Сортировка тут не различает заглавные-прописные буквы и учитывает CurrentCulture.