2009-06-30 20 views
12

Come attenuare/sbiadire tutte le altre finestre di un'applicazione in Delphi 2009.Dissolvi tutte le altre finestre di un'applicazione quando viene visualizzata una finestra di dialogo?

Il modulo ha una proprietà AlphaBlend, ma controlla solo il livello di trasparenza. Ma sarebbe bello se potessimo avere qualcosa come questo (Concentrated window). Anche stackoverflow.com lo fa, quando proviamo a inserire un link/immagine ecc nel post.

Come possiamo ottenere questo risultato in un'applicazione delphi?

+0

Si dovrebbe chiarire la tua domanda, dim/svanire tutte le altre finestre di applicazione è diverso dall'effetto Exposé (che non ha un posto su Windows in ogni caso , questo è nient'altro che programmazione settoriale cargo senza la funzionalità Exposé) nel link. – mghie

+1

Si noti che questo comportamento è incoerente con il modo in cui si comportano tutte le altre app sulla piattaforma Windows. Anche se è un effetto piuttosto interessante, è probabile che tu stia solo contribuendo al caos dell'interfaccia che è già la piattaforma Windows. – onnodb

risposta

22

Ecco un'unità che ho appena messo insieme per te.

Per utilizzare questa unità, rilasciare un componente TApplication nel modulo principale e nella chiamata OnModalBegin _GrayForms e quindi nel metodo OnModalEnd chiamare il metodo _NormalForms.

Questo è un esempio molto semplice e potrebbe essere reso molto più complesso. Verifica di più livelli di chiamata, ecc ....

Per cose come i dialoghi di sistema (aperto, salvataggio, ecc.) È possibile avvolgere il metodo di esecuzione della finestra di dialogo in un tentativo ... infine bloccare le funzioni appropriate per ottenere una reazione simile .

Questa unità dovrebbe funzionare su Win2k, WinXP, Vista e dovrebbe funzionare anche su 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

Soluzione interessante, ma perché non utilizzare solo un modulo che copre l'intero desktop con un valore alfanumerico più grande? Il metodo sopra provocherà caselle su forme sovrapposte che appariranno più scure. – skamradt

+3

Se stai andando a sovrapporre l'intero desktop, sarebbe fantastico, ma immagino che sia utile solo per una finestra di dialogo modale di sistema piuttosto che per una finestra di dialogo modale dell'applicazione. Come ho detto, questo è stato qualcosa che ho buttato giù rapidamente come esempio. Al momento non ho un uso per qualcosa di simile e ho pensato che sarebbe stato un buon esercizio mentale. –

+2

Oltre alla tua domanda riguarda tutte le altre finestre dell'applicazione. Non il desktop. –

1

Non sono sicuro del modo "giusto" per farlo, ma per "dissolvenza", ciò che puoi fare è posizionare il modulo in un'altra forma completamente bianca (colore di sfondo bianco, no controlli).

Così quando il tuo modulo è in trasparenza 0%, verrà visualizzato come un modulo normale, ma quando sarà in trasparenza del 50% verrà sbiadito in bianco. Puoi ovviamente scegliere altri colori come sfondo.

Non vedo l'ora di vedere altre risposte ...

EDIT: dopo aver visto il tuo link "Jedi Concentrate", sembra che uno sfondo grigio scuro sarà mimare l'effetto di esporre al meglio.

0

ho creato un effetto simile a quello Jedi concentrato con un modulo dimensionato per la Screen.WorkArea con Colore: = clblack e BorderStyle: = bsNone

ho trovato l'impostazione del AlphaBlendValue era troppo lento per animare piacevolmente , per cui uso SetLayeredWindowAttributes()

codice dell'unità:

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 per chiamare i metodi VCL da un thread di lavoro e per chiamare le funzioni API di Windows su un HWND creato da un thread diverso. – mghie

+0

Ho usato questo per un paio d'anni e non ho notato alcun problema. Ci sono problemi che non ho visto? e quali sono i metodi VCL nel thread di lavoro? – jasonpenny

+0

L'utilizzo di "fForm.Handle" nel thread di lavoro può comportare il richiamo di un numero qualsiasi di metodi VCL, se l'handle non è ancora stato assegnato. Il tuo codice potrebbe funzionare, ma chiamare i metodi VCL dai thread bg dovrebbe essere evitato in generale. E mentre l'accesso agli oggetti di Windows da diversi thread può funzionare, è molto difficile ottenere il giusto. Se possibile, evitalo pure. Buona risorsa: http://blogs.msdn.com/oldnewthing/archive/2005/10/10/479124.aspx e le quattro parti seguenti. Frase di scelta: "in generale, le modifiche a una finestra devono essere apportate solo dal thread che la possiede". – mghie

1

Un modo per farlo è quello di mettere un'altra forma dietro la finestra di dialogo, questo fo rm non avrebbe bordi e conterrebbe una singola immagine. Questa immagine sarebbe stata una cattura dell'intero desktop appena prima che la finestra di dialogo fosse spuntata, quindi eseguita attraverso una trasformazione per ridurre la luminosità di ogni pixel del 50%. Un trucco che funziona abbastanza bene qui è usare una forma nera e includere solo altri pixel. Se sai per certo che avrai il supporto per i temi, puoi opzionalmente usare un modulo completamente nero e usare le proprietà alphablend e alphablendvalue..questo permetterà al sistema operativo di eseguire la trasformazione della luminosità per te. Un valore alfablu di 128 è = 50%.

EDIT

Come mghie indicate, v'è la possibilità di un utente preme alt-tab per passare ad un'altra applicazione. Un modo per gestire questo scenario sarebbe quello di nascondere la finestra "overlay" nell'applicazione. Disattivare l'evento e mostrarlo sull'evento application.OnActivate. Ricorda solo di impostare lo zorder della finestra overlay più in basso della finestra di dialogo modale.

+0

Cosa succede quando l'utente alterna le schede ad un'altra applicazione mentre la finestra di dialogo modale è ancora aperta? – mghie

7

Ho fatto qualcosa di simile per mostrare una forma modale cercando di mantenere l'implementazione il più semplice possibile. Non so se questo si adatta alle vostre esigenze, ma qui è:

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; 
Problemi correlati