Как работать с регионами

x77
http://club.shelek.com/

  • Общее описание
  • Функции создания/удаления регионов
  • Функции отображения регионов
  • Прочие функции
  • Функции работы с окнами и регионами
  • Создание региона
  • Комбинирование регионов
  • Рисование регионов
  • Прочие функции
  • Сохранение и загрузка региона

Общее описание

Windows API предоставляет набор функций, позволяющих описать произвольную (при желании — достаточно сложную) геометрическую фигуру, которая потом может использоваться при работе с окнами, или, в терминологии Delphi, элементами управления. Использование может заключаться, например, в отрисовке на холсте, в задании специфического региона обновления окна, и т.д. Помимо таких вот относительно невинных возможностей, технология регионов позволяяет также глумиться над благородными очертаниями любого потомка TWinControl (иными словами, любым контролом, имеющим Handle, aka TForm, TButton, и т.д.). Особенно широко регионы используются в формах, при их отрисовке и обновлении. Итак, для начала, давайте разберёмся, что такое этот самый регион.

Win32 SDK регион определяет следующим образом:

В Microsoft Windows регионом называется прямоугольник, полигон или эллипс (либо комбинация двух или более этих фигур), которые могут быть заполненны, нарисованы, инвертированы, обрамлены и могут использоваться для определения местонахождения курсора (имеется в виду, что есть стандартная функция, определяющая входит ли точка (X,Y) в наш регион).

В SDK упомянуты три основных типа регионов: прямоугольные, эллиптические, и полигональные. О чётвёртом типе, прямоугольном с закруглёнными краями, стыдливо умалчивается, ну да и фиг с ним. Видимо это связано с тем, что его можно получить из первых двух. Дело в том, что регионы могут комбинироваться между собой с применением логических операций OR, XOR и т.д. Но к этому мы вернёмся позже.

С точки зрения Windows, регион является обыкновенным объектом GDI, задаваемым дескриптором HRGN. В Delphi также доступен этот тип, и объявление его выглядит следующим образом:

HRGN = type LongWord;

Как видим, ничего сверхъестественного. По сути это указатель на некую структуру в памяти. Структура эта описана следующим образом:

TRgnData = _RGNDATA; _RGNDATA = record rdh: TRgnDataHeader; Buffer: array of CHAR; Reserved: array of CHAR; end;

Что примечательно, в Win32 SDK _RGNDATA объявлена немного иначе. Наиболее любопытный член этой записи, безусловно, TRgnDataHeader. В модуле Windows.pas фигурирует и он:

TRgnDataHeader = _RGNDATAHEADER; _RGNDATAHEADER = packed record dwSize: DWORD; iType: DWORD; nCount: DWORD; nRgnSize: DWORD; rcBound: TRect; end;

Вот его стоит рассмотреть подробнее.

  • dwSize: Указывает размер заголовка (т.е. _RGNDATAHEADER) в байтах;
  • iType: Тип региона, всегда равен RDH_RECTANGLES = 1;
  • nCount: Кол-во прямоугольников, из которых составлен регион;
  • nRgnSize: Размер буфера, необходимого для получения структуры TRect, указывающей координаты прямоугольников, составляющих регион. Если размер неизвестен, поле может быть равно нулю;
  • rcBound: Координаты прямоугольника, в который вписан регион.

Вот именно эта структура и задаёт регион, как таковой, и именно с ней работают практически все функции API. Пора познакомиться с ними поближе.

Условно все эти функции можно разбить на три группы: создание/удаление, отрисовка и прочие. Расмотрим их все по порядку. Для удобства я буду приводить не си-шные декларации из SDK, а паскалевские декларации, объявленные в модуле Windows, ибо именно с ними нам и предстоит работать.

Функции создания/удаления регионов

Функция Синтаксис Описание
CombineRgn function CombineRgn (p1, p2, p3: HRGN; p4: Integer): Integer; stdcall; Создаёт новый регион p1, комбинируя регионы p2 и p3 на основе режима p4
CreateEllipticRgn function CreateEllipticRgn (p1, p2, p3, p4: Integer): HRGN; stdcall; Создаёт эллиптический регион по четырём координатам
CreateEllipticRgnIndirect function CreateEllipticRgnIndirect (const p1:TRect): HRGN; stdcall; Создаёт эллиптический регион, впсанный в заданный прямоугольник p1
CreatePolygonRgn function CreatePolygonRgn (const Points; Count, FillMode: Integer): HRGN; stdcall; Создаёт полигональный регион по массиву точек Points с числом вершин Count с режимом заливки FillMode
CreatePolyPolygonRgn function CreatePolyPolygonRgn (const pPtStructs; const pIntArray; p3, p4: Integer): HRGN; stdcall; Создаёт регион, состоящий из серии полигонов, задаваемых массивом точек pPtStructs, число вершин каждого из которых указывается в массиве pIntArray с общим числом вершин p3 и режимом заливки p4
CreateRectRgn function CreateRectRgn (p1, p2, p3, p4: Integer): HRGN; stdcall; Создаёт прямоугольный регион по четырём координатам
CreateRectRgnIndirect function CreateRectRgnIndirect (const p1: TRect): HRGN; stdcall; Создаёт прямоугольный регион по заданному прямоугольнику p1
CreateRoundRectRgn function CreateRoundRectRgn (p1, p2, p3, p4, p5, p6: Integer): HRGN; stdcall; Создаёт прямоугольный регион по координатам p1-p4 с эллиптически закруглёнными краями с высотой эллипса скругления p5 и шириной p6
ExtCreateRegion function ExtCreateRegion (XForm: PXForm; Count: DWORD; const RgnData: TRgnData): HRGN; stdcall; Создаёт регион по его данным RgnData размером Count с указанной трансформацией XForm
DeleteObject function DeleteObject (p1: HGDIOBJ): BOOL; stdcall; Удаляет регион и освобождает все, занятые под него ресурсы

Надо сказать, что последняя функция DeleteObject не фигурирует в разделе по работе с регионами в явном виде, т.к. предназначена она для более широкого использования. По большому счёту, она удаляет любой логический объект GDI (перо, кисть, шрифт, битмап) и освобождает все отведённые под этот объект ресурсы. Для полных даунов SDK ласково уточняет, что после вызова сей функции ссылка на удаляемый объект становится неопределённой.

Функция Синтаксис Описание
SetPolyFillMode function SetPolyFillMode (DC: HDC; PolyFillMode: Integer): Integer; stdcall; Устанавливает режим заливки регионов PolyFillMode для контекста dc
GetPolyFillMode function GetPolyFillMode (DC: HDC): Integer; stdcall; Возвращает текущий режим заливки регионов на конктексте dc
PaintRgn function PaintRgn (DC: HDC; RGN: HRGN): BOOL; stdcall; Рисует регион rgn на контексте dc текущей кистью контекста
FillRgn function FillRgn (DC: HDC; hrgn: HRGN; hbr: HBRUSH): BOOL; stdcall; Заливает регион hrgn на контексте dc с заданной кистью hbr
FrameRgn function FrameRgn (DC: HDC; hrgn: HRGN; hbr: HBRUSH; Width, Height: Integer): BOOL; stdcall; Рисует на контексте dc рамку вокруг региона hrgn кистью hbr соответствующей ширины и высоты Width и Height

Прочие функции

Функция Синтаксис Описание
EqualRgn function EqualRgn (p1, p2: HRGN): BOOL; stdcall; Проверяет, являются ли размеры и форма регионов p1 и p2 идентичными
InvertRgn function InvertRgn (DC: HDC; p2: HRGN): BOOL; stdcall; Инвертирует цвета региона p2 на контексте dc
OffsetRgn function OffsetRgn (RGN: HRGN; XOffset, YOffset: Integer): Integer; stdcall; Сдвигает регион rgn по осям X и Y на XOffset и YOffset соответственно
SetRectRgn function SetRectRgn (Rgn: HRgn; X1, Y1, X2, Y2: Integer): BOOL; stdcall; Конвертирует регион rgn в прямоугольный, заданный четырьмя координатами
GetRgnBox function GetRgnBox (RGN: HRGN; var p2: TRect): Integer; stdcall; Возвращает прямоугольник p2, в который вписан указанный регион rgn
RectInRegion function RectInRegion (RGN: HRGN; const Rect: TRect): BOOL; stdcall; Определяет, «влазит» ли пряпоугольник Rect в границы региона rgn
PtInRegion function PtInRegion (RGN: HRGN; X, Y: Integer): BOOL; stdcall; Определяет, входит ли точка (X,Y) в регион rgn
GetRegionData function GetRegionData (RGN: HRGN; p2: DWORD; p3: PRgnData): DWORD; stdcall; Заполняет данными региона rgn буфер PRgnData размером p2, при pRgnData = nil — возвращает размер региона в памяти.

Каким же образом это безобидное и абстрактное, в общем-то, понятие взаимодействует со вполне реальными элементами управления? Ведь все перечисленные функции служат, по большому счёту, для того, чтобы создать некую замысловатую фигуру и тем или другим способом отрисовать её в чьём-то контексте, DC, а о том, чтобы каким-то образом изменить форму той же кнопки и речи даже нет. Дело здесь в том, что мы пока мельком рассмотрели функции, касающиеся создания и манипуляций регионами. А ещё существуют функции работы с окнами, и вот там-то нам все наши созданные регионы и пригодятся. Поглядим, что это за функции.

Функции работы с окнами и регионами

Функция Синтаксис Описание
SetWindowRgn function SetWindowRgn(hWnd: HWND; hRgn: HRGN; bRedraw: BOOL): Integer; stdcall; Устанавливает для окна с дескриптором hWnd регион отображения hRgn, при взведённом флаге bRedraw — перерисовывая окно
GetWindowRgn function GetWindowRgn(hWnd: HWND; hRgn: HRGN): Integer; stdcall; Возвращает регион отображения hRgn для окна с дескриптором hWnd

Сразу оговорюсь, что на самом деле этих функций гораздо больше, равно как и область применения регионов гораздо ширше. Но нас они пока не волнуют. Итак, мы знаем, как создать и модифицировать регион, как скормить его окну (ибо параметр hWnd есть не что иное, как WinControl.Handle), и как его уничтожить. Настало время слегка поэксперементировать.

Создание региона

Запускаем среду, заводим новый проект, тщательно сохраняем куда попало. В обработчике OnCreate пишем первое, что, по идее, приходит в голову:

procedure TForm1.FormCreate(Sender: TObject); var r: hRGN; begin r := CreateEllipticRgn (0, 0, Width, Height); try SetWindowRgn (Handle, r, TRUE); finally DeleteObject (r); end; end;

Результатом запуска окажется симпатичный эллипс на экране. А теперь попробуйте дописать такой простенький обработчик на OnKeyPress:

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); var bmp: TBitmap; begin if Key = ‘p’ then begin bmp := GetFormImage; try bmp.SaveToFile (‘c:\temp.bmp’); finally bmp.Free; end; end; end;

В момент выполнения программы при нажатии «p» мы, по идее должны получить картинку нашей формы. Но как это ни покажется странным, мы получим картинку полной, т.е. неусечённой формы. Собственно, если взглянуть на реализацию GetFormImage — ничего странного не обнаружиться, создаётся виртуальный битмап, на который себя отрисовывает форма и все контролы на ней. Но об этой забавной фиче стоит помнить: для любой, самой извратной формы всегда можно получить её первоначальную картинку функцией GetFormBitmap, а любой загруглённый/продырявленный/звездообразный контрол всегда можно отрисовать на любой левый канвас в его первозданном виде функцией PaintTo.

Аналогичным образом создаются и другие типы регионов, единственное внимание стоит, пожалуй, уделить полигональным. Начнём с самого простого. В делкарации функции есть интересный параметр: FillMode. Согласно SDK он может принимать два значения — ALTERNATE и WINDING. Со вторым всё просто — вычисляются границы, и всё, что между — заливается текущей кистью. А вот первый при заливке анализирует, по какую сторону ребра (чётную или нечётную) он находится. Что это означает, мы увидим на следующем примере.

procedure TForm1.FormCreate(Sender: TObject); var ap: array of TPoint; r: hRGN; begin ap := Point (Width div 2, 0); ap := Point (Width div 3 * 2, Height); ap := Point (0, Height div 3); ap := Point (Width, Height div 3); ap := Point (Width div 3, Height); r := CreatePolygonRgn (ap, 5, WINDING); try SetWindowRgn (Handle, r, TRUE); finally DeleteObject (r); end; end;

Запустите программу, посмотрите на результат (она превратит форму в звезду), и поменяйте значение параметра FillMode на ALTERNATE. И запустите её снова. На тот случай, если по каким-то причинам вам влом напрягаться со всякими там модами, ниже приведёны примеры того, что вы могли увидеть:

FillMode = WINDING FillMode = ALTERNATE

Обратите внимание, что если вы кликаете мышкой на «дырку», сообщение получает окно, лежащее под вашей формой. Если форма выведена на пустом десктопе, вы вполне успешно можете из «дырки» вызвать контекстное меню экрана. Т.е. это дейтсвительно «дыра», а не просто прозрачная отрисовка. Теперь разберёмся, как работает функция CreatePolyPolygonRgn. Её предназначение в том, чтобы рисовать сразу несколько регионов, которые могу даже не пересекаться. А могу — и пересекаться, именно для этого и нужен параметр FillMode. Поглядим, как это выглядит, и что оно делает:

procedure TForm1.FormCreate(Sender: TObject); var ap: array of TPoint; av: array of integer; r: hRGN; begin ap := Point (0, 0); ap := Point (Width, 0); ap := Point (Width, Height div 2); ap := Point (0, Height div 2); ap := Point (Width div 2, Height div 2); ap := Point (0, Height); ap := Point (Width, Height); av := 4; av := 3; r := CreatePolyPolygonRgn (ap, av, 2, WINDING); try SetWindowRgn (Handle, r, TRUE); finally DeleteObject (r); end; end;

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

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

Комбинирование регионов

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

  • RGN_ADD — Пересечение двух регионов p2 и p3
  • RGN_COPY — Создаёт копию региона p2
  • RGN_DIFF — Вернёт часть p2, не являющуюся частью p3
  • RGN_OR — Объединение двух регионов p2 и p3
  • RGN_XOR — Объединяет регионы p2 и p3, исключая пересекающиеся области

Возвращаемые значения могут быть NULLREGION (пустой регион), SIMPLEREGION (один прямоугольник), COMPLEXREGION (всё остальное) и ERROR (нифига не создано). Посмотрим, как это выглядит на практике (чтобы не сильно мучитья, я просто дописывал предыдущий пример).

procedure TForm1.FormCreate(Sender: TObject); var ap: array of TPoint; av: array of integer; r, r2, r3, r4: hRGN; begin ap := Point (0, 0); ap := Point (Width, 0); ap := Point (Width, Height div 2); ap := Point (0, Height div 2); ap := Point (Width div 2, Height div 2); ap := Point (0, Height); ap := Point (Width, Height); av := 4; av := 3; r := CreatePolyPolygonRgn (ap, av, 2, WINDING); try r2 := CreateRoundRectRgn (Width div 4 — 20, Height div 6 — 20, Width div 4 + 20, Height div 6 + 20, 16, 16); r3 := CreateRoundRectRgn (Width div 4 * 3 — 20, Height div 6 — 20, Width div 4 * 3 + 20, Height div 6 + 20, 16, 16); r4 := CreateEllipticRgn (Width div 10, Height div 9 * 3, Width div 10 * 9, Height div 9 * 4); try CombineRgn (r, r, r2, RGN_XOR); CombineRgn (r, r, r3, RGN_XOR); CombineRgn (r, r, r4, RGN_XOR); finally DeleteObject (r2); DeleteObject (r3); DeleteObject (r4); end; SetWindowRgn (Handle, r, TRUE); finally DeleteObject (r); end; end;

Нерасмотренной из первой группы функций осталась только ExtCreateRgn. Я сейчас не буду на ней заостряться, скажу только, что на пару с функцией GetRegionData, она может пригодиться, например, для сохранения и загрузки регионов в файл / из файла.

Рисование регионов

Регионы нужны не только для того, чтобы резать дырки в формах. Иногда они могут оказаться довольно полезным инструментом именно в своём «родном» качестве, т.е. для отрисовки на экране достаточно сложных геометрических фигур. Например, для вывода карт, представляющих собой совокупность ломанных линий, построенных по массивам точек. Создать такую линию нам уже не составит труда, пора разобраться, как её показать юзеру.

Из функций отрисовки две первые нам уже смутно знакомы: они делают тоже, что делает параметр FillMode (ALTERNATE/WINDING) для функций CreatePolygonRgn и CreatePolyPolygonRgn. GetPolyFillMode получает заданный для указанного контекста режим заливки, а SetPolyFillMode устанавливает его. Просто на этот раз речь идёт не о создании региона, а всего лишь о его отрисовке. Установленное значение будет иметь смысл для всех функций, заливающих регион, т.е. PaintRgn и FillRgn, при этом сам регион останется таким, каким он и был создан, а вот раскрашен будет по разному, в том случае, если он состоит из нескольких пересекающихся регионов. Для простых регионов типа прямоугольника или элипса установка данного значения ничего не меняет.

Итак. Давайте срочно что-нить создадим и нарисуем. Можно, конечно, сделать это в одной функции, например в OnCreate, но тогда изображение будет весьма недолговечным — до первой перерисовки формы. Поэтому поступим иначе: объявим private property fRgn, в OnCreate его инициализируем, в OnPaint будем его отображать, а в OnDestroy — уничтожим. Код методов представлен ниже:

procedure TForm1.FormCreate(Sender: TObject); begin fRgn := CreateEllipticRgn (10, 10, 200, 200); end; procedure TForm1.FormDestroy(Sender: TObject); begin DeleteObject (fRgn); end; procedure TForm1.FormPaint(Sender: TObject); begin Canvas.Brush.Color := clBlack; PaintRgn (Canvas.Handle, fRgn); end;

Следует помнить, что Функции отрисовки регионов всегда работают с цветом, указанным в Canvas.Brush.Color. Даже рисуя бордюр (frame) использоваться будет не цвет Canvas.Pen, что, в общем-то, представляется более логичным, а цвет Canvas.Brush.

Ничего такой получился кружочек. Погребального вида. Давайте сделаем его более жизнерадостным, и заодно разберёмся, как работает FrameRgn:

procedure TForm1.FormPaint(Sender: TObject); var bmp: TBitmap; begin bmp := TBitmap.Create; try bmp.LoadFromFile (‘C:\WINDOWS\Голубые кружева 16.bmp’); Canvas.Brush.Bitmap := bmp; PaintRgn (Canvas.Handle, fRgn); Canvas.Brush.Color := clBlack; FrameRgn (Canvas.Handle, fRgn, Canvas.Brush.Handle, 2, 2); finally Canvas.Brush.Bitmap := nil; bmp.Free; end; end;

У меня получилась такая вот картинка:

Насколько я могу судить, функции FillRgn и PaintRgn отличаются друг от друга только тем, что первая позволяет указать дескриптор кисти, не связанной с текущим canvas’ом. Сомнительная фича с точки зрения дельфей, т.к. манипулировать с текущим цветом кисти канваса всяко легче, чем создавать отдельный экземпляр класса TBrush. Вот, собственно, и всё об отрисовке. Примечательно то, что для того, чтобы нарисовать регион нам не нужно знать, что он из себя представляет. Мы просто передаём дескриптор одной и той же процедуре, а она отобразит на экране круг, овал, треугольник, звезду Давида — всё, что угодно.

Функции, представленные в разделе прочее ничего особенно интересного из себя не представляют, и, в общем-то, интуитивно понятны. поэтому рассотрим лишь некоторые из них.

Сделаем это на примере. Давайте озадачимся возможностью таскать мышкой по всей форме круг, созданный в предыдущем примере. Что нам нужно. Во-первых, запоминать, где началось перетаскивание (fStartX, fStartY). Во-вторых, флаг (fDragging), указывающий, что юзер действительно перетскивает наш регион, а не просто гоняет с экрана мух. В третьих, надо узнать, ткнул ли он на регион, а не мимо (PtInRegion). В четвёртых, надо двигать регион по мере того, как он двигает мышь (OffsetRgn). Вот, пожалуй, и всё. На этот раз текст модуля приведу полностью. Единственное что там стоит упомянуть — это свойство DoubleBuffered. Оно выставлено в TRUE, т.к. иначе появляется мерцание. Итак.

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormPaint(Sender: TObject); procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); private { Private declarations } fDragging: boolean; fRgn: hRGN; fStartX, fStartY: integer; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin fRgn := CreateEllipticRgn (10, 10, 200, 200); fDragging := FALSE; DoubleBuffered := TRUE; end; procedure TForm1.FormDestroy(Sender: TObject); begin DeleteObject (fRgn); end; procedure TForm1.FormPaint(Sender: TObject); var bmp: TBitmap; begin bmp := TBitmap.Create; try bmp.LoadFromFile (‘C:\WINDOWS\Aieoaua e?o?aaa 16.bmp’); Canvas.Brush.Bitmap := bmp; PaintRgn (Canvas.Handle, fRgn); Canvas.Brush.Color := clBlack; FrameRgn (Canvas.Handle, fRgn, Canvas.Brush.Handle, 2, 2); finally Canvas.Brush.Bitmap := nil; bmp.Free; end; end; procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if (Button = mbLeft) and (PtInRegion (fRgn, X, Y)) then begin fDragging := TRUE; fStartX := X; fStartY := Y; end; end; procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if (ssLeft in Shift) and fDragging then begin OffsetRgn (fRgn, X — fStartX, Y — fStartY); fStartX := X; fStartY := Y; Refresh; end; end; procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin fDragging := FALSE; end; end.

Как видим, абсолютно ничего сложного в работе с регионами нет. А вот возможности они дают довольно интересные. Фигуру любой формы можно раскрасить как угодно (в т.ч. и битмапом), вывести на экран, определить, входит ли точка (X,Y) в эту фигуру, двигать её, и многое ещё другое. Для полноты картины нам осталось только научиться сохранять регионы на диск и считывать обратно.

Сохранение и загрузка региона

Как уже говорилось в самом начале, все данные о регионе храняться в структуре RGNDATA. Упоминалась также и функция, позволяющая эту структуру получить: GetRegionData. У этой функции есть приятная особенность: если в третий параметр передать nil, то она вернёт размер памяти, необходимый для сохранения региона.

procedure SaveRegion (FileName: string); var s: TStream; size: cardinal; data: pointer; begin s := TFileStream.Create (FileName, fmCreate); try size := GetRegionData (fRgn, SizeOf (RGNDATA), nil); data := GlobalAllocPtr(GPTR, size); try GetRegionData(fRgn, size, data); s.Write(data^, size); finally GlobalFreePtr(data); end; finally s.Free; end; end;

Аналогичным образом можно и прочитать записанный на диск регион:

function LoadRegion (FileName: string): hRGN; var data: PRgnData; s: TStream; begin s := TFileStream.Create (FileName, fmOpenRead); try data := GlobalAllocPtr(GPTR, s.size); try s.Read(data^, s.Size); Result := ExtCreateRegion(nil, s.Size, data^); finally GlobalFreePtr(data); end; finally s.Free; end; end;

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

Хочется надеятся, что кого-то сей опус подвигнет на создание чего-нить хорошего, или просто сэкономит несколько часов ползанья по Win32 SDK.

Ведущие западные юридические фирмы расширяются за счет создания филиалов в различных городах мира и за счет создания партнерских сетей. В России первые шаги в этом направлении предприняла фирма Vegas-Lex, имеющая три отделения – в Москве, Волгограде и Краснодаре – и создавшая собственную партнерскую сеть, цель которой – оказание высококачественных юридических услуг по всей стране.

Мы побеседовали на эту тему с руководителем Комитета Регионального развития Vegas-Lex.

Мысль о развитии региональной сети является весьма инновационной среди юридических фирм. Как возникла идея идти в регионы?

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

Хотя надо признать, что наши идеи не всеми воспринимаются однозначно. Конкуренция, утечка мозгов, потеря проектной самостоятельности – вот первое, что приходит на ум скептикам. Но с целым рядом ведущих региональных операторов мы уже разговариваем о конкретных шагах, с кем-то находимся в процессе переговоров: пытаемся понять друг друга и выяснить перспективы создания альянса.

Какие цели Vegas-Lex ставит перед собой в плане регионального развития?

Мы хотим построить большую, мультиофисную юридическую компанию с отделениями во всех российских регионах и ключевых странах ближнего зарубежья – прежде всего в Украине и Казахстане. В настоящее время в России такой фирмы нет. Два-три офиса с этой точки зрения – явно недостаточны. Примером того, чего нам хочется достигнуть, являются ведущие международные юридические фирмы – с десятками отделений по всему миру, с их умением аккумулировать ресурсы для поддержания таких сетей и с их опытом управления гигантскими, по нашим меркам, системами.

В истории Vegas-Lex был период, когда компания открывала свои офисы в регионах. Сейчас мы выбрали несколько иную стратегию – внедрение Партнерской программы Фирмы. В настоящее время она объединяет 10 региональных фирм. И это при том, что мы начали активно по ней работать лишь в октябре 2007 года, после презентации нашей Партнерской программы на Международном юридическом Форуме в Ялте.

Мы очень осторожно подходим к вопросу партнерства. Партнерство предполагает совместимость. Но, как правило, наш бизнес и бизнес региональных компаний имеют много различий. В рамках переговоров мы пытаемся найти общее объединяющее начало.

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

Существует ли какое-то формальное закрепление такого сотрудничества?

Да, все действительно оформляется юридически. Процесс завершается подписанием протокола присоединения к Партнерской программе Vegas-Lex.

Насколько высока доля проектов, выполняемых совместно с региональными фирмами?

В настоящее время эта доля невелика, но есть тенденция к ее увеличению. Часть проектов мы просто передаем нашим партнерам, и они работают с клиентами напрямую.

Вы упомянули, что некоторые региональные фирмы рассматривают вашу компанию как конкурента, а не как потенциального партнера.

Да, такая проблема есть, и я считаю подобную реакцию совершенно нормальной. Открытие супермаркета в городе угрожает бизнесу средних магазинов на 10 кварталов вокруг. Но мы стараемся объяснить потенциальным партнерам, что не претендуем на занятые ими сегменты рынка. Мы предлагаем дополнительный рычаг, дополнительные возможности. После присоединения к Партнерской программе региональная фирма может рассматривать свой производственный потенциал как общий с Vegas-Lex. В одной сербской поговорке говорится: «Нас с русскими 300 миллионов». У всероссийской фирмы есть очевидные преимущества: независимость от местной власти, накопленный опыт участия в крупных и узкоспециализированных проектах, достаточные финансовые и кадровые ресурсы. В свою очередь, региональный партнер вкладывает в общее дело знание местного рынка и умение на нем работать, мобильность, связи и прочее. Если эти факторы дополняют друг друга, то выигрывают все.

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

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

Что может помешать налаживанию партнерства между Vegas-Lex и региональной фирмой?

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

Как Вы можете оценить текущий уровень качества услуг региональных фирм?

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

Если говорить о выполнении совместных проектов или передаче поручений на субподряд, как Vegas-Lex контролирует качество услуг региональных фирм?

Мы стараемся максимально подробно расписать «техническое задание» и делаем регулярные проверки качества исполнения. Когда создаются совместные рабочие группы, ответственность за ее работу принимает на себя Vegas-Lex. На первых порах могут возникать некоторые трудности, но после 2-3 проектов мы, как правило, достигаем полного взаимопонимания.

В чем заключаются основные различия в корпоративных культурах столичных и региональных фирм?

Это и подход к формированию стратегии, и мотивация сотрудников, и планирование текущей деятельности, и видение развития рыночной ситуации в перспективе. У многих региональных фирм вообще нет никакой стратегии, а горизонт планирования ограничивается двумя-тремя годами. Мотивация почти всегда сводится к деньгам – премия, процент от выигранной суммы. Мы же подходим к этому делу по-другому, и у нас основное значение имеют нематериальные факторы.

Проводите ли вы совместные с региональными партнерами маркетинговые мероприятия?

Мы крайне аккуратны, даже сдержанны в этом направлении. Мы считаем, что лучший маркетинг юридической фирмы – это успешно осуществленные проекты. Поэтому сейчас мы не проводим активный публичный маркетинг в этом направлении.

Многие региональные фирмы имеют широкую специализацию. Является ли такой подход ограничением для развития региональных фирм? Если да, то почему?

Я считаю, что для юридической фирмы широкая специализация – вещь пагубная. Нельзя продавать картошку в бутике. Точнее, продавать-то можно, но покупать не будут ни картошку, ни предметы роскоши. Обстановка бутика напугает потребителя картошки, а ценителя высокой моды будет раздражать соседство, несоответствующее его статусу. Нельзя смешивать «регистрацию фирмы от 3000 рублей» и «сопровождение особо сложных инвестиционных проектов» – это только ухудшает рыночные позиции компании. Нужно определяться со специализацией, выбрать свою нишу и работать на нее.

Фирмы с какой специализацией являются для Vegas-Lex наиболее привлекательными кандидатами для сотрудничества?

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

Что нужно делать региональным фирмам, чтобы стать привлекательными партнерами для крупных российских фирм?

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *