Fleur je winforms controls een beetje op

Door RobIII op dinsdag 9 februari 2010 14:03 - Reacties (13)
CategorieŰn: Development, Life as a developer, Views: 7.049

De standaard winforms treeview en listview zijn altijd al een beetje een doorn in mijn oog geweest; ze zien er gedateerd uit zoals je in onderstaand screenshot zult zien. Toch is het met een paar regels code makkelijk om een wat 'modernere look' toe te passen. Weinig code, veel plezier. Dat is altijd goed :P

http://tweakers.net/ext/f/6QmO2gzLdB4Jhsdeqor1lAnj/full.png Op de ÚÚn of andere manier zijn de listview en treeview sowieso niet 's werelds fijnste controls om mee te werken waardoor ik wel eens ben uitgeweken naar third-party controls die nÚt even dat beetje extra bieden. Zo is het bij een 'standaard listview' belachelijk lastig om een pijltje in de columnheaders toe te voegen voor een sorteerindicatie en zou je soms willen dat de treeview control meerdere kolommen ondersteunde om maar eens wat te noemen. Althans; dat geldt voor Winforms (2.0) controls; ik ben to be honest nog niet veel (of genoeg) bezig geweest met WPF en/of .Net 3.0+ gezien ik voor mijn werk (voorlopig) nog even aan 2.0 vast zit.

Natuurlijk, overal is iets op te maken, alles is aan te passen. Maar vaker wel dan niet brengt dat een boel code met zich mee welke ook nog eens gebruik maakt van een hele berg native calls (ofwel P/Invoke's) om te bereiken wat je wil. Uitwijken naar een 3rd party control die je het werk uit handen neemt is dan een makkelijke keuze. Zo kan ik van harte de (gratis!) Krypton Toolsuite aanraden. Vergeleken met de (in mijn ogen door de jaren heen bloated geworden) Infragistics controls krijg je een flinke zwieper aan de look van je GUI in ruil voor een relatief kleine footprint en een goed werkende control suite. (Overigens gebruiken we het hier veel en hebben daarom toen ook besloten om de auteur eens flink te sponsoren en hebben dus meteen heel de suite inclusief sourcecode aangeschaft). De prijzen van de Krypton onderdelen zijn ook stukken vriendelijker dan de Infragistics/DevExpress varianten.

Ik draaf weer eens van 't paadje :P Waar 't om ging is het moderniseren van de treeview en listview controls. Nou, dat is easy. We beginnen met een class die we voor beide controls nodig gaan hebben; deze noemen we bijvoorbeeld NativeMethods.cs:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace KeenSystems.SmartMan.General
{
    public static class NativeMethods
    {
        public const int WM_CREATE = 0x1;

        [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
        public extern static int SetWindowTheme(IntPtr hWnd, string pszSubAppName, string pszSubIdList);

        public static int SetExplorerTheme(Control ctrl)
        {
            return SetWindowTheme(ctrl.Handle, "Explorer", null);
        }
    }
}


Je zult al gauw zien dat de 'magic' zit in de SetWindowTheme call. Meer details vind je in de documentatie; daar zal ik niet te ver op in gaan.

Het enige wat je nu nog moet doen is voor iedere control (treeview/listview) welke je wil 'updaten' met de 'moderne' look de SetExplorerTheme method aanroepen. In mijn geval doe ik dat in het Form's Load event:

C#:
1
2
NativeMethods.SetExplorerTheme(treeView1);
NativeMethods.SetExplorerTheme(listView1);


http://tweakers.net/ext/f/7Ohxn74073dC9hoMStFq2crb/full.png Voila. 2 Controls gestyled. Let in de treeview vooral op de plusjes en minnetjes welke zijn veranderd naar de typerende driehoekjes en in de listview en treeview zie je ook dat er een 'hippe' selectie gebruikt wordt in plaats van de saaie blauwe standaard selectie.

Wat ik echter nog beter kan aanbevelen is het afleiden van je eigen controls. Kwestie van de listview/treeview inheriten en vanaf dat moment je eigen control op je forms pleuren in plaats van de standaard controls. Zo profiteer je later nog makkelijker van wijzigingen die je doorvoert in je controls, maar dat hoef ik niemand hopelijk uit te leggen ;)

Ik heb het op deze manier aangepakt:

C#:
1
2
3
4
5
6
7
8
9
10
11
//Treeview:
public class MyTreeview : TreeView
{
    protected override void WndProc(ref Message m)
    {   
        //Use 'modern' style treeview
        if (m.Msg == NativeMethods.WM_CREATE)
            NativeMethods.SetExplorerTheme(this);
        base.WndProc(ref m);
    }
}


Voor de listview doe je natuurlijk hetzelfde. Voor de echte juiste look kun je voor de treeview ook de treelines nog uitschakelen, wat ik zelf overigens niet mooi vind:

C#:
1
treeView1.ShowLines = false;


Een andere aanrader is om het font meteen te wijzigen in het (weinig intu´tieve) SystemFonts.IconTitleFont dat je hierboven ook meteen toegepast ziet.

C#:
1
2
treeView1.Font = SystemFonts.IconTitleFont;
listView1.Font = SystemFonts.IconTitleFont;


http://tweakers.net/ext/f/IWevhVx6FqdbxOZjM5B1kPQD/full.jpgOf natuurlijk in je afgeleide control. Makkelijker kan het haast niet. Be-dee-be-dee-be-dee:

TwitterNuJIJeKudosFacebookFriendfeedGoogle BookmarksDiggdel.ici.ousTechnoratiSphinnMixxStumpleUponYahoo! BookmarksMaak je eigen RML op RobIII.nl!

Volgende: OfficeExpert.biz == zakkenwassers 02-'10 OfficeExpert.biz == zakkenwassers
Volgende: In het land der blinden... part 7 12-'09 In het land der blinden... part 7

Reacties


Door Tweakers user Snake, dinsdag 9 februari 2010 14:25

Ik zou de 2de parameter null laten. Dan wordt deze de naam van de calling application.

Terwijl dat jij je nu voordoet als Explorer.

Door Tweakers user RobIII, dinsdag 9 februari 2010 14:30

Ik lees 't anders:
pszSubAppName
    [in] Pointer to a string that contains the application name to use in place of the calling application's name. If this parameter is NULL, the calling application's name is used.
Wat ik daar uit haal (en de voorbeelden online) is juist dat je daarmee aangeeft de "Explorer theme" te willen gebruiken/overnemen. Maak ik 'm null dan haalt de call ook niets uit (zojuist even getest).

[Reactie gewijzigd op dinsdag 9 februari 2010 14:31]


Door Tweakers user Snake, dinsdag 9 februari 2010 15:08

Ah inderdaad, nu snap ik het.

Hij neemt dan eigelijk de stijl over van die applicatie en applied die op de jouwe.

En de laatste is voor CLSID's. Maar wat is een application's name? Ik heb Firefox geprobeerd, dat doet het niet, Messenger doet ook niets.

[Reactie gewijzigd op dinsdag 9 februari 2010 15:15]


Door Tweakers user RobIII, dinsdag 9 februari 2010 15:42

Ik heb me niet tot op de bodem in de materie verdiept (hey, 't werkt, it compiles, ship it! :P ) maar voor zover ik begrijp wordt sowieso (voorlopig?) alleen nog maar "Explorer" (of "explorer") geaccepteerd en werkt 't ook alleen maar op listviews/treeviews. Meer informatie zou te vinden zijn in AeroStyle.xml in de Windows SDK maar die heb ik momenteel niet bij de hand Leef je uit :Y).

Misschien ook even het vermelden waard dat her-en-der vermeld wordt dat het DoubleBuffered property van het form op true dient te staan; nu gebruiken wij sowieso alleen maar DoubleBuffered (afgeleide) forms dus dat kon wel eens kloppen; ik heb echter in het simpele formpje dat ik voor dit blog gebruikte geen problemen ondervonden zonder double buffering; maar dat zegt niet veel natuurlijk.

[Reactie gewijzigd op dinsdag 9 februari 2010 15:51]


Door Tweakers user Rhapsody, dinsdag 9 februari 2010 15:52

Mooie post.

Ik was in het begin bang dat je met een of andere exotische look aan zou komen :P

Door Tweakers user Battle Bunny, dinsdag 9 februari 2010 15:58

Ik moet heel eerlijk zeggen dat ik de opgefleurde variant veel minder duidelijk vind dan de default. Vooral de kolom koppen in je listview zijn onduidelijk (kan aan laptop scherm liggen). Daarbij vind ik het ook bokke irritant als die lijntjes in de treeview verdwijnen.

Maar ja, ik zou het liefst dan ook nog Windows 2000 draaien ;)

Overigens gaan wij de Janus WinForms kit gebruiken. Heeft ook een Ribbon control en een hele berg andere zaken. Kost alleen wel een paar centen.

Door Tweakers user RobIII, dinsdag 9 februari 2010 16:33

Ik was in het begin bang dat je met een of andere exotische look aan zou komen :P
Nee hoor ;)
Ik moet heel eerlijk zeggen dat ik de opgefleurde variant veel minder duidelijk vind dan de default. Vooral de kolom koppen in je listview zijn onduidelijk (kan aan laptop scherm liggen).
Dat is a) even wennen aan Segoe UI (default Vista/Win7 font) en b) kwestie van het font niet wijzigen als je dat niet mooi vindt :P Het mooie is dat er gewoon gebruik wordt gemaakt van 't font dat Windows overal gebruikt; dus je controls houden zich nu (beter) aan de globale stijl en wijken juist niet meer af. Als jij in Windows een ander font zou instellen neemt je applicatie dat dus mee.
Daarbij vind ik het ook bokke irritant als die lijntjes in de treeview verdwijnen.
Eensch. Maar zie b) ;)

[Reactie gewijzigd op dinsdag 9 februari 2010 17:08]


Door Tweakers user sanderev66, woensdag 10 februari 2010 11:05

Thx voor deze blog! Hier was ik nog naar aan het zoeken om mijn programma's meer Windows qua look te maken. (Vooral Connect en NoteIt)

Door Tweakers user Battle Bunny, woensdag 10 februari 2010 18:45

Yay, ook gebruikt. Even een tip, in VB.Net kan het ook zo:

code:
1
2
3
4
5
6
Private Declare Unicode Function SetWindowTheme Lib "uxtheme.dll" (ByVal hWnd As IntPtr, ByVal pszSubAppName As String, ByVal pszSubIdList As String) As Integer

  Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
    SetWindowTheme(Me.Handle, "Explorer", Nothing)
    MyBase.OnHandleCreated(e)
  End Sub


Door Tweakers user sanderev66, woensdag 10 februari 2010 21:53

Je je kan idd ook OnHandleCreated gebruiken, maar je kan ook gewoon de WndProc overriden. Ook in VB ;)


Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
Public Class NativeMethods

    Public Const WM_CREATE As Integer = &H1

    Public Declare Unicode Function SetWindowTheme Lib "uxtheme.dll" (ByVal hWnd As IntPtr, ByVal pszSubAppName As String, ByVal pszSubIdList As String) As Integer

    Public Shared Function SetExplorerTheme(ByVal ctrl As Control) As Integer
        Return SetWindowTheme(ctrl.Handle, "Explorer", Nothing)
    End Function
End Class



Dat is NativeMethods in VB.Net ;)


Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
Public Class ListViewTest
    Inherits ListView

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = NativeMethods.WM_CREATE Then
            NativeMethods.SetExplorerTheme(Me)
        End If

        MyBase.WndProc(m)
    End Sub

End Class


En dat is de WndProc override ;) Net als in C# dus.

[Reactie gewijzigd op woensdag 10 februari 2010 22:04]


Door Tweakers user Battle Bunny, donderdag 11 februari 2010 10:14

Snap dat je ook de WndProc kunt gebruiken. Alleen waarom in window messages lopen te roeren als er netjes een event voor is die dat al voor je doet?

Door Tweakers user sanderev66, donderdag 11 februari 2010 11:03

Omdat het sneller is ;)

Door Tweakers user Alex), dinsdag 30 maart 2010 22:16

Leuke tip, maar ik vind de fonts in je screenshot een beetje blurry. Gek eigenlijk, want ik heb ClearType aanstaan (default in Win7) en hier ziet alles er verder prima uit.

Op http://www.microsoft.com/...ClearType/tuner/tune.aspx staat een tunertooltje, misschien is dat ook nog een tip? :)

Reageren is niet meer mogelijk