Ever had those flickers in your 50+ component forms?
Trying to play with SuspendLayout / ResumeLayout didn’t help? That’s because it only suspends the automatic layout, triggered by the Anchor and Dock properties.
Setting double-buffering to true didn’t help either? That’s because it only suppresses flicker on individual controls: a label, a button and so on…
Setting the OptimizedDoubleBuffer flag to true? Nope, no changes…
After struggling for many hours trying to eliminate a flicker that was occuring on my application form, I finally found a solution on msdn:
It’s called compositing double-buffering and it’s simply 7 lines of code put in somewhere in your form code:
-
-
protected override CreateParams CreateParams {
-
get {
-
CreateParams cp = base.CreateParams;
-
cp.ExStyle |= 0×02000000;
-
return cp;
-
}
-
}
Explanation from nobugz:
I discovered a new Windows style in the SDK header files, available for Windows XP and (presumably) Vista: WS_EX_COMPOSITED. With that style turned on for your form, Windows XP does double-buffering on the form and all its child controls
nobugz is the man…
Delegates make it easier to find stuff in generic lists.
Let’s consider the following simple class:
-
Public Class Person{
-
private string m_name;
-
private int m_age;
-
-
// .Net3.0 syntax only.
-
public string Name{get;set}
-
public int Age{get;set}
-
-
public Person(string name, int age){
-
m_name = name;
-
m_age = age;
-
}
Instanciating :
-
List<Person> lstPpl = new List<Person>();
-
lstPpl.Add(new Person("Christian", 1));
-
lstPpl.Add(new Person("Noah", 0.3));
We can easily find Noah:
-
Person myson = lstPpl.Find(delegate(Person p) { return "Noah" == p.Name; });
For re-use: a small class that does encryption and binary serialization:
-
using System;
-
using System.Collections.Generic;
-
using System.Text;
-
using System.IO;
-
using System.Runtime.Serialization.Formatters.Binary;
-
using System.Security.Cryptography;
-
using System.Windows.Forms;
-
-
namespace MyNamespace
-
{
-
class Tools
-
{
-
// change me…
-
private static string m_encryptionKey = "password";
-
-
public static byte[] Encrypt(byte[] plainData, string sKey)
-
{
-
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
-
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
-
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
-
ICryptoTransform desencrypt = DES.CreateEncryptor();
-
byte[] encryptedData = desencrypt.TransformFinalBlock(plainData, 0, plainData.Length);
-
return encryptedData;
-
}
-
-
public static byte[] Decrypt(byte[] encryptedData, string sKey)
-
{
-
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
-
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
-
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
-
ICryptoTransform desDecrypt = DES.CreateDecryptor();
-
byte[] decryptedData = desDecrypt.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
-
return decryptedData;
-
}
-
-
public static void SaveObjectToFile(object obj, string path){
-
try
-
{
-
MemoryStream memStream = new MemoryStream();
-
BinaryFormatter binFormatter = new BinaryFormatter();
-
binFormatter.Serialize(memStream, obj);
-
byte[] encryptedBytes = Encrypt(memStream.ToArray(), m_encryptionKey);
-
memStream.Close();
-
Stream streamToFile = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
-
streamToFile.Write(encryptedBytes, 0, encryptedBytes.Length);
-
streamToFile.Flush();
-
streamToFile.Close();
-
}
-
catch (Exception e)
-
{
-
MessageBox.Show(e.Message);
-
}
-
}
-
-
public static object LoadObjectFromFile(string path)
-
{
-
try
-
{
-
-
Stream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
-
byte[] encryptedObj = new byte[fileStream.Length];
-
fileStream.Read(encryptedObj, 0, (int)encryptedObj.Length);
-
MemoryStream memStream = new MemoryStream(Decrypt(encryptedObj, m_encryptionKey));
-
BinaryFormatter binFormatter = new BinaryFormatter();
-
object decryptedObj = binFormatter.Deserialize(memStream);
-
memStream.Close();
-
fileStream.Close();
-
return decryptedObj;
-
}
-
catch (Exception e)
-
{
-
MessageBox.Show(e.Message);
-
}
-
return null;
-
}
-
}
-
}
Il m’arrive souvent de voir des blocs de code de contrôle inutilement complexes, mais je suis tombé récemment sur un bloc qui m’a particulièrement bluffé :
-
if( iNb < 100 ) {
-
if( iNb > 50 ) {
-
[… bloc 1 …]
-
} else if( iNb > 30 ) {
-
[… bloc 2 …]
-
} else if( iNb > 10 ) {
-
if( iNb <= 20 ){
-
[… bloc 3 …]
-
} else {
-
[… bloc 4 …]
-
}
-
}
-
}
Lorsque l’on remplit les [...] avec des dizaines de lignes de code (ce que je déconseille aussi fortement), on se retrouve vite à oublier le contrôle mal organisé qui se planque dans le code…
Il existe une pratique simple, qui doit être mise sous forme de théorie quelque part sur le net, qui consiste d’abord à visualiser les ensembles sur lesquels on fait le contrôle. Ici, on peut facilement déterminer 4 ensembles que l’on essaye de distinguer:
iNb dans ]50, 100[ (bloc 1)
iNb dans ]30, 50 [ (bloc 2)
iNb dans ]20, 30 [ (bloc 3)
iNb dans ]10, 20 [ (bloc 4)
Ensuite, le principe de base est le suivant : on conditionne sur le plus grand conteneur d’abord, et on restreint par la suite. On se retrouve alors avec un code beaucoup plus clair :
-
if( iNb < 100 ){
-
[… bloc 1 …]
-
} else if( iNb < 50 ) {
-
[… bloc 2 …]
-
} else if( iNb < 30 ) {
-
[… bloc 3 …]
-
} else if( (iNb < 20) && (iNb > 10) ){
-
[… bloc 4 …]
-
}
Autre possibilité, encore plus lisible je trouve :
-
if((iNb > 50) && (iNb < 100)){
-
[… bloc 1 …]
-
} else if ((iNb > 30) && (iNb < 50)){
-
[… bloc 2 …]
-
} else if ((iNb > 20) && (iNb < 30)){
-
[… bloc 3 …]
-
} else if ((iNb > 10) && (iNb < 20)){
-
[… bloc 4 …]
-
}
On redécrit les ensembles directement au sein des conditionnelles, de manière à ce que l’on ait pas à retrouver la condition précédente pour bien comprendre l’actuelle… Enfin tout du moins c’est ce que je pense…
Certains raccourcis peuvent parfois ralonger certaines routes. Par example, il est souvent requis d’échanger deux variables entre elles. Pour peu que cet échange doive avoir lieu au sein d’une boucle, disons pour un affichage fluide par ex., on va chercher à en optimiser l’échange.
De manière logique, on pense de suite à utiliser une variable intermédiaire :
-
void main()
-
{
-
int a = 0;
-
int b = 1;
-
int c;
-
-
// swap utilisant une 3e variable
-
c = a;
-
a = b;
-
b = c;
-
}
Cette méthode a beau avoir certains défauts, elle est en tout cas très clair.
Il y a pourtant plus court à écrire :
-
void main()
-
{
-
int a = 0;
-
int b = 1;
-
-
// en une ligne!
-
a+=b-=a=b-a;
-
}
Ou du moins c’est ce que l’on pourrait croire… Regardons le code assembleur généré par les deux corps de programmes précédents:
Swap à 3 variables :
-
push edi ; preparer de la place
-
push esi ; pour 3 variables
-
push ebx
-
cmp dword ptr ds:[00312E08h],0
-
je 00000011
-
call 78ACB4AF
-
xor esi,esi ; a = 0
-
xor edi,edi ; b = 0
-
xor ebx,ebx ; c = 0
-
xor esi,esi ; a = 0
-
mov edi,1 ; b = 1
-
mov ebx,esi ; c = a
-
mov esi,edi ; a = b
-
mov edi,ebx ; b = c
Swap en une ligne :
-
push edi ; preparer de la place
-
push esi ; pour 2 variables
-
cmp dword ptr ds:[003F2E08h],0
-
je 00000010
-
call 7963B4AF
-
xor esi,esi ; a = 0
-
xor edi,edi ; b = 0
-
xor esi,esi ; a = 0
-
mov edi,1 ; b = 1
-
mov eax,edi ; eax = reg. temporaire
-
sub eax,esi ; eax = b - a
-
mov esi,eax ; a = b - a
-
sub edi,esi ; b = b - a
-
add esi,edi ; a = a + b
On économise peut-être du tps en n’allouant pas de place pour une troisième variable, on le paye plus tard. Au final, le swap en une seule ligne prendra légèrement plus de temps que le swap sur 3 lignes. Plus important encore, le swap en une ligne est beaucoup moins lisible… Autant donc travailler clairement!!
Remarque :
Il existe une version optimale d’un échange de deux variables, détaillée ici : http://en.wikipedia.org/wiki/XOR_swap . Certains diront que cette version n’est pas entièrement portable… Mais ca reste à prouver, je l’ai vu tourner un peu partout… La technique consiste à utiliser la commande XOR qui est plus rapide que la commande MOV, comme ceci:
-
void main()
-
{
-
int a = 0;
-
int b = 1;
-
-
// avec des Xor :
-
a = a ^ b;
-
b = a ^ b;
-
a = a ^ b;
-
}
Ce qui donne :
-
push edi
-
push esi
-
cmp dword ptr ds:[00212E08h],0
-
je 00000010
-
call 7974B4AF
-
xor esi,esi
-
xor edi,edi
-
xor esi,esi
-
mov edi,1
-
xor esi,edi
-
xor edi,esi
-
xor esi,edi
Encore une fois, la lisibilité n’est pas optimale, à moins d’être habitué à faire des XOR!