Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:

Error. Page cannot be displayed. Please contact your service provider for more details. (16)


Как сделать полупрозрачное окно

Поиск:
ТЕОРИЯ:

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

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

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

Внутренняя схема перерисовки окон в Windows такова, что каждый раз перерисовывается только та его часть, которая не закрыта другими окнами и, следовательно, видна на экране. Это ускоряет процесс обновления экрана. Но полупрозрачное окно должно каким-то образом получать информацию о том, что нарисовано под ним. Здесь мы вступаем в противоречие с правилами Windows, поэтому следует прибегнуть к некоторому обману. При необходимости перерисовать полупрозрачное окно надо это окно ненадолго убрать с экрана. Как только все нижележащие окна будут перерисованы, надо запомнить ту область экрана, которая будет закрыта окном, вновь вывести на экран это окно и отрисовать его с учётом сохранённой картинки. Первая трудность на этом пути - как узнать, что перерисовка всех окон закончилась? Самый примитивный способ- подождать! Скажу только, что времени задержки 400 мс, которое установлено в этом примере вполне хватило. Вторая трудность - если вдруг окно, лежащее под полупрозрачным, обновилось, то это не приведёт сразу же к перерисовке этого окна.

Теперь о том, как осуществить всё это на практике. Перерисовка окна определяется обработкой одного из двух сообщений: WM_EraseBkgnd и WM_Paint. WM_Paint лучше вот почему: Во-первых, WM_EraseBkgnd может посылаться по несколько раз, соответственно окно перерисовывается до нескольких раз подряд. Во-вторых, между WM_EraseBkgnd и WM_Paint есть существенная разница: в первом случае Windows сам определяет, какая часть окна должна перерисоваться, и рисовать за пределами этой части просто не разрешает. А полупрозрачное окно, вообще говоря, обладает нетрадиционной точкой зрения на этот вопрос, что и приводит к конфликтам, особенно тогда, когда полупрозрачное окно частично закрыто другими окнами. Что же касается WM_Paint, то и тут Windows, конечно же, держит всё под контролем и тоже следит за тем, какая область окна должна быть перерисована. Однако, к счастью для полупрозрачных окон, это всё не выливается в прямые запреты, как в случае с WM_EraseBkgnd, здесь Windows ограничивается только выдачей ценных указаний через BeginPaint и TPaintStruct. Ну, а теперь мы отрисовываем окно целиком. И наконец, в-третьих, Windows зачем-то генерирует WM_EraseBkgnd после выполнения ShowWindow, поэтому попытка спрятать окно при обработке этого сообщения приведёт к бесконечной рекурсии. Впрочем, пренебрегать WM_EraseBkgnd тоже не стоит. Дело в том, что во время запуска программы этому процессу, видимо, присваивается повышенный приоритет. Это приводит к тому, что наше окно начинает рисоваться, Windows посылает WM_EraseBkgnd, стандартная процедура обработки этого события закрашивает всю клиентскую часть окна красивым серым цветом (как обычно), затем обрабатывается WM_Paint, в котором окно прячется с экрана, после чего остальные окна должны быстро перерисоваться и, когда пройдёт заданное время, программа посмотрит, что там нарисовано, начнёт наложение светофильтра... А окна-то не успели перерисоваться! Программа увидит своё собственное окно, которое мы не успели стереть. Это нам не надо, поэтому, чтобы не увеличивать время ожидания, нужно заблокировать вызов стандартного обработчика WM_EraseBkgnd. Это, естественно, никак не отразится на скорости перерисовки остальных окон, но ведь и само окно ничего не нарисует на экране, и после ожидания программа увидит то, что надо.

И последний штрих: при перемещении окна Windows, во избежание ненужных действий, не запускает механизм перерисовки, а просто переносит изображение с одного места на другое. Для полупрозрачных окон это недопустимо, изображение должно обновляться при каждом переносе. Для этого надо отслеживать сообщение WM_Move, которое возникает в таких случаях. И, соответственно, запускать перерисовку окна. Если WM_Move вам не подходит , вы можете использовать WM_WindowPosChanged. Я не заметил разницы...

Проблема заключается в том, что в некоторых версиях системы при перемещении окна не рисуется рамка, а каждый раз происходит перерисовка всего окна целиком. То же самое происходит и при изменении размеров окна. Так происходит, например, в Win NT и в Win95 при установленном MS Plus! Ключ к решению проблемы лежит в обработке сообщений WM_EnterSizeMove и WM_ExitSizeMove. Нужно завести переменную типа boolean, которая будет изменяться при начале перетаскивания с False на True и наоборот при его завершении. Соответственно обработчик WM_Paint должен следить за этой переменной и не выполнять задержку. Проблема - как узнать, что должно быть под окном. Если каждый раз запоминать не только нужную часть экрана, а весь экран целиком, то к моменту входа в режим перетаскивания программа будет обладать всей необходимой информацией о том, что там внизу. Теперь надо будет только вырезать нужный кусок. Главный недостаток такого подхода - программа должна быть, как пионер, всегда готова к началу перетаскивания и всегда сохранять экран целиком, что приведёт к дополнительному расходу памяти. Но от этого тоже можно избавиться, храня только нужный кусочек экрана (пока не реализовано).

ПРАКТИКА:
Бросим на форму Button, SpinEdit, ColorDialog.

Потом приводим код к такому состоянию:
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Spin;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ColorDialog1: TColorDialog;
    SpinEdit1: TSpinEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure SpinEdit1Change(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    BM:TBitmap;
    BM2:TBitmap;
    Moving:Boolean;
    procedure WMEraseBkgnd(var Msg:TWMEraseBkgnd);message WM_EraseBkgnd;
    procedure WMPaint(var Msg:TWMPaint);message WM_Paint;
    procedure WMMove(var Msg:TMessage);message WM_Move;


    procedure WMEnterSizeMove(var Msg:TMessage);message WM_EnterSizeMove;
    procedure WMExitSizeMove(var Msg:TMessage);message WM_ExitSizeMove;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  TranspColor: TColor;
   Transparency:Integer=40;
const
      DelayTime:Integer=400;

implementation

{$R *.DFM}

type PRGBArray=^TRGBArray;
     TRGBArray=array[0..1000000] of TRGBTriple;


procedure Delay(DelayTime:Integer);
 var TicksNow:Integer;
  begin
   TicksNow:=GetTickCount;
   repeat
    Application.ProcessMessages
   until GetTickCount-TicksNow>=DelayTime
  end;



procedure TForm1.WMEraseBkgnd;
 begin
  Msg.Result:=1

 end;

procedure TForm1.WMPaint;
 var DC:HDC;
     PS:TPaintStruct;
     CW,CH,CX,CY:Integer;
     SL:PRGBArray;
     X,Y:Integer;
  begin
   CW:=ClientWidth;
   CH:=ClientHeight;
   CX:=ClientOrigin.X;
   CY:=ClientOrigin.Y;

   if not Moving then
    begin
     ShowWindow(Handle,SW_Hide);


     SetActiveWindow(0);

     Delay(400);

     DC:=GetDC(0);

     BitBlt(BM.Canvas.Handle,0,0,BM.Width,BM.Height,DC,0,0,SrcCopy);

     ReleaseDC(0,DC);
    end;

   BM2.Width:=CW+1;
   BM2.Height:=CH+1;
   BM2.PixelFormat:=pf24bit;
   BM2.Canvas.Draw(-CX,-CY,BM);
   for Y:=0 to CH do
    begin
     SL:=BM2.ScanLine[Y];
     for X:=0 to CW do
      begin
       SL[X].rgbtRed:=(Transparency*SL[X].rgbtRed+(100-Transparency)*GetRValue(TranspColor)) div 100;
       SL[X].rgbtGreen:=(Transparency*SL[X].rgbtGreen+(100-Transparency)*GetGValue(TranspColor)) div 100;
       SL[X].rgbtBlue:=(Transparency*SL[X].rgbtBlue+(100-Transparency)*GetBValue(TranspColor)) div 100
      end
    end;

   ShowWindow(Handle,SW_Show);
   DC:=BeginPaint(Handle,PS);

   BitBlt(DC,0,0,BM2.Width,BM2.Height,BM2.Canvas.Handle,0,0,SrcCopy);

   Msg.DC:=DC;
   inherited;

   EndPaint(Handle,PS)
  end;

procedure TForm1.WMMove;
 begin
  Invalidate;
  inherited
 end;

procedure TForm1.WMEnterSizeMove;
 begin
  Moving:=True;
  inherited
 end;

procedure TForm1.WMExitSizeMove;
 begin
  inherited;
  Moving:=False
 end;

procedure TForm1.FormCreate(Sender: TObject);
 begin
  BM:=TBitmap.Create;
  BM.Width:=GetSystemMetrics(SM_CXScreen);
  BM.Height:=GetSystemMetrics(SM_CYScreen);
  BM.PixelFormat:=pf24bit;
  BM2:=TBitmap.Create;
  Moving:=False
 end;

procedure TForm1.Button1Click(Sender: TObject);
 begin
  if ColorDialog1.Execute then
   begin
    TranspColor:=ColorDialog1.Color;
    Invalidate
   end
 end;

procedure TForm1.SpinEdit1Change(Sender: TObject);
 begin
  Transparency:=SpinEdit1.Value;
  Invalidate
 end;

procedure TForm1.FormDestroy(Sender: TObject);
 begin
  BM.Free;
  BM2.Free
 end;

end. 


Приведенный код без комментариев. Они все в присоединенном файле. Дерзайте.

Послесловие:
Тут довольно много некорректного кода (по причине того, что я много еще не знаю).
Просьба все улучшения выкладывать в этой теме (без флейма, плз).
В примере добавлены два label`а, чтобы показать, что их свойство Transparent работает.
И еще- попробуйте в SpinEdit добавить ноликов, чтобы значение выходило за «100», будет красиво!

Помогли- DelphiKingdom
Автор: SoWa
Сайт: http://www.pclub.net.ru/forum/index.php






Просмотров: 4603

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную