Acg N. .
Temat: "Fader tła dialogu" - może się przyda.
Czasem zdarza mi się pisać aplikację, która ma sporo pól i kontrolek w głównym oknie. Nawet, jeśli jest ono czytelne, to gdy wyświetla się "nad tym" jakiś dialog (lepiej - kilka dialogów), to "mogą się pomieszać oczy", dialog zlewa się z tłem.Swego czasu, jeszcze za czasów programowania w MFC, natknąłem się na implementację znanego z Windows XP "wygaszania tła", gdy pojawia się dialog z opcjami dotyczącymi zamknięcia systemu. Przez kilka lat nie korzystałem, ale teraz wróciłem do tej koncepcji i bardzo ją sobie chwalę. Bardzo się podobała klientom (i szefowi :) ), ponieważ pozwala bardziej skupić uwagę na zawartości okna no i ciekawie wygląda. Jednocześnie narzut kodu do uzyskania tego efektu jest minimalny.
Nie optymalizowane graficznie, dlatego dla okien zmaksymalizowanych (1280x1024)
chwilę (pół sekundy?) to potrwa. Nie miga (double buffered), ale widoczne jest minimalne opóźnienie przy wyszarzaniu, co jednak zawsze można zgonić na odwołania do DB ;)
Zamieszczam przykładowy kod, który to robi do poeksperymentowania. A nuż się komuś z Was przyda.
namespace xxxxxx.Utils
{
public partial class BackgroundFader : Form
{
private BackgroundFader()
{
InitializeComponent();
}
Form myParent;
public BackgroundFader(Form parent)
{
InitializeComponent();
this.myParent = parent;
}
private void BackgroundFader_Load(object sender, EventArgs e)
{
this.Location = this.myParent.Location;
this.Size = this.myParent.Size;
this.BackgroundImage = new Bitmap(this.Width, this.Height);
using (Bitmap bmpScreenShot = new Bitmap(this.Width, this.Height))
{
this.myParent.DrawToBitmap(bmpScreenShot, new Rectangle(0, 0, this.Width, this.Height));
//UnmanagedFunctions.ImageProcessing.GrayScaleImage(bmp);
System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix(new float[][]{
new float[]{0.3f,0.3f,0.3f,0,0},
new float[]{0.59f,0.59f,0.59f,0,0},
new float[]{0.11f,0.11f,0.11f,0,0},
new float[]{0,0,0,1,0,0},
new float[]{0,0,0,0,1,0},
new float[]{0,0,0,0,0,1}});
using (System.Drawing.Graphics g = Graphics.FromImage(this.BackgroundImage))
{
System.Drawing.Imaging.ImageAttributes ia = new System.Drawing.Imaging.ImageAttributes();
ia.SetColorMatrix(cm);
g.DrawImage(bmpScreenShot, new Rectangle(0, 0, bmpScreenShot.Width, bmpScreenShot.Height), 0, 0, bmpScreenShot.Width, bmpScreenShot.Height, GraphicsUnit.Pixel, ia);
}
}
}
}
}
Atrybuty okna:
* ShowInTaskbar = false
* ControlBox = false
* Text = "" (String.Empty)
* FormBorderStyle = none (inaczej bitmapa będzie przesunięta o kilka pikseli w dół i w prawo)
* DoubleBuffered = true
Okno można by tworzyć gdzieś globalnie i trzymać, a jedynie zmieniać mu rozmiar i rysować na nim bitmapy różnych okien, dzięki czemu nie tworzymy go za każdym razem.
Przykład wykorzystania (z MVC#):
W widoku: (czyli formatce)
[WinformsView(typeof(RegisterPatientRequest_Task), RegisterPatientRequest_Task.wndRegisterPatientRequest_View, ShowModal=true)]
public partial class wndRegisterPatientRequest_View : WinFormViewForRegisterPatientRequestController, IView_RegisterPatientRequest
{
//------------------------ POCZĄTEK ------------------------------------
Utils.BackgroundFader bkgFader;
public void GrayMe(bool gray)
{
if (gray)
{
this.bkgFader = new xxxxx.Utils.BackgroundFader(this);
bkgFader.Show();
}
else
{
this.bkgFader.Close();
}
}
//------------------------ KONIEC ------------------------------------
...........
I to już cały kod w przypadku prostej aplikacji formsowej. Trza danemu oknu po prostu wywołać taką metodę.
Można w dodatku zrobić z niej extension method i nie martwić się przekazywaniem referencji do bieżącego okna.
W MVC# dodatkowo jeszcze:
W prezenterze/kontrolerze (plus dodać def. metody w interfejsie widoku)
public void GrayMe(bool gray)
{
View.GrayMe(gray);
}
I potem w prezenterze dialogu, który ma nam "wyskoczyć" ponad powyższy widok (np. w prezenterze dialogu "Dodaj rezerwację", który ma wyskoczyć ponad widok "Lista rezerwacji"):
// mówimy prezenterowi określonego widoku, żeby wywołał // jego metodę, która pobierze tło widoku, sprowadzi do odcieni szarości,
// a następnie wyświetli okno o rozmiarach tego widoku i przekopiuje na nie wyszarzoną bitmapę
RegisterPatientRequest_Controller ctrl = ((RegisterPatientRequest_Controller)Task.Navigator.GetController(RegisterPatientRequest_Task.wndRegisterPatientRequest_View));
ctrl.GrayMe(true); // wyszarzamy
......
// wyświetlamy nasz dialog
Task.Navigator.Navigate(RegisterPatientRequest_Task.wndAddEditReservation);
// mówimy prezenterowi w/w widoku, żeby wywołał jego metodę, która zamknie okno z wyszarzoną bitmapą tła
ctrl.GrayMe(false);
Można to oprzeć maszynę stanów (yield true/false) i mieć nieparametryzowaną metodę GrayMe.
Efekt:
Nie rysują mi się na bitmapie 2 kontrolki. Podejrzewam, że ma to związek z z-orderem, chociaż obie są zadokowane i "Bring to front" ani "Send to back" nie działa :o Ale kto będzie na to zwracał uwagę... :]
Oryginalne okno do wyszarzenia:
I z wyszarzeniem:
Zadanie na spostrzegawczość - którego headera (to te paski z tytułami widoków) i jakiego komponentu jeszcze brakuje? ;)
I kilka okien na sobie:
Swoją drogą, to może macie jakiś lepszy sposób na to, bo ten jest bardzo "dosłowny" :)Adrian Olszewski edytował(a) ten post dnia 08.08.10 o godzinie 19:18