Windows 7 in stile Goccia d’ombra in forma senza bordi

Versione breve:

Obiettivo: un’oscurità di Windows 7 profonda e scura in WinForm senza bordi in C #


Soluzioni esistenti conosciute 1: semplice ombretto in stile XP con CreateParams.

Problema: troppo debole, troppo leggero, troppo brutto.


Soluzioni esistenti note 2: Sostituisci GDI di modulo con bitmap.

Problema: perdere la possibilità di utilizzare i controlli, solo funzionali come una schermata iniziale.


Obiettivo da questo post: trovare una soluzione mediana a questo problema o una migliore insieme.

. . .

Versione lunga:

(Modifica: mi riferisco all’ombra che passa lungo il bordo di qualsiasi forma di finestra, se questo non era chiaro.) Capisco che c’è un modo per rendere gli ombretti in stile XP in C # usando:

C # Codice 1 – Semplice ombretto in stile XP (Problema: alla luce, al debole, al brutto)

// Define the CS_DROPSHADOW constant private const int CS_DROPSHADOW = 0x00020000; // Override the CreateParams property protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ClassStyle |= CS_DROPSHADOW; return cp; } } 

Tuttavia, sto cercando di capire come farli apparire come fanno in Windows 7 (ombre più profonde e più grandi) e non riesco a capire il modo migliore per farlo.

Ho ora creato un metodo che mi permetterà di sovrascrivere l’intero modulo GDI e apparire come uno splash screen (credito non mio):

C # Codice 2: Sostituisci il modulo GDI con Bitmap (Problema: non è ansible utilizzare i controlli del modulo, GUI di difficile manutenzione)

  public void SetBitmap(Bitmap bitmap, byte opacity) { if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); // 1. Create a compatible DC with screen; // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC; // 3. Call the UpdateLayeredWindow. IntPtr screenDc = Win32.GetDC(IntPtr.Zero); IntPtr memDc = Win32.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr oldBitmap = IntPtr.Zero; try { hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap oldBitmap = Win32.SelectObject(memDc, hBitmap); Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); Win32.Point pointSource = new Win32.Point(0, 0); Win32.Point topPos = new Win32.Point(Left, Top); Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); blend.BlendOp = Win32.AC_SRC_OVER; blend.BlendFlags = 0; blend.SourceConstantAlpha = opacity; blend.AlphaFormat = Win32.AC_SRC_ALPHA; Win32.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); } finally { Win32.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { Win32.SelectObject(memDc, oldBitmap); Win32.DeleteObject(hBitmap); } Win32.DeleteDC(memDc); } } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style return cp; } } 

Tuttavia, questo mi dà uno sfondo a 32 bit (come ho bisogno di aggiungere manualmente il dropshadow), ma perdo la possibilità di creare elementi del modulo che sono visibili.

Quindi, in sostanza, sto cercando di capire una mediana tra questi due metodi. Qualcosa che mi darà ombre scure profonde e scure senza perdere altre funzionalità / causando eccessivi requisiti di riverniciatura.

Okay, quindi dopo circa 4 ore di brainstorming e codifica, ho finalmente sviluppato una soluzione. Fondamentalmente, ho creato 2 forms.

Modulo n. 1 : crea l’ombretto modificando e combinando 8 immagini (4 gradienti angoli + 4 gradienti lineari per ciascuna direzione) e impostale come sfondo utilizzando il secondo codice che ho postato sopra ( C # Codice 2: Sostituisci modulo GDI con Bitmap ). Il codice praticamente lo spiega.

 public partial class Dropshadow : Form { public Dropshadow(Form parentForm) { /*This bit of code makes the form click-through. So you can click forms that are below it in z-space */ int wl = GetWindowLong(this.Handle, -20); wl = wl | 0x80000 | 0x20; SetWindowLong(this.Handle, -20, wl); InitializeComponent(); //Makes the start location the same as parent. this.StartPosition = parentForm.StartPosition; parentForm.Activated += ParentForm_Activated; //Fires on parent activation to do a this.BringToFront() this.Deactivate += This_Deactivated; //Toggles a boolean that ensures that ParentForm_Activated does fire when clicking through (this) parentForm.Closed += ParentForm_Closed; //Closes this when parent closes parentForm.Move += ParentForm_Move; //Follows movement of parent form //Draws border with standard bitmap modifications and merging /* Omitted function to avoid extra confusion */ Bitmap getShadow = DrawBlurBorder(parentForm.ClientSize.Width, parentForm.ClientSize.Height); /* **This code was featured in the original post:** */ SetBitmap(getShadow, 255); //Sets background as 32-bit image with full alpha. this.Location = Offset; //Set within DrawBlurBorder creates an offset } private void ParentForm_Activated(object o, EventArgs e) { /* Sets this form on top when parent form is activated.*/ if (isBringingToFront) { /*Hopefully prevents recusion*/ isBringingToFront = false; return; } this.BringToFront(); /* Some special tweaks omitted to avoid confusion */ } private void This_Deactivated(object o, EventArgs e) { /* Prevents recusion. */ isBringingToFront = true; } /* Closes this when parent form closes. */ private void ParentForm_Closed(object o, EventArgs e) { this.Close(); } /* Adjust position when parent moves. */ private void ParentForm_Move(object o, EventArgs e) { if(o is Form) this.Location = new Point((o as Form).Location.X + Offset.X, (o as Form).Location.Y + Offset.Y); } } 

Modulo n. 2 : al momento del lancio è stata appena lanciata la forma di dropshadow e ho anche creato alcune interfacce per consentire ulteriore integrazione e flessibilità che ho omesso per evitare ulteriore confusione. Fondamentalmente i metodi per garantire che la forma di Dropshadow non stia portando via i clic del mouse dal modulo attivo e non costringerebbe l’utente a fare clic su un pulsante due volte se la forma di Dropshadow era in cima.

Grazie, Corylulu.

Una class praticabile è qui .

 var f = new Dropshadow(this) { BorderRadius = 40, ShadowColor = Color.Blue }; f.RefreshShadow(); 

DEMO

Il DrawShadow crea un’ombra come bitmap, ma non è ancora perfetta. Questa class non è perfetta, ma funziona.

A proposito, non so come hide il modulo ombra nella barra delle applicazioni. Imposta ShowInTaskBar = false causerà la scomparsa del modulo.

MODIFICARE

Riscrivo la class, ora sembra così, un vero DropShadow.

La fonte è qui

Una cosa che dovresti sapere è che questa class non considera il border-radius (prende forma in css).

La proprietà principale è

  • ShadowColor
  • ShadowV
  • ShadowH
  • ShadowSpread
  • ShadowBlur

La proprietà è uguale a css box-shadow , vedi qui

Questi propertyies

  • ShadowSpread
  • ShadowBlur
  • ShadowColor

richiede la chiamata manuale RefreshShadow() .

Vai al progetto demo