myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
   » Plugin für Firefox
   » Plugin für IE
   » Gadget für Windows
» Regeln
» Wie poste ich richtig?
» Datenschutzerklärung
» wbb-FAQ

Mitglieder
» Liste / Suche
» Stadt / Anleitung dazu
» Wer ist wo online?

Angebote
» ASP.NET Webspace
» Bücher
» Zeitschriften
   » dot.net magazin
» Accessoires

Ressourcen
» .NET-Glossar
» guide to C#
» openbook: Visual C#
» openbook: OO
» .NET BlogBook
» MSDN Webcasts
» Search.Net

Team
» Kontakt
» Übersicht
» Wir über uns
» Bankverbindung
» Impressum

» Unsere MiniCity
MiniCity
» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Knowledge Base » Artikel » [Artikel] Custom Window Border für Form's
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | An Freund senden | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

[Artikel] Custom Window Border für Form's

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
egrath egrath ist männlich
myCSharp.de-Poweruser/ Experte

images/avatars/avatar-2119.jpg


Dabei seit: 24.07.2005
Beiträge: 871
Entwicklungsumgebung: MonoDevelop, NetBeans, Vi
Herkunft: Österreich / Steyr


egrath ist offline

[Artikel] Custom Window Border für Form's

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Eigene Window Rahmen zeichnen

Dieser Artikel beschreibt wie man in den Non-Client Bereich einer Form zeichnet um beispielsweise das aussehen des Rahmens und der Buttons so zu verändern dass diese einen eigenen Look and Feel erhalten.

Grundlegendes:

Ein Windows Fenster besteht sobald eine Titelleiste vorhanden ist immer aus zwei Bereichen:
  • Dem Client Bereich (Client Area)
  • Dem Non-Client Bereich (Non-Client Area)
Untenstehende Grafik zeigt wo sich diese Bereiche befinden:



Im Normalfall wird eine Applikation nur in die CA zeichnen, die NCA wird vom Framework (genauer gesagt eigentlich von der darunter liegenden Win32-API) gezeichnet. Um dies selbst zu übernehmen müssen wir verschiedene Windows Messages bearbeiten. So kann man beispielsweise Fenster erzeugen die folgendermassen aussehen:



Implementierung

Das Zeichnen in die NCA wird durch verschiedene Windows Messages initiiert. Diese sind:

Code:
1:
2:
3:
4:
5:
6:
7:
Message  Name    	Beschreibung
WM_NCPAINT              Der NCA muss neu gezeichnet werden
WM_NCACTIVATE           Das Window wurde aktiviert/deaktiviert. Der Rahmen muss 
                        dementsprechend gezeichnet werden (bsp. Rahmen abdunkeln)
WM_ACTIVATE             s.o.
WM_NCLBUTTONDOWN        Die linke Maustaste wurde in der NCA gedrückt
WM_NCCALCSIZE           Die grösse der NCA muss neu berechnet werden da es eine Grössenänderung gab

Der erste Schritt besteht darin, statische Konstanten zu definieren welche die numerischen Windows Messages in sprechenden Namen abbilden. Anschliessend überschreiben wir die Methode "WndProc" und bearbeiten die folgenden Messages:

WM_NCPAINT:

Wir müssen den gesamten NCA neu zeichnen. Dazu erstellen wir uns eine entsprechende Methode welche sich mittels der nativen API Funktion "GetWindowDC" einen Handle auf den Drawing Context des Fensters holt und anschliessend ein Graphics Objekt für diesen erstellt. In dieses Graphics Objekt zeichnen wir anschliessend den NCA. Ebenso müssen wir alle benötigten Steuerelemente welche im NCA erscheinen sollen (in unserem Beispiel nur Minimize/Maximize und Close). zeichnen.

WM_ACTIVATE und WM_NCACTIVATE:

Wenn diese Messages empfangen werden, so senden wir eine neue Message welche WM_NCPAINT antriggert.

WM_NCLBUTTONDOWN:

Wir müssen überprüfen ob sich die Maus über einem der Steuerelemente befindet und entsprechend darauf reagieren. Wird auf den Close-Button geklickt so senden wir eine neue Message WM_CLOSE welche die Applikation dazu veranlasst sich zu beenden. Für Minimize und Maximize rufen wir die entsprechenden Methoden des Frameworks auf.

WM_NCCALCSIZE:

Wir müssen die Grösse der NCA neu berechnen. Dies gestaltet sich relativ einfach, da wir als Message Parameter einen Zeiger auf eine native Struktur vom Typ NCCALCSIZE_PARAMS übergeben bekommen die wir dafür benutzen können.

Native Calls und andere besonderheiten:

Wir benötigen zwei native Win32-API Funktionen um die Funktionalität implementieren zu können:
  • GetWindowDC - liefert einen Handle auf den Drawing Context des angegebenen Fensters zurück
  • SendMessage - eine neue Message in den Message Loop schicken
Weitere Dinge die beachtet werden müssen:
  • Wir müssen die nativen Icons der Titlebar deaktivieren damit diese uns nicht in die Quere kommen. Dies geschieht durch "ControlBox = false" in der initialisierung des Fensters
  • Die nativen Win32 Funktionen geben oftmals ein DWORD zurück welches im Low-Word und High-Word jeweils einen Parameter enthalten. Dafür müssen wir einen entsprechende Methode schreiben welche diese als einzelne Paramter zurückgibt (HighWord und LowWord im Source)


Dateianhang:
zip borderoverride.zip (11 KB, 2.404 mal heruntergeladen)
02.11.2006 22:38 Beiträge des Benutzers | zu Buddylist hinzufügen
Andreas.May Andreas.May ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2474.gif


Dabei seit: 07.09.2006
Beiträge: 915


Andreas.May ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Prima Beitrag Daumen hoch

Hätte mir vor einigen Wochen eine menge Arbeit erspaart smile

C#-Code:
#region NCHITTEST enum
    /// <summary>
    /// Location of cursor hot spot returnet in WM_NCHITTEST.
    /// </summary>
    public enum NCHITTEST
    {
        /// <summary>
        /// On the screen background or on a dividing line between windows
        /// (same as HTNOWHERE, except that the DefWindowProc function produces a system beep to indicate an error).
        /// </summary>
        HTERROR = (-2),
        /// <summary>
        /// In a window currently covered by another window in the same thread
        /// (the message will be sent to underlying windows in the same thread until one of them returns a code that is not HTTRANSPARENT).
        /// </summary>
        HTTRANSPARENT = (-1),
        /// <summary>
        /// On the screen background or on a dividing line between windows.
        /// </summary>
        HTNOWHERE = 0,
        /// <summary>In a client area.</summary>
        HTCLIENT = 1,
        /// <summary>In a title bar.</summary>
        HTCAPTION = 2,
        /// <summary>In a window menu or in a Close button in a child window.</summary>
        HTSYSMENU = 3,
        /// <summary>In a size box (same as HTSIZE).</summary>
        HTGROWBOX = 4,
        /// <summary>In a menu.</summary>
        HTMENU = 5,
        /// <summary>In a horizontal scroll bar.</summary>
        HTHSCROLL = 6,
        /// <summary>In the vertical scroll bar.</summary>
        HTVSCROLL = 7,
        /// <summary>In a Minimize button.</summary>
        HTMINBUTTON = 8,
        /// <summary>In a Maximize button.</summary>
        HTMAXBUTTON = 9,
        /// <summary>In the left border of a resizable window
        /// (the user can click the mouse to resize the window horizontally).</summary>
        HTLEFT = 10,
        /// <summary>
        /// In the right border of a resizable window
        /// (the user can click the mouse to resize the window horizontally).
        /// </summary>
        HTRIGHT = 11,
        /// <summary>In the upper-horizontal border of a window.</summary>
        HTTOP = 12,
        /// <summary>In the upper-left corner of a window border.</summary>
        HTTOPLEFT = 13,
        /// <summary>In the upper-right corner of a window border.</summary>
        HTTOPRIGHT = 14,
        /// <summary>    In the lower-horizontal border of a resizable window
        /// (the user can click the mouse to resize the window vertically).</summary>
        HTBOTTOM = 15,
        /// <summary>In the lower-left corner of a border of a resizable window
        /// (the user can click the mouse to resize the window diagonally).</summary>
        HTBOTTOMLEFT = 16,
        /// <summary>    In the lower-right corner of a border of a resizable window
        /// (the user can click the mouse to resize the window diagonally).</summary>
        HTBOTTOMRIGHT = 17,
        /// <summary>In the border of a window that does not have a sizing border.</summary>
        HTBORDER = 18,

        HTOBJECT = 19,
        /// <summary>In a Close button.</summary>
        HTCLOSE = 20,
        /// <summary>In a Help button.</summary>
        HTHELP = 21,
    }

Was man noch ergänzen könnte ist das man beim eigenen gezeichneten "Icon" noch NCHITTEST.HTMENU über SendMessage mitgeben kann um die Systemmenü Eigenschaften zu realisieren.


Wer noch das Systemmenu etwas erweitern möchte kann das hiermit:

Klasse:

C#-Code:
public class CSystemMenu
    {

        [DllImport("USER32", EntryPoint = "GetSystemMenu", SetLastError = true,
                   CharSet = CharSet.Unicode, ExactSpelling = true,
                   CallingConvention = CallingConvention.Winapi)]
        private static extern IntPtr apiGetSystemMenu(IntPtr WindowHandle,
                                                      int bReset);

        [DllImport("USER32", EntryPoint = "AppendMenuW", SetLastError = true,
                   CharSet = CharSet.Unicode, ExactSpelling = true,
                   CallingConvention = CallingConvention.Winapi)]
        private static extern int apiAppendMenu(IntPtr MenuHandle, int Flags,
                                                 int NewID, String Item);

        [DllImport("USER32", EntryPoint = "InsertMenuW", SetLastError = true,
                   CharSet = CharSet.Unicode, ExactSpelling = true,
                   CallingConvention = CallingConvention.Winapi)]
        private static extern int apiInsertMenu(IntPtr hMenu, int Position,
                                                  int Flags, int NewId,
                                                  String Item);

        // Remove menu
        [DllImport("user32.dll")]
        private static extern bool RemoveMenu(IntPtr hMenu, int uPosition, int uFlags);

        public CSystemMenu()
        {
        }



        private IntPtr m_SysMenu = IntPtr.Zero;    // Handle to the System Menu

        public CSystemMenu()
        {
        }

        /// <summary>
        /// Remove an menu entry
        /// </summary>
        /// <param name="Pos"></param>
        /// <param name="Flags"></param>
        /// <returns></returns>
        public bool RemoveMenu(int Pos, ItemFlags Flag)
        {
            return RemoveMenu(m_SysMenu, Pos, (int) Flag);
        }

        public bool InsertSeparator(int Pos)
        {
            return (InsertMenu(Pos, ItemFlags.mfSeparator |
                                ItemFlags.mfByPosition, 0, ""));
        }


        public bool InsertMenu(int Pos, int ID, String Item)
        {
            return (InsertMenu(Pos, ItemFlags.mfByPosition |
                                ItemFlags.mfString, ID, Item));
        }

        public bool InsertMenu(int Pos, ItemFlags Flags, int ID, String Item)
        {
            return (apiInsertMenu(m_SysMenu, Pos, (Int32)Flags, ID, Item) == 0);
        }


        public bool AppendSeparator()
        {
            return AppendMenu(0, "", ItemFlags.mfSeparator);
        }


        public bool AppendMenu(int ID, String Item)
        {
            return AppendMenu(ID, Item, ItemFlags.mfString);
        }

        public bool AppendMenu(int ID, String Item, ItemFlags Flags)
        {
            return (apiAppendMenu(m_SysMenu, (int)Flags, ID, Item) == 0);
        }


        public static CSystemMenu FromForm(Form Frm)
        {
            CSystemMenu cSysMenu = new CSystemMenu();

            cSysMenu.m_SysMenu = apiGetSystemMenu(Frm.Handle, 0);
            if (cSysMenu.m_SysMenu == IntPtr.Zero)
            { // Throw an exception on failure
                throw new NoSystemMenuException();
            }

            return cSysMenu.;
        }

        public static void ResetSystemMenu(Form Frm)
        {
            apiGetSystemMenu(Frm.Handle, 1);
        }

        public static bool VerifyItemID(int ID)
        {
            return (bool)(ID < 0xF000 && ID > 0);
        }
    }

Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von Andreas.May am 21.11.2006 21:22.

05.11.2006 12:44 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Andreas.May Andreas.May ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2474.gif


Dabei seit: 07.09.2006
Beiträge: 915


Andreas.May ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Das einzige was man dazu ergänzen muss, passt gut auf bei MDI Childs dort muss man etwas verändern bei PointToScreen.

Was in deinem Code noch fehlt, ist das verhinden von Windows Themes welches man mit folgenden Codeschnipsel verhindert.

C#-Code:
        [DllImport("uxtheme.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern int SetWindowTheme(IntPtr hwnd, String pszSubAppName,
                                         String pszSubIdList);

Einfach beim entsprechenden Formular die OnHandleCreated Methode überschreiben bzw. ergänzen um die Windows Themes zu verhinden.

C#-Code:
        protected override void OnHandleCreated(EventArgs e)
        {
            SetWindowTheme(this.Handle, "", "");
            base.OnHandleCreated(e);
        }
05.11.2006 13:06 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Andreas.May Andreas.May ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2474.gif


Dabei seit: 07.09.2006
Beiträge: 915


Andreas.May ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hier mal ein paar Bilder dazu.

Hier erkennt man das es sich um keinen typischen WindowsBorder handelt und auch die Breite nicht dem eines WindowsBorder entspricht.



Und hier das gleiche als Überblick:

Andreas.May hat dieses Bild (verkleinerte Version) angehängt:
overview.jpg
Volle Bildgröße

05.11.2006 13:34 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
egrath egrath ist männlich
myCSharp.de-Poweruser/ Experte

images/avatars/avatar-2119.jpg


Dabei seit: 24.07.2005
Beiträge: 871
Entwicklungsumgebung: MonoDevelop, NetBeans, Vi
Herkunft: Österreich / Steyr

Themenstarter Thema begonnen von egrath

egrath ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Andreas.May,

Danke für die Ergänzung des Artikels!

Grüsse, Egon
05.11.2006 16:31 Beiträge des Benutzers | zu Buddylist hinzufügen
Andreas999 Andreas999 ist männlich
myCSharp.de-Mitglied

Dabei seit: 12.11.2006
Beiträge: 3


Andreas999 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,
ausgehend von borderoverride.zip möchte ich dieses Fenster als MDI Container laufen lassen und darin Fenster dieses Typs nutzen. Wie würde das gehen? Bei mir bricht da immer Visual Studio ab:

C#-Code:
private void MainWindow_Load(object sender, EventArgs e)
        {

            Form2 oForm = new Form2();
            oForm.MdiParent = this;
            oForm.Show();
        }

.

Form2 ist ein vom MainWindow geerbtes Windows Form und die Eigenschaft isMdiContainer wurde bei MainWindow auf true und bei oForm auf false gesetzt.

Beim Maximieren und Minimieren erscheint bei mir in Win XP prof. in einer Animation dieses Fenster mit der ursprünglichen blauen Leiste und hat danach erst wieder die dunkelgraue Farbe. Wie kann ich diese Animation abschalten oder dafür sorgen, daß auch während dieses Vorgangs die Leiste in der richtigen Farbe erscheint?

Alles Gute!
Andreas

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Andreas999 am 12.11.2006 03:12.

12.11.2006 03:10 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Fabian Fabian ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-1590.jpg


Dabei seit: 09.12.2004
Beiträge: 1.979
Entwicklungsumgebung: Visual Studio 2010
Herkunft: Dortmund


Fabian ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo egrath,

und mal wieder ein sehr guter Artikel von Dir!


Gruß,
Fabian
12.11.2006 11:22 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Andreas.May Andreas.May ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2474.gif


Dabei seit: 07.09.2006
Beiträge: 915


Andreas.May ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Andreas,
denk dran Windows Themes zu verhindern, der Code dazu steht etwas weiter oben smile
21.11.2006 21:03 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 5 Monate.
dr4g0n76
myCSharp.de-Poweruser/ Experte

images/avatars/avatar-1768.jpg


Dabei seit: 07.07.2005
Beiträge: 2.754
Entwicklungsumgebung: SharpDevelop/VS.NET
Herkunft: Deutschland


dr4g0n76 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Schön, dass hier von Andreas mein Fenster gepostet wurde, lol.

Dann ist es ja auch nicht schlecht hier den Code dazu zu posten:

EDIT: Achtung, dazu muss in MDI-Windows noch etwas Code für das Bewegen dieser Art von Fenstern programmiert werden...

Es empfiehlt sich auch eigene Forms von LonghornForm abzuleiten und dann meine SkinLib zu benutzen.


Dateianhang:
rar CustomBorderForm.rar (960 KB, 1.429 mal heruntergeladen)

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von dr4g0n76 am 28.04.2007 10:55.

28.04.2007 10:54 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
DarKlajid DarKlajid ist männlich
myCSharp.de-Mitglied

Dabei seit: 25.01.2007
Beiträge: 386
Herkunft: Köln


DarKlajid ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Mahlzeit.

Ich find das Thema hochinteressant, hab aber mit beiden hier geposteten Beispielen noch ein Problem:




[EDIT=herbivore]Die Bilder leider nicht mehr verfügbar. Daher Bilder statt sie über [IMG] einzubinden bitte immer als Dateianhang hochladen. Vielen Dank![/EDIT]

Warum erscheinen dort dann doch manchmal die Originalbuttons von Windows?
Beim ersten Beispiel ist das trivial zu demonstrieren, es passiert bei mir immer wenn ich zum ersten Mal mit der Maus ueber diese Region fahre.
Beim zweiten Beispiel ist es schwieriger zu triggern, scheint erst nach Aktivierung/Deaktivierung von Buttons (Beispiel: Maximieren) zu passieren, ebenfalls dann sobald man mit der Maus ueber diesen Bereich faehrt.

Ideen, wieso das passiert und wie man das unterdrueckt?
06.05.2007 16:28 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
dr4g0n76
myCSharp.de-Poweruser/ Experte

images/avatars/avatar-1768.jpg


Dabei seit: 07.07.2005
Beiträge: 2.754
Entwicklungsumgebung: SharpDevelop/VS.NET
Herkunft: Deutschland


dr4g0n76 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Das passiert, weil in gewisser Weise doch nur darüber gezeichnet wird, aber nur für den Fall das bestimmte Windows-Nachrichten nicht korrekt über die API abgefangen werden, d.h. ganz einfach mein Beispiel wird ja dann wohl noch Fehler enthalten.
05.06.2007 14:07 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Kalleberlin Kalleberlin ist männlich
myCSharp.de-Mitglied

Dabei seit: 11.06.2007
Beiträge: 165


Kalleberlin ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo smile

Das alles klingt wirklich sehr interessant, gerade deshalb weil ich mich in dieses Thema einarbeiten möchte.

Nun aber mal meine Frage:

Woher zum Teufel, wisst Ihr z.B. diese Parameter :

C#-Code:
        private const int WM_NCPAINT = 0x0085;
        private const int WM_NCLBUTTONDOWN = 0x00A1;
        private const int WM_NCLBUTTONUP = 0x00A2;
        private const int WM_NCHITTEST = 0x0084;
        private const int WM_NCCALCSIZE = 0x0083;
        private const int WM_NCACTIVATE = 0x0086;
        private const int WM_CLOSE = 0x0010;

Gibt es irgendwo in den weiten unseres Netzes, gute (kann auch englisch sein) Tutorials die einem diese Art der Programmierung näher bringt?

Gruß
Kalleberlin
25.06.2007 12:31 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
nin nin ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2166.gif


Dabei seit: 19.01.2007
Beiträge: 738
Entwicklungsumgebung: c#, VS2005 Pro
Herkunft: graz,AT


nin ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

25.06.2007 12:39 Beiträge des Benutzers | zu Buddylist hinzufügen
egrath egrath ist männlich
myCSharp.de-Poweruser/ Experte

images/avatars/avatar-2119.jpg


Dabei seit: 24.07.2005
Beiträge: 871
Entwicklungsumgebung: MonoDevelop, NetBeans, Vi
Herkunft: Österreich / Steyr

Themenstarter Thema begonnen von egrath

egrath ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Original von Kalleberlin
Woher zum Teufel, wisst Ihr z.B. diese Parameter :

C#-Code:
        private const int WM_NCPAINT = 0x0085;
        private const int WM_NCLBUTTONDOWN = 0x00A1;
        private const int WM_NCLBUTTONUP = 0x00A2;
        private const int WM_NCHITTEST = 0x0084;
        private const int WM_NCCALCSIZE = 0x0083;
        private const int WM_NCACTIVATE = 0x0086;
        private const int WM_CLOSE = 0x0010;

Die meisten dieser Konstanten kann man im Normalfall nachlesen indem man zuerst die Win32 API Dokumentation (MSDN) nachliest und sich anschliessend die Werte der Konstanten aus den entsprechenden C/C++ Header Dateien des Platform SDK's raussucht (wird beides installiert wenn man eine voll Visual Studio installation hat)

Grüsse,
Egon
17.07.2007 22:29 Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 3 Monate.
CGE500
myCSharp.de-Mitglied

Dabei seit: 21.10.2007
Beiträge: 7


CGE500 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Erstmal möchte ich mich für dieses Artikel wirklich bedanken! Der hat mich sehr viel weitergebracht.

Allerding scheint der Code für Windows Vista nicht mehr richtig zu funktionieren. Als ich das Projekt zuerst Debugged habe, fiel mir auf, dass zwischen der Titlebar und dem Client-Bereich ein Teil war, wo nichts gezeichnet war. Der Grund war "borderWidth" welches zu klein berechnet wurde.
Dann fiel mir aber ein, dass ich ja den Aero-Style (Theme) aktiviert habe und baute das Snippet von Andreas ein, jetzt wird alles schön gezeichnet.
Jedoch, wenn ich das Fenster verkleiner/vergrößer treten unschöne Fehler auf (Bildfetzen von darunterliegenden Fenstern werden in dieses gezeichnet), und wenn ich das Fenster maximiere, wird das gesamte Layout des Fensters zerstört, dass heißt die Buttons werden, wenn überhaupt, irgendwo gezeichnet, und die Titlebar überlappt sich mit dem Client-Bereich.

Wisst ihr an was das vielleicht liegen könnte? Oder hat sich in Vista in diesem Bereich so viel geändert, dass man da alles neu schreiben müsste?
Mein Problem ist nur, dass ich zurzeit keinen Rechner mit XP zur Verfügung habe, mit welchem ich das ganze paralell testen könnte.

lg
CGE

PS: Wenn Screenshots von den Fehlern erwünscht sind, kann ich gern welche posten!
01.11.2007 00:21 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Team (Admin)

images/avatars/avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 48.929
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo CGE500,

wenn jemand eine definitive Antwort weiß, ist es ja gut, aber bitte in einem Artikel-Thread nur Fragen und Hinweise zum Artikel; keine Diskussionen und keine Problemlösungsversuche zu Problemen in eigenen Anwendungen. Vor allem keine langen Diskussionen. Eine Frage, eine Antwort, mehr nicht. Mach bei Bedarf einen eigenen Thread auf.

Bitte auch diesen Hinweis nicht diskutieren, sondern hinnehmen und beachten. Vielen Dank!

herbivore
01.11.2007 07:12 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
TheProgrammer TheProgrammer ist männlich
myCSharp.de-Mitglied

Dabei seit: 28.10.2007
Beiträge: 3
Entwicklungsumgebung: Visual Studio C# 2005 Express


TheProgrammer ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Erstmal vielen dank für den schönen Artikel ;-)

Zitat:
Woher zum Teufel, wisst Ihr z.B. diese Parameter :C#-Code:
private const int WM_NCPAINT = 0x0085;
private const int WM_NCLBUTTONDOWN = 0x00A1;
private const int WM_NCLBUTTONUP = 0x00A2;
private const int WM_NCHITTEST = 0x0084;
private const int WM_NCCALCSIZE = 0x0083;
private const int WM_NCACTIVATE = 0x0086;
private const int WM_CLOSE = 0x0010;

Das liegt daran, dass viele vermutlich vor ihrer C# Zeit wie ich C++ oder so programmiert haben und teilweise wohl immer noch programmieren und dort Kontakt zur Win32API hatten/haben.
07.11.2007 15:09 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 4 Monate.
Andreas.May Andreas.May ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2474.gif


Dabei seit: 07.09.2006
Beiträge: 915


Andreas.May ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hier eine Lösung um das Flackern zu unterbinden:

C#-Code:
private void PaintNonClientArea(IntPtr hWnd, IntPtr hRgn)
        {
            CWin32.RECT rWnd = new CWin32.RECT();
            if (CWin32.GetWindowRect(hWnd, ref rWnd) == 0)
                return;

            Rectangle rClip = new Rectangle(0, 0, rWnd.Width, rWnd.Height);
            IntPtr hDC = CWin32.GetWindowDC(hWnd);

            IntPtr hCDC = CWin32.CreateCompatibleDC(hDC);
            IntPtr hBitmap = CWin32.CreateCompatibleBitmap(hDC, rClip.Width, rClip.Height);
            CWin32.SelectObject(hCDC, hBitmap);
            CWin32.BitBlt(hCDC, 0, 0, rClip.Width, rClip.Height, hDC, 0, 0, (uint)CWin32.TernaryRasterOperations.SRCCOPY);

            using (Graphics g = Graphics.FromHdc(hCDC))
            {
                this.OnNonClientAreaPaint(new PaintEventArgs(g, rClip));
            }

            CWin32.BitBlt(hDC, 0, 0, rClip.Width, rClip.Height, hCDC, 0, 0, (uint)CWin32.TernaryRasterOperations.SRCCOPY);
            CWin32.DeleteObject(hBitmap);
            CWin32.DeleteDC(hCDC);
            CWin32.ReleaseDC(hWnd, hDC);
        }
26.03.2008 15:31 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Xx tja xX Xx tja xX ist männlich
myCSharp.de-Mitglied

Dabei seit: 12.04.2008
Beiträge: 38
Entwicklungsumgebung: Visual C# 2005 Express Edition
Herkunft: Deutschland Sachsen Anhalt Landkreis Anhalt Bitterfeld Tornau vor der Heide


Xx tja xX ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also eigentlich kann man das doch auch über eine etwas kürzere variante machen nämlich man setzt als BAckground image einen Rahmen den man lebst editiert hat
sollte mit Möglichkeit eine bessere auflösung haben und gibt Stretch an für das Background image verhalten an dann setzt man ein Panel zur sicherheit damit man auch schön beim klicken auf den oberen rand eine Dropdown menu bekommt wie beim normalen NCA setzt darauf dann Pictureboxen die dann beim Enter-/LeaveEvent und beim klicken eine andere Optik annehemen und schreibt dann noch im code was beim klicken auf die Pictureboxen passieren soll
an den seiten und unten macht man dann auch ein panel auf der sichtbaren Fläche des Rahmens und legt dann noch bei den rändern fest das die zieh und Schiebe Mouse kommt und dass die ganze sache sich auf ihren Locations bzw Sizes ändert jenbach schub oder ZUg

Das wars. Zunge raus smile fröhlich geschockt großes Grinsen Teufel
12.04.2008 22:37 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Andreas.May Andreas.May ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2474.gif


Dabei seit: 07.09.2006
Beiträge: 915


Andreas.May ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hier noch eine kleine ergänzung:

Die Buttonpossitionen wie Close / Minimize / Maximize / Help müssen nicht unbedingt ausgerechnet werden sollte man diese beibehalten wollen. Hier reicht ein NCHITTEST.HTMENU als rückgabewert.

Und hier noch ein  Link zum SystemMenü.
14.04.2008 13:38 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Andreas.May Andreas.May ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-2474.gif


Dabei seit: 07.09.2006
Beiträge: 915


Andreas.May ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

[Noch nicht getestet daher pure Theorie]

Ein weiterer Tipp fürs "Globale" Design auf mehrere Controls wie ComboBoxen, ListViews, Forms usw.

Wenn man vor hat ein Skinning Tool für alle Controls zu schreiben, so kann man eine Klasse erstellen, diese von System.ComponentModel.Component ableitenund das Interface System.ComponentModel.IExtenderProvider einbinden (siehe hierzu die deutsche MSDN, ist etwas besser), Anhand der IExtenderProvider.CanExtend kann man die verschiedenen Typen unterteilen.
Anhand des Attributes [System.ComponentModel.ProvideProperty(„SubClassing“), typeof(bool))] kann man eine Set und Get Methode einbinden in dem Fall wäre das hier SetSubClassing(Control control, bool value); und GetSubclassing(Control control);. Anhand des boolischen Wertes kann man nun festlegen ob ein Subclassign vorgenommen werden soll oder nicht. Das Subclassing kann man einfach über einbinden des Interface System.Windows.Forms.IMessageFilter erstellen und via Application.AddMessageFilter(new SkiningClass()); realisieren. Bei eingehenden Nachrichten filtert man dann diese anhand des Control.Handel und beachtet dann nur die üblichen wichtigen Nachrichten (siehe dazu vorherige Posts). Um nun die Skins den jeweiligen Controls zuziweisen macht es Sinn einen Assembly Loader zu schreiben in der die jeweiligen resourcen für die verschiedenen Controls bereitgestellt werden. Anhand eines Tools könnte man diese Assemblys erstellen lassen (SkiningTool) und diese einbinden.

Kostet zwar etwas arbeit aber man könnte somit auf Fremdanbieter-Programme verzichten und seine eigene Library aufbauen mit modernen Look and Feel für Windows Komponenten.

[Edit] Vergessen zu erklären warum IMessageFilter ;-)
Da man ja vorhat ein "globales" Skinning Tool zu haben das alle Cntrols den selben Style besitzen. Ist es natürlich vorteilhaft wenn alle Controls sich zur Laufzeit dem gewählten Style anpassen. Somit müssen alle Nachrichten gleichzeitig verarbeitet werden. Da kommt die Application ins spiel und die damit verbundenen globalen Windows Nachrichten.

Natürlich sollten unterschiedliche GUI Threads exestieren (warum auch immer) würden diese voneinander im Style untershciedlich sein. Sollte man unterschiedliche Styles für jedes einzelne Control wünschen so kann man das Klassische SubClassing verwenden.

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Andreas.May am 16.04.2008 11:35.

16.04.2008 10:04 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 9 Monate.
Schattenkanzler Schattenkanzler ist männlich
myCSharp.de-Mitglied

Dabei seit: 27.04.2004
Beiträge: 238
Entwicklungsumgebung: Visual Studio 2008
Herkunft: Berlin


Schattenkanzler ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Wow, toller Artikel! Daumen hoch Bin ich jetzt gerade erst drauf gestoßen (leider nach einer Menge Arbeit...na ja, besser spät als nie).

Eine Ergänzung und eine Frage hab ich aber, hoffe, das sprengt den Rahmen nicht...

Ergänzung: Maximieren

Wenn man in borderoverride.zip (von egrath) doppelt auf die Titelleiste klickt, passiert - anders als normalerweise - nichts. Könnte mir vorstellen, dass das den ein oder anderen stört und möchte da mal meinen Senf dazu abgeben...

Das Verhalten kann man ganz leicht realiseren, indem man der Methode WndProc(ref Message) den folgenden case-Block hinzufügt:

C#-Code:
case WM_NCLBUTTONDBLCLK:
    this.WindowState = this.WindowState.Equals(FormWindowState.Maximized) ? FormWindowState.Normal : FormWindowState.Maximized;
    break;

Die neue Konstante muss man natürlich noch angeben:

C#-Code:
private const int WM_NCLBUTTONDBLCLK = 0x00A3;

So, nun zu meiner Frage.

Wenn die Form maximiert ist, kann man sie immer noch verschieben. Das widerspricht IMHO dem Verhalten einer "normalen" Windows-Form. Muss man dieses Verhalten einfach nachbauen (nach dem Motto "wenn maximiert, dann nicht bewegbar") oder gibt es da einen nativeren bzw. eleganteren Weg über die API?

Würde mich wirklich interessieren, da ich momentan (nebenbei) eine Form alá Office 2007 bastele und dazu diese Techniken benutzen möchte.

Greets - SK
06.02.2009 15:18 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Kalleberlin Kalleberlin ist männlich
myCSharp.de-Mitglied

Dabei seit: 11.06.2007
Beiträge: 165


Kalleberlin ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Gerade eben beim Surfe ;)

C#-Code:
[DllImport("user32.dll")]
private static extern Int32 EnableMenuItem ( System.IntPtr hMenu , Int32 uIDEnableItem, Int32 uEnable);
private const Int32 HTCAPTION = 0×00000002;
private const Int32 MF_BYCOMMAND =0×00000000;
private const Int32 MF_ENABLED =0×00000000;
private const Int32 MF_GRAYED =0×00000001;
private const Int32 MF_DISABLED =0×00000002;
private const Int32 SC_MOVE = 0xF010;
private const Int32 WM_NCLBUTTONDOWN = 0xA1;
private const Int32 WM_SYSCOMMAND = 0×112;
private const Int32 WM_INITMENUPOPUP = 0×117;

protected override void WndProc(ref System.Windows.Forms.Message m )
{
if( m.Msg == WM_INITMENUPOPUP )
{
//handles popup of system menu
if ((m.LParam.ToInt32() / 65536) != 0 ) // 'divide by 65536 to get hiword
{
Int32 AbleFlags = MF_ENABLED;
if (!Moveable)
{
AbleFlags = MF_DISABLED | MF_GRAYED; // disable the move
}
EnableMenuItem(m.WParam, SC_MOVE, MF_BYCOMMAND | AbleFlags);
}
}if(!Moveable)
{
if(m.Msg==WM_NCLBUTTONDOWN) //cancels the drag this is IMP
{if(m.WParam.ToInt32()==HTCAPTION) return;
}
if (m.Msg==WM_SYSCOMMAND) // Cancels any clicks on move menu
{
if ((m.WParam.ToInt32() & 0xFFF0) == SC_MOVE) return;
}
}
base.WndProc(ref m);
}
24.02.2009 22:40 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 7 Jahre.
Der letzte Beitrag ist älter als 5 Jahre.
Antwort erstellen


© Copyright 2003-2014 myCSharp.de-Team. Alle Rechte vorbehalten. 24.09.2014 04:38