2012-01-09 10 views
9

Short Version:Windows 7 styl dropshadow w postaci obramowania

Cel: głęboki, ciemny, Windows 7 dropshadow w obramowania WinForm w C#


Znane istniejące rozwiązania 1: Prosty cień w stylu XP z wykorzystaniem CreateParams.

Problem: Słaby, jasny, brzydki.


istniejące znane rozwiązania 2: Wymienić GDI formy z mapy bitowej.

Problem: Stracić możliwość korzystania z elementów sterujących, funkcjonalny tylko jako ekran powitalny.


Cel przez tego postu: Znajdź medianę rozwiązanie tego problemu lub wszystko razem lepszy.

. . .

Długa wersja:

(Edycja:. Mam na myśli listy rozwijanej cieniu będzie wzdłuż granicy jakiejkolwiek formie okien, jeśli nie było jasne) Rozumiem że istnieje sposób, aby XP dropshadows stylu w C# za pomocą:

C# Code 1 - Prosty XP-styl dropshadow (problem: na światło, słaby, aby brzydki)

// 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; 
    } 
} 

Jednak staram się dowiedzieć, jak sprawić, aby wyglądały jak w Windows 7 (głębsze i większe cienie) i nie mogą znaleźć najlepszego sposobu na zrobienie tego.

Mam metodę teraz utworzony, który pozwoli mi zastąpić całą formularz GDI i wyglądać ekran powitalny będzie (kredyt nie moje):

C# Code 2: Wymień formularz GDI z problemem Bitmap (: można „t używać formantów formularzy, trudne do utrzymania GUI)

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; 
     } 
    } 

jednak to daje mi pełną 32-bitową tła (jak mogę wymagać, aby dodać dropshadow ręcznie), ale tracą zdolność do tworzenia form elementy, które są widoczne.

W zasadzie próbuję ustalić medianę między tymi dwoma metodami. Coś, co da mi głębokie i ciemne cienie bez utraty innej funkcjonalności/powodując nadmierne wymagania dotyczące ponownego malowania.

+1

Czym dokładnie jest twoje pytanie? To naprawdę nie jest miejsce, w którym można napisać rozwiązanie dla ciebie. –

+3

@Ramhound Pytanie jest proste. Szukam w tym kierunku właściwego wskazania. Moje pytanie jest dobrze zbadane i tak wyraźne, jak tylko mogłem. Chcę tylko wiedzieć, czy ktokolwiek wie o lepszym sposobie tworzenia cieni w stylu systemu Windows 7. – Corylulu

+1

Czy możesz dodać zrzut ekranu lub makietę pożądanego rezultatu? –

Odpowiedz

7

Okej, więc po około 4 godzinach burzy mózgów i kodowania, w końcu opracowałem rozwiązanie. Zasadniczo zrobiłem 2 formularze.

Postać 1: Tworzenie dropshadow modyfikując i łączenia 8 obrazów (4 rogach gradienty + 4 gradienty liniowe dla każdego kierunku ruchu) i ustawić je w tle za pomocą drugiego kodu I zamieszczonych powyżej (C# 2 Kod: Zastąp formularz GDI bitmapą). Kod prawie to wyjaśnia.

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); 
    } 
} 

Formularz nr 2: To właśnie uruchamia formularz dropshadow na rozpoczęcie i stworzyłem także kilka interfejsów, aby umożliwić dalszą integrację i elastyczność, które pominąłem, aby uniknąć dodatkowych nieporozumień. Zasadniczo metody zapewniające, że formularz Dropshadow nie zabierał kliknięć myszy z aktywnej postaci i nie zmuszałby użytkownika do dwukrotnego kliknięcia przycisku, jeśli formularz Dropshadow był na wierzchu.

+1

Nie wiem, czy to najlepszy sposób na zrobienie tego, ale myślę, że to całkiem sprytne. –

3

Dzięki, Corylulu.

Robocza klasa to here.

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

f.RefreshShadow(); 

DEMO

DrawShadow stworzyć cień jak bitmapy, ale jeszcze nie jest doskonały. Ta klasa nie jest idealna, ale działa.

BTW, nie wiem, jak ukryć formularz cieni na pasku zadań. Ustaw ShowInTaskBar = false spowoduje, że formularz zniknie.

EDIT

ja przepisać klasę, teraz wygląda to tak, prawdziwe dropshadow.

źródłowy jest here.

Jedną rzeczą, którą powinieneś wiedzieć jest ta klasa, nie biorąc pod uwagę border-radius (przyjmuje formę css).

nieruchomość Main jest

  • shadowColor
  • ShadowV
  • ShadowH
  • ShadowSpread
  • ShadowBlur

Obiekt jest taka sama jak css box-shadow patrz here

Te propertyies

  • ShadowSpread
  • ShadowBlur
  • shadowColor

wymagają ręcznego połączenia RefreshShadow().

Przejdź do demo project

+0

To interesujące zagadnienie dotyczące ShowInTaskbar. Wydaje mi się, że nie mam tego samego problemu. Wyślę kod, który mam i wyślę go, jeśli chcesz. Jest to bardzo stary kod, więc nie jest dokładnie zoptymalizowany. – Corylulu

+0

Moja wersja staje się trochę bardziej zaangażowana, i nie pamiętam dokładnie dlaczego: P, ale tutaj jest wszystko, co zrobiłem, aby kod działał dobrze. Niektóre z nich mogą być specyficzne dla tego, co próbowałem zrobić, ale w większości próbowałem to wszystko usunąć. http://pastie.org/8588447 – Corylulu

+0

Tworzę nowy projekt, aby przetestować cień, dowiedzieć się, że 'ShowInTaskBar' jest w porządku. Teraz trudną częścią jest narysowanie bitmapy cieni. Chciałbym użyć parametru opisanego [tutaj] (http://www.w3schools.com/cssref/css3_pr_box-shadow.asp) .Zauważyłem, że rysujesz cień użyj obrazu, który nie jest zmienny. – wener