2009-06-30 13 views
12

Cómo atenuar/atenuar todas las demás ventanas de una aplicación en Delphi 2009.Fundir todas las otras ventanas de una aplicación cuando se muestra un cuadro de diálogo?

El formulario tiene una propiedad AlphaBlend, pero solo controla el nivel de transparencia. Pero sería bueno si podemos tener algo como esto (Concentrated window). Incluso stackoverflow.com hace eso, cuando tratamos de insertar un enlace/imagen, etc. en la publicación.

¿Cómo podemos lograr esto en una aplicación delphi?

+0

Debe aclarar su pregunta, dim/desaparecer todas las demás ventanas de la aplicación es diferente del efecto de ficha (que no tiene lugar en Windows de todos modos , eso no es más que programación de culto a la carga sin la funcionalidad Exposé) en el enlace. – mghie

+1

Tenga en cuenta que este comportamiento no es coherente con la forma en que se comportan todas las demás aplicaciones en la plataforma de Windows. Aunque es un efecto bonito, es probable que contribuya al caos de la interfaz que ya tiene la plataforma Windows. – onnodb

Respuesta

22

Aquí hay una unidad que acabo de unir para usted.

Para usar esta unidad, suelte un componente de TApplication en su formulario principal y en la llamada OnModalBegin _GrayForms y luego en OnModalEnd llame al método _NormalForms.

Este es un ejemplo muy simple y podría hacerse más complejo muy fácilmente. Comprobación de niveles de llamadas múltiples, etc.

Para cosas como los diálogos de sistema (abrir, guardar, etc.) puede ajustar el método de ejecución de diálogo en un intento ... finalmente bloquear llamando a las funciones apropiadas para obtener una reacción similar .

Esta unidad debería funcionar en Win2k, WinXP, Vista e incluso debería funcionar en Win7.

Ryan.

unit GrayOut; 

interface 

procedure _GrayForms; 
procedure _GrayDesktop; 
procedure _NormalForms; 

implementation 

uses windows, classes, forms, Contnrs, Types, Graphics, sysutils; 

var 
    gGrayForms : TComponentList; 

procedure _GrayDesktop; 
var 
    loop : integer; 
    wScrnFrm : TForm; 
    wForm : TForm; 
    wPoint : TPoint; 

begin 
    if not assigned(gGrayForms) then 
    begin 
     gGrayForms := TComponentList.Create; 
     gGrayForms.OwnsObjects := true; 

     for loop := 0 to Screen.MonitorCount - 1 do 
     begin 
     wForm := TForm.Create(nil); 
     gGrayForms.Add(wForm); 

     wForm.Position := poDesigned; 
     wForm.AlphaBlend := true; 
     wForm.AlphaBlendValue := 64; 
     wForm.Color := clBlack; 
     wForm.BorderStyle := bsNone; 
     wForm.Enabled := false; 
     wForm.BoundsRect := Screen.Monitors[loop].BoundsRect; 
     SetWindowPos(wForm.handle, HWND_TOP, 0,0,0,0, SWP_NOSIZE or SWP_NOMOVE); 
     wForm.Visible := true; 
     end; 
    end; 
end; 

procedure _GrayForms; 
var 
    loop : integer; 
    wScrnFrm : TForm; 
    wForm : TForm; 
    wPoint : TPoint; 
    wScreens : TList; 

begin 
    if not assigned(gGrayForms) then 
    begin 
     gGrayForms := TComponentList.Create; 
     gGrayForms.OwnsObjects := true; 

     wScreens := TList.create; 
     try 
     for loop := 0 to Screen.FormCount - 1 do 
      wScreens.Add(Screen.Forms[loop]); 

     for loop := 0 to wScreens.Count - 1 do 
     begin 
      wScrnFrm := wScreens[loop]; 

      if wScrnFrm.Visible then 
      begin 
       wForm := TForm.Create(wScrnFrm); 
       gGrayForms.Add(wForm); 

       wForm.Position := poOwnerFormCenter; 
       wForm.AlphaBlend := true; 
       wForm.AlphaBlendValue := 64; 
       wForm.Color := clBlack; 
       wForm.BorderStyle := bsNone; 
       wForm.Enabled := false; 
       wForm.BoundsRect := wScrnFrm.BoundsRect; 
       SetWindowLong(wForm.Handle, GWL_HWNDPARENT, wScrnFrm.Handle); 
       SetWindowPos(wForm.handle, wScrnFrm.handle, 0,0,0,0, SWP_NOSIZE or SWP_NOMOVE); 
       wForm.Visible := true; 
      end; 
     end; 
     finally 
     wScreens.free; 
     end; 
    end; 
end; 

procedure _NormalForms; 
begin 
    FreeAndNil(gGrayForms); 
end; 

initialization 
    gGrayForms := nil; 

end. 
+4

Una solución interesante, pero ¿por qué no usar solo un formulario que cubra todo el escritorio con un alphablendvalue más grande? El método anterior causará cuadros en formularios superpuestos que aparecerán más oscuros. – skamradt

+3

Si va a superponerse a todo el escritorio, sería genial, pero supongo que solo es útil para un diálogo modal del sistema en lugar de un diálogo modal de la aplicación. Como dije, esto fue algo que fallé rápidamente como un ejemplo. Actualmente no tengo un uso para algo como esto y pensé que sería una buena exersize mental. –

+2

Además su pregunta es sobre todas las demás ventanas de la aplicación. No el escritorio –

1

No estoy seguro de la forma "correcta" de hacerlo, pero para "fundir el blanco", lo que puede hacer es colocar su formulario en otra forma totalmente blanca (color de fondo blanco, no controles).

Así que cuando su formulario está en 0% de transparencia, se mostrará como una forma regular, pero cuando esté en 50% de transparencia se desvanecerá en blanco. Obviamente puedes elegir otros colores como fondo.

Estoy deseando ver otras respuestas ...

EDIT: después de ver su enlace "Concentrado Jedi", parece que un fondo de color gris oscuro se imitan el efecto de exponer mejor.

0

he creado un efecto similar a los Jedi concentrado con un formulario dimensionado para la Screen.WorkArea con Color: = clBlack y EstiloDeLosBordes: = bsNone

me encontré con el establecimiento de la AlphaBlendValue era demasiado lento para animar muy bien , así que uso SetLayeredWindowAttributes()

código de la unidad:

unit frmConcentrate; 

{$WARN SYMBOL_PLATFORM OFF} 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs; 

type 
    TFadeThread = class(TThread) 
    private 
     fForm: TForm; 
    public 
     constructor Create(frm: TForm); 
     procedure Execute; override; 
    end; 

    TConcentrateFrm = class(TForm) 
     procedure FormDestroy(Sender: TObject); 
     procedure FormClick(Sender: TObject); 
    private 
     { Private declarations } 
     fThread: TFadeThread; 
    public 
     { Public declarations } 
    end; 

procedure StartConcentrate(aForm: TForm = nil); 

var 
    ConcentrateFrm: TConcentrateFrm; 

implementation 

{$R *.dfm} 

procedure StartConcentrate(aForm: TForm = nil); 
var 
    Hnd: HWND; 
begin 
    try 
     if not Assigned(ConcentrateFrm) then 
     ConcentrateFrm := TConcentrateFrm.Create(nil) 
     else 
     Exit; 

     ConcentrateFrm.Top := Screen.WorkAreaTop; 
     ConcentrateFrm.Left := Screen.WorkAreaLeft; 
     ConcentrateFrm.Width := Screen.WorkAreaWidth; 
     ConcentrateFrm.Height := Screen.WorkAreaHeight; 

     Hnd := GetForegroundWindow; 

     SetWindowLong(ConcentrateFrm.Handle, GWL_EXSTYLE, 
     GetWindowLong(ConcentrateFrm.Handle, GWL_EXSTYLE) or WS_EX_LAYERED 
    ); 
     SetLayeredWindowAttributes(
     ConcentrateFrm.Handle, 
     ColorToRGB(clBlack), 
     0, 
     LWA_ALPHA 
    ); 
     ConcentrateFrm.Show; 

     if Assigned(aForm) then 
     aForm.BringToFront 
     else 
     SetForegroundWindow(Hnd); 

     ConcentrateFrm.fThread := TFadeThread.Create(ConcentrateFrm); 
     Application.ProcessMessages; 
     ConcentrateFrm.fThread.Resume; 
    except 
     FreeAndNil(ConcentrateFrm); 
    end; 
end; 

procedure TConcentrateFrm.FormClick(Sender: TObject); 
var 
    p: TPoint; 
    hnd: HWND; 
begin 
    GetCursorPos(p); 

    ConcentrateFrm.Hide; 
    hnd := WindowFromPoint(p); 
    while GetParent(hnd) 0 do 
     hnd := GetParent(hnd); 

    SetForegroundWindow(hnd); 

    Release; 
end; 

procedure TConcentrateFrm.FormDestroy(Sender: TObject); 
begin 
    ConcentrateFrm := nil; 
end; 

{ TFadeThread } 

constructor TFadeThread.Create(frm: TForm); 
begin 
    inherited Create(true); 
    FreeOnTerminate := true; 
    Priority := tpIdle; 

    fForm := frm; 
end; 

procedure TFadeThread.Execute; 
var 
    i: Integer; 
begin 
    try 
     // let the main form open before doing this intensive process. 
     Sleep(300); 

     i := 0; 
     while i < 180 do 
     begin 
     if not Win32Check(
      SetLayeredWindowAttributes(
       fForm.Handle, 
       ColorToRGB(clBlack), 
       i, 
       LWA_ALPHA 
      ) 
     ) then 
     begin 
      RaiseLastOSError; 
     end; 
     Sleep(10); 
     Inc(i, 4); 
     end; 
    except 
    end; 
end; 

end.
+1

-1 para llamar a los métodos VCL de un hilo de trabajo y para llamar a las funciones de API de Windows en un HWND creado por un hilo diferente. – mghie

+0

He estado usando esto por un par de años y no he notado ningún problema. ¿Hay problemas que simplemente no he visto? y que son métodos VCL en el hilo de trabajo? – jasonpenny

+0

El uso de "fForm.Handle" en el subproceso de trabajo puede dar como resultado que se invoque una cantidad de métodos de VCL, si el asa no se ha asignado aún. Su código puede funcionar, pero es mejor evitar los métodos VCL de bg threads en general. Y aunque acceder a los objetos de Windows desde diferentes subprocesos puede funcionar, es muy difícil hacerlo bien. Si es posible, evítelo también. Buen recurso: http://blogs.msdn.com/oldnewthing/archive/2005/10/10/479124.aspx y las cuatro partes siguientes. Cita de elección: "en términos generales, las modificaciones de una ventana solo deben realizarse desde el hilo que lo posee". – mghie

1

Una forma de hacer esto es colocar otra forma detrás de diálogo, este fo rm no tendría bordes, y contendría una sola imagen. Esta imagen sería una captura de todo el escritorio justo antes de que apareciera el diálogo, luego se ejecutará a través de una transformación para reducir la luminosidad de cada píxel en un 50%. Un truco que funciona bastante bien aquí es usar una forma negra y solo incluir cualquier otro píxel. Si sabe con certeza que tendrá compatibilidad con temas, puede utilizar opcionalmente una forma completamente negra y usar las propiedades alphablend y alphablendvalue ... esto le permitirá al sistema operativo realizar la transformación de luminosidad por usted. Un alphablendvalue de 128 es = 50%.

EDITAR

Como mghie señalado, no es la posibilidad de un usuario presionando alt-tab para cambiar a otra aplicación. Una forma de manejar este escenario sería ocultar la ventana "superposición" en el evento application.OnDeActivate, y mostrarlo en la aplicación. Evento OnActivate. Solo recuerde configurar el zorder de la ventana de superposición inferior a su cuadro de diálogo modal.

+0

¿Qué sucede cuando el usuario alterna las pestañas con otra aplicación mientras el diálogo modal aún está abierto? – mghie

7

He hecho algo similar para mostrar un formulario modal tratando de mantener la implementación lo más simple posible. No sé si esto se ajuste a sus necesidades, pero aquí está:

function ShowModalDimmed(Form: TForm; Centered: Boolean = true): TModalResult; 
var 
    Back: TForm; 
begin 
    Back := TForm.Create(nil); 
    try 
    Back.Position := poDesigned; 
    Back.BorderStyle := bsNone; 
    Back.AlphaBlend := true; 
    Back.AlphaBlendValue := 192; 
    Back.Color := clBlack; 
    Back.SetBounds(0, 0, Screen.Width, Screen.Height); 
    Back.Show; 
    if Centered then begin 
     Form.Left := (Back.ClientWidth - Form.Width) div 2; 
     Form.Top := (Back.ClientHeight - Form.Height) div 2; 
    end; 
    result := Form.ShowModal; 
    finally 
    Back.Free; 
    end; 
end; 
Cuestiones relacionadas