Programmation sur Tablet PC : Réalisation d’un outil de recherche de notes.
Accueil > Articles > Matériels
|
|
Auteurs
|
|
 |
|
 |

26532 83/401
|
Introduction
Voici le troisième article
consacré au développement d’applications pour TabletPC. Nous avons vu les bases
du SDK TabletPC qui permet de gérer l’interface et l’encre numérique (voir
articles 1
et 2).
Comme promis (!), nous allons maintenant commencer notre projet de réalisation
d’un outil de recherche de notes. Le but sera de permettre à un utilisateur de
rechercher une note dans un dossier. Les fichiers de notes seront des fichiers Office
(.doc, .xls ou .ppt), texte (.txt), HTML, ou Windows Journal (.jnt). Les
critères de recherche seront la date, la taille, le nom du fichier bien sûr,
mais ils permettront également de rechercher des mots dans le fichier.
C’est là que résidera d’ailleurs la plus grosse difficulté puisqu’il faudra
tenir compte du formatage du fichier, ou même réaliser la reconnaissance de
caractères pour les fichiers .jnt.
Présentation de
l’application
Notre intention est de réaliser
un outil de recherche permettant de retrouver une note rédigée par l’utilisateur
ou l’un de ses collaborateurs. Lorsque Paul et Virginie participent à une
réunion de projet, chacun prend des notes sur son TabletPC à l’aide du Windows
Journal. Des rapports ont été rédigés, des fichiers textes saisis, des tableaux
Excel enregistrés. Paul et Virginie vont en fait enregistrer leurs documents et
notes dans un dossier partagé sur un ‘serveur’ central. Lorsqu’il souhaitera
retrouver une note, Paul lancera une recherche sur ce dossier et accédera ainsi
à toutes les notes prises par lui-même ou Virginie. On va même faire
mieux : Paul pourra accéder à ce répertoire offline : dans le train,
Paul recherchera une note dans un dossier local, copie exacte du dossier
partagé. Pour lui tout se fera de manière transparente : lorsque Paul est
déconnecté du réseau, une copie est réalisée, et lorsqu’il tente d’accéder au
dossier partagé, il y accède comme si de rien n’était, avec le même chemin
réseau… S’il modifie, ajoute ou supprime un fichier, ou si une modification a
lieu sur le serveur, une synchronisation aura lieu à son retour au bureau.
Nous allons donc tout d’abord
voir comment réaliser ce partage et cette synchronisation automatique. Ensuite,
nous allons réaliser un moteur de recherche de fichiers dans un dossier. Ce
moteur recherchera les documents dans le dossier partagé, que l’utilisateur
soit connecté au réseau ou pas.
Notre moteur va filtrer les
fichiers présents dans le dossier spécifié (et éventuellement les
sous-dossiers) correspondant aux critères sélectionnés par l’utilisateur :
date, taille, nom. Une fois cette recherche effectuée, le contenu des fichiers
sera analysé, et si les mots recherchés s’y trouvent, le score du fichier sera
augmenté. Le résultat de la recherche sera donc une liste de fichiers, à chacun
étant associé un score de pertinence en fonction de son contenu.
Le point important de ce moteur
sera la recherche dans le contenu. Il faudra en effet tenir compte du formatage
du fichier, et notamment permettre la recherche dans le contenu des fichiers
encre numérique du Windows Journal (.jnt).
Configuration de la
synchronisation de fichiers
Il faut tout d’abord créer un
dossier partagé sur un serveur accessible par tous les collaborateurs du
projet. C’est directement sur ce dossier qu’ils créeront leurs fichiers, qu’ils
soient connectés ou pas au réseau. Pour cela, il faut que chaque utilisateur
configure sur son poste la synchronisation des fichiers. Pour synchroniser un
dossier réseau, il suffit de le sélectionner dans l’explorateur et de faire ‘Fichier
à
Rendre disponible hors connexion’. Si c’est la première fois que vous utilisez
les fichiers hors connexion, une boîte de dialogue apparaît vous invitant à
configurer la synchronisation. Suivez les étapes :
Le dossier est maintenant disponible hors connexion, et sa
synchronisation est automatique !
Pour plus de détails sur la synchronisation des fichiers,
voir :
http://www.laboratoire-microsoft.org/articles/network/xp_sync/
Recherche dans tout
type de fichiers
Le problème majeur rencontré lors
de l’élaboration de notre moteur de recherche est, comme nous l’avons déjà fait
remarquer, la recherche dans le contenu. La première solution à laquelle nous avons
pensé est l’utilisation de l’automation.
Automation
L’automation consiste à ‘piloter’
via une interface COM une autre application. Cela se fait très simplement et
permet par exemple d’ouvrir un document Word, de l’enregistrer dans un autre
format (texte par exemple) puis de fermer Word. Voici le code C#
correspondant :
void DocToHtml(string
docPath,string htmlPath)
{
// ouverture de Word
Word.Application app=new Word.Application();
// on le cache !
app.Visible=false;
//
pour les paramètres qu’on ne va pas fournir :
object o=Missing.Value;
object docFile=docPath; //
chemin du doc avec transtypage
// Ouverture du document :
_Document doc=app.Documents.Open(ref docFile,ref o,ref o,
ref
o, ref o, ref
o, ref o, ref
o, ref o, ref
o, ref o,
ref
o, ref o, ref
o, ref o, ref
o);
object
fileName=htmlPath; // Chemin de destination
object
format=Word.WdSaveFormat.wdFormatText;//Txt
// On enregistre le doc au format texte :
doc.SaveAs(ref
fileName,ref format,ref
o,ref o,ref o,ref o,
ref
o,ref o,ref o,ref o,ref o, ref o, ref o,
ref
o, ref o, ref
o);
object
t=true;
// On quitte en évitant les messages de fermeture
app.Quit(ref t,ref o,ref o);
}
Pour l’utiliser, il suffit de rajouter la référence COM Microsoft
Word x.xx Object Library ainsi que la directive using
Word;. Notez également que pour éviter la confusion lors de la compilation
entre Word.Application et System.Windows.Forms.Application, vous devez
bien préciser le nom d’espace à chaque mention
de Application : par exemple System.Windows.Forms.Application.Run().
Par ce moyen d’automation, il devrait
être possible d’enregistrer en format texte tous les documents sur lesquels on
effectue la recherche puis d’ouvrir ces fichiers texte et de les lire afin
d’effectuer la recherche. Cela permet de réaliser une recherche sur des formats
propriétaires.
Un premier inconvénient est la
lenteur de cette méthode : utiliser une liaison Automation est extrêmement
lent, et si l’on doit répéter la méthode sur des centaines de fichiers,
l’utilisateur risque vite de s’impatienter… Autre problème de la méthode,
toutes les applications ne présentent pas une interface OLE Automation. Windows
Journal notamment ne présente pas cette interface…
Nous devons donc envisager une autre méthode qui soit plus
rapide et surtout plus ‘universelle’…
IFilter
La seule solution qui existe pour accéder au contenu des
fichiers Windows Journal (.jnt) est l’utilisation de IFilter. (Pour des
détails sur IFilter : http://msdn.microsoft.com/library/default.asp?url=/library/en-us/indexsrv/html/ixrefint_9sfm.asp).
IFilter est une interface qui possède des méthodes permettant d’extraire
les données d’un fichier au format propriétaire. Un format de fichier peut
implémenter cette interface, dans une dll qui contient les opérations
d’extraction. Plusieurs de ces dll sont pré-installées dans Windows : pour
les fichiers HTML, Word, Excel, PowerPoint, texte simple, fichiers binaires.
Pour d’autres formats, la dll implémentant IFilter peut être installé et
enregistré dans la registry (voir http://msdn.microsoft.com/library/default.asp?url=/library/en-us/indexsrv/html/ixufilt_56yb.asp).
Dans le cas des TabletPC, le format Windows Journal est également supporté par
défaut (pour le Windows Journal, l’implémentation de IFilter est faite
dans JNTFiltr.dll)
Pour utiliser IFilter, nous allons redéfinir les
constantes, structures et énumérations dont nous avons besoin, puis nous allons
déclarer l’interface IFilter ainsi que la méthode LoadIFilter
incluse dans la dll query.dll. C’est en effet LoadIFilter qui va charger
pour chaque fichier l’implémentation du IFilter correspondant.
Nous allons donc créer un RunTime Callable Wrapper qui va
nous permettre d’utiliser IFilter et LoadIFilter depuis le monde
.Net. Voici le code correspondant aux déclarations de IFilter et de
LoadIFilter :
using System.Runtime.InteropServices;
//...
//...
/// <summary>
/// Interface
IFilter
/// </sumary>
// Cette interface est exposée à COM puisqu’elle est
utilisée par
LoadIFilter
[ComVisible(true),
Guid("89BCB740-6119-101A-BCB7-00DD010655AF"),
// ID de IFilter
CLSCompliant(false), // Code non
CLS
//
Interface IUnknown lorsque visible de COM :
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
protected interface
IFilter
{
/// <summary>
///
Intialisation du IFilter
/// </sumary>
[PreserveSig] // L’interface est la même pour .Net et pour COM
uint Init(
[MarshalAs(UnmanagedType.U4)]
IFILTER_INIT grfFlags,
uint cAttributes,
[MarshalAs(UnmanagedType.LPArray)]
FULLPROPSPEC[] aAttributes,
out IntPtr pdwFlags
);
/// <summary>
/// Récupération d'un Chunk
/// </sumary>
[PreserveSig] // L’interface est la même pour .Net et pour COM
uint GetChunk(
[MarshalAs(UnmanagedType.Struct)]
out STAT_CHUNK pStat
);
/// <summary>
/// Récupération d'une portion de Chunk
/// </sumary>
[PreserveSig] // L’interface est la même pour .Net et pour COM
uint GetText(ref
int pcwcBuffer, IntPtr awcBuffer);
}
/// <summary>
/// Charge un
IFilter
/// </sumary>
[DllImport("query.dll")]
protected static extern int LoadIFilter(
[MarshalAs(UnmanagedType.LPWStr)]
string pwcsPath,
IntPtr pUnkOuter,
[MarshalAs(UnmanagedType.Interface)]
out IFilter ppIUnk
);
La définition de toutes les constantes liées à l’utilisation
de IFilter est visible dans la première partie du code téléchargeable ici. Nous pouvons maintenant effectuer des recherches
dans des fichiers au format propriétaire, y compris dans l’encre numérique des
fichiers Windows Journal. Pour cela, il faut tout d’abord charger un IFilter :
IFilter filter;
LoadIFilter(file.FullName, new
IntPtr(0), out filter);
On initialise ensuite notre filter :
IntPtr uflags;
filter.Init(IFILTER_INIT.IFILTER_INIT_APPLY_INDEX_ATTRIBUTES,
0, null, out uflags);
Nous allons maintenant pouvoir lire notre fichier. Pour
cela, il existe deux méthodes de IFilter : GetText() et GetChunk().
GetChunk renvoie le contenu du fichier morceau par morceau (chunk par
chunk) au fur et à mesure de ses appels. Ensuite, pour chaque morceau, la
méthode GetText() renvoie une partie du texte du chunk, au fur et à
mesure de ses appels également. Pour savoir si GetChunk bute sur la fin
du fichier ou si GetText bute sur la fin du chunk, il suffit de regarder
leur valeur de retour. GetChunk renvoie 0 si tout est OK (s’il n’a pas rencontré
la fin du fichier) et GetText peut renvoyer (entre autres) :
-
0 si pas de problème
-
FILTER_S_LAST_TEXT si le texte renvoyé est le dernier
-
FILTER_E_NO_MORE_TEXT s’il n’y a plus de texte
Voici donc le code permettant d’accéder à un fichier via IFilter :
uint retGT; // Valeur de retour
du GetText
uint retGC = 0; // Valeur de retour
du GetChunk
IntPtr buff; // buffer
pour GetText
int buffSize; // Taille du
buffer pour le GetText
Char[] data = null; // Tableau de
caractères extraits
STAT_CHUNK
statChunk;
string extractedData =
""; // Nouvelle string extraite du Chunk
while(retGC ==
IFILTER_RETURNED_VALUES.FILTER_OK)
{
// On récupère un nouveau Chunk :
retGC =
filter.GetChunk(out statChunk);
// On alloue le buffer :
buff = Marshal.AllocHGlobal(2 *
BuffSizeForChunk.BuffSize); try
{
// Initialisation de la taille du buffer :
buffSize =
BuffSizeForChunk.BuffSize; // Const à
déclarer !!
// Récupération de la nouvelle portion de texte :
retGT =
filter.GetText(ref buffSize, buff);
// Tant qu'il y a du texte
while((retGT ==
IFILTER_RETURNED_VALUES.FILTER_OK)
||
(retGT == IFILTER_RETURNED_VALUES.FILTER_S_LAST_TEXT))
{
//
Allocation du tableau de caractères :
data = new
Char[buffSize];
// Remplissage du tableau de caractères :
Marshal.Copy(buff, data, 0,
buffSize) ;
// Recopie du tableau de caractères dans une string
extractedData
= new string(data,0,buffSize);
//
Traitement sur la chaîne extraite
//…
//…
// Initialisation de la taille du buffer :
buffSize
= BuffSizeForChunk.BuffSize; // Const à
// déclarer !!
// Récupération de la nouvelle portion de texte :
retGT =
filter.GetText(ref buffSize, buff);
} // fin de la boucle sur un texte
}
catch
{
}
finally
{
Marshal.FreeHGlobal(buff);
// Libération du buffer
}
} // fin de la boucle sur les Chunks
Nous allons maintenant écrire le
corps de notre moteur de recherche. Notre objectif est de réaliser une dll qui
permettra de lancer une recherche en précisant plusieurs paramètres (taille,
dates, nom, recherche dans le contenu, respect de la casse, recherche dans les
sous-dossiers…).
dll du moteur de
recherche
Notre dll sera donc construite
autour de IFilter et LoadIFilter. Nous allons présenter la
dll classe par classe. Le code complet est disponible ici.
Détail des classes de la dll
• SearchInFile
Cette classe
est celle qui permet d’effectuer une recherche dans un fichier via IFilter.
Elle ne contient qu’une seule méthode publique (et statique) : Run().
àIFILTER_INIT
àCHUNK_BREAKTYPE
àCHUNKSTATE
àPROPSPEC
àFULLPROPSPEC
àSTAT_CHUNK
àIFILTER_RETURNED_VALUES
Il s’agit
d’énumérations, de structures et de classes protégées qui contiennent les
paramètres utilisés par IFilter.
àLoadIFilter()
Cette
déclaration est celle de la méthode LoadIFilter implémentée dans query.dll. On
indique donc le nom de la dll ainsi que le mot clé extern. Les
paramètres sont marshallés pour correspondre à ceux de la dll. Nous en rappelons
le code :
// Charge un IFilter
[DllImport("query.dll")]
protected static extern int
LoadIFilter(
[MarshalAs(UnmanagedType.LPWStr)]
string pwcsPath,
IntPtr pUnkOuter,
[MarshalAs(UnmanagedType.Interface)]
out IFilter ppIUnk
);
àIFilter
C’est la
déclaration de l’interface. Reportez-vous à la partie précédente de cet article
pour en voir le code.
àRun()
Voici enfin la
seule méthode exposée par SearchInFile. C’est cette méthode (statique)
qui peut être appelée pour lancer une recherche dans un fichier. Son prototype est :
public static int
Run(FileInfo file, ArrayList wordsList, bool caseSensitive)
Le premier
paramètre est le chemin complet du fichier. Le second est le tableau contenant
la liste des mots (ou expressions) à rechercher dans le fichier. Enfin, le
troisième paramètre précise si la recherche doit respecter la casse.
Au début de
la méthode, on transforme la liste des mots à rechercher en une hashtable :
la hashtable contient un booléen qui nous permettra de savoir si un mot
a déjà été rencontré ou pas (cette information sera utilisée pour le calcul du
score).
Comme dit
plus haut, on effectue ensuite deux boucles : l’une sur les chunks,
l’autre sur les parties de texte de chaque chunk. Au cœur de ces deux
boucles, on analyse le texte extrait : pour chaque mot de la hashtable,
on parcourt la chaîne extraite et on compte le nombre de fois où le mot
apparaît. On incrémente alors un compteur : si c’est la première fois que
l’on rencontre le mot (on le sait par la valeur dans la hashtable), on
augmente fortement le score. Sinon, on ajoute un nombre de points plus faible.
Cela permet d’éviter qu’un fichier soit bien noté alors qu’une seule des expressions
recherchées apparaît plusieurs fois, par rapport à un fichier où toutes les expressions
n’apparaissent qu’à une seule reprise.
Comme le GetText()
peut couper un mot n’importe où, et qu’une des expressions recherchées peut
être à cheval sur chacune des coupures, il est important de reprendre une
partie du GetText() précédent. On ne va cependant pas reprendre tout le
texte précédent, mais seulement la fin, le nombre de caractères recopiés étant
déterminé par la taille de la plus grande expression recherchée. C’est ce que
l’on fait ici à l’aide des variables string oldData, string newData,
int indexOfNewData, int MaxWordSize et de la méthode MaxLength() :
int MaxWordSize = MaxLength(h); // h est la liste des expressions à rechercher
// …
// …
// Recopie
du tableau de caractères dans une string :
newData = new string(data,0,buffSize);
//
Calcul du nombre de caractères qu’il faut recopier :
indexOfNewData =
System.Math.Min(oldData.Length, MaxWordSize);
//
Concaténation de ces caractères avec la nouvelle chaîne :
newData = oldData.Substring(oldData.Length -
indexOfNewData) + newData;
// Si on
ne tient pas compte de la casse :
if(!caseSensitive) newData = newData.ToUpper();
// On
lance la recherche dans la nouvelle string :
score +=
SearchKeyWords(newData,h,indexOfNewData);
// La
nouvelle devient l’ancienne en fin de boucle :
oldData = newData;
àInitHashtable()
Cette méthode
initialise une hashtable en fonction de l'ArrayList passé. Au
passage, les doublons sont éliminés. La hashtable rendue contiendra pour
chaque expression de la liste un booléen initialisé à false. Ce booléen
précise si l’expression a déjà été rencontrée dans le fichier en cours
d’analyse. Lorsque l’expression est rencontrée pour la première fois, le score
du fichier augmente fortement. Aux prochaines occurrences du mot, le score sera
plus faiblement augmenté.
àSearchKeyWords()
Cette
méthode recherche une liste d’expressions dans une string et renvoie un score.
à
MaxLength()
Renvoie la
taille de l’expression la plus longue de la liste passée en paramètre.
• SearchInDirectory
Cette
classe présente une seule méthode publique (et statique) Run() qui
permet de lancer la recherche dans un répertoire en précisant tous les
paramètres de la recherche. C’est la principale méthode de notre dll, celle qui
est appelée de l’extérieur lorsque l’on souhaite effectuer une recherche.
àRun()
Cette
méthode établit d’abord la liste des fichiers à partir des filtres sur le nom
par appel à ListFiles(). Ensuite, les fichiers trouvés sont filtrés en
fonction des autres critères de la recherche (taille, dates). Enfin, la méthode
va associer un score à chaque fichier en faisant appel à SearchInFile.Run().
Le résultat de cette méthode est un tableau de ScoredFileInfo (pour une
description de ce type, voir plus loin).
àListFiles()
C’est une
méthode privée qui liste tous les fichiers correspondant aux filtres sur le nom
spécifiés. A noter que les filtres de la liste sont coordonnés par des
‘ou’ : si au moins un des filtres de la liste apparaît dans le nom du
fichier, celui-ci est sélectionné. Des appels récursifs permettent de scanner
les sous-dossiers le cas échéant.
• SearchAttributes
Cette classe
contient tous les paramètres possibles d’une recherche. Elle contient donc une
liste d’attributs privés avec leurs propriétés publiques respectives. On
trouve :
ArrayList filters;// Liste de filtres sur l'extension
bool
subdirectories = false;//
Inclure la recherche dans les sous-dossiers
string
directoryPath = "";// Répertoire de
recherche
ArrayList fileName;// Liste des mots pour la recherche sur le nom de fichier
ArrayList fileContent;// Liste des mots pour la recherche sur le contenu
System.DateTime startTime;// Date de début pour la recherche sur date
System.DateTime endTime;// Date de fin pour la recherche sur date
DateTypeSearch dateSearch =
DateTypeSearch.Without;// Type de la recherche sur
date
long
minSize = 0;// Taille mini pour la recherche sur
taille
long
maxSize;// Taille maxi pour la recherche sur taille
bool
sizeSearch = false;//
Recherche sur la taille ?
bool
caseSensitive = false;//
Respect de la casse ?
• ScoredFileInfo
ScoredFileInfo
est une classe contenant un membre FileInfo de type System.IO.FileInfo
et un membre Score de type int (ce sont en réalité les propriétés
des attributs fileInfo et score). On associe ainsi un score à un
fichier : FileInfo contient toutes les informations sur le fichier
et Score et le résultat de la recherche dans le contenu du fichier. Il
aurait été ici plus adéquat de dériver la classe FileInfo, mais cela est
malheureusement impossible puisque cette classe est scellée.
Nous avons
redéfini la méthode Equals() avec deux surcharges afin de pouvoir
comparer un ScoredFileInfo avec un FileInfo ou avec un autre ScoredFileInfo.
La comparaison ne s’effectue que sur le FullName (on ne compare pas par
exemple LastAccessTime car deux FileInfo du même fichier peuvent
ne pas avoir le même…) :
public bool
Equals(FileInfo fi)
{
// Comparaison du chemin des deux fichiers
return (this.FileInfo.FullName
== fi.FullName);
}
public
bool Equals(ScoredFileInfo fi)
{
// Comparaison du chemin des deux fichiers
return (this.FileInfo.FullName
== fi.FileInfo.FullName);
}
• Bonus et BuffSizeForChunk
Ces deux classes contiennent des
membres int qui sont déclarés en const. Ces membres tiennent en
fait lieu de constantes : en C#, la seule manière de faire un #define
est de créer un membre const dans une classe… Vous pouvez donc en lieu
et place des #define créer une classe qui contiendra toutes les
constantes de votre programme.
•
ExtendedArrayList
Cette classe dérive de ArrayList. Elle
ajoute une surcharge pour la méthode Contains(). Contains()
permet maintenant de tester l’appartenance d’un ScoredFileInfo ou d’un FileInfo
à la liste.
• DateTypeSearch
Il s’agit d’une énumération
contenant les différents types de recherche sur la date :
public enum DateTypeSearch : int
{
Without
= 0,
Modification
= 1,
Creation
= 2
}
Création d’une dll en .Net
Il n’y a rien de plus
simple : il suffit de créer un
projet bibliothèque de classes :

Ensuite, on crée ses propres classes dans le namespace.
Utilisation de la dll
Pour utiliser la dll dans un projet, il faut tout d’abord
ajouter une référence vers cette dll. Pour cela, aller dans ‘ProjetàAjouter
une référence’ et faites ‘Parcourir’. Sélectionnez la dll et validez. Pour
l’utiliser plus facilement, vous pouvez ajouter en tête de votre code la
directive : using
SearchFiles;.
Pour lancer
une recherche sur des fichiers, il suffit de créer un objet SearchAttributes
sa, de remplir tous ses champs dossier, date, taille, respect de la casse
(les champs ont des valeurs par défaut, il n’est pas nécessaire de tous les
saisir) et de remplir les listes de filtres sur le nom (Filters) et les
expressions à rechercher dans le contenu (FileContent). Ensuite, on fait
un appel à la méthode statique :
SearchInDirectory.Run(sa);
Le code d’une petite interface de
test est disponible ici. Cette interface,
extrêmement sommaire (elle ne contient d’ailleurs pas tous les paramètres
possibles et la recherche ne s’effectue que sur une seule expression) permet de
tester rapidement la dll. En outre, tous les résultats sont affichés sans tri
et de façon un peu brute.
Conclusion
Nous avons réalisé une dll .Net
permettant de rechercher des fichiers dans un dossier. La recherche s’effectue
sur les nom, date et taille. Notre moteur de recherche permet également de
préciser des expressions à rechercher dans le contenu du fichier. La recherche
dans le contenu s’effectue en tenant compte du format du fichier (.doc, .html,
.xls… et surtout .jnt). Le résultat de la recherche est une liste de fichiers
correspondant aux critères de base (nom, taille, date), à chaque fichier étant
associé un score correspondant à la pertinence de son contenu par rapport aux
expressions recherchées.
Le code de la dll est disponible ici.
Le code de l’exe de test est disponible ici.
La dll est téléchargeable ici.
L’exe est téléchargeable ici.
Le code proposé est un code de
base, qui peut être encore largement amélioré. Il serait par exemple pratique
de pouvoir spécifier plusieurs répertoires de recherche. Un autre problème de
la dll présentée est que le résultat de la recherche est rendu à la fin de
toute la recherche : si la recherche est longue, la liste des fichiers
n’apparaît qu’au bout de plusieurs dizaines de secondes. Il faudrait pouvoir envoyer
au client la liste des fichiers au fur et à mesure que ceux-ci sont trouvés.
Une autre amélioration possible serait la création d’une barre de progression
de la recherche. Ces améliorations sont possibles en créant des événements.
Nous allons maintenant nous attacher
à la réalisation d’une interface exploitant notre moteur. Cette interface
contiendra plusieurs éléments orientés TabletPC (voir le premier
article de cette série).
|
|
 |
Pour afficher ou poster un commentaire, cliquez sur ce lien : Forum-Microsoft
|
|