|
Trouver une ressource
Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !
PROPRIÉTÉS D'EXTENSION AVEC C# 3.0
Information sur la source
Description
Ceci est un code qui permet de simuler des propriétés d'extension grâce à aux méthodes d'extension de C# 3.0. L'idée est de définir 2 méthodes d'extension, l'une servant d'accesseur set et l'autre d'accesseur get, les valeurs étant prises et stockées dans un Dictionary. J'ai prévu pour cela deux façons de faire : - première méthode : la méthode servant d'accesseur set doit avoir un nom commençant par "Set", tandis que celle servant d'accesseur get doit avoir un nom commençant par "Get". Le reste des noms doit être égaux pour les 2 méthodes. Par exemple : public static string GetProperty(this IA i) public static void SetProperty(this IA i, string value) - deuxième méthode : les 2 méthodes portent le même nom mais des attributs "Get" et "Set" leurs sont associées. Par exemple : [Get] public static string AProperty(this IA i) { return string.Empty; } [Set] public static void AProperty(this IA i, string value) { } J'intercepte, les appels de ces méthodes afin de centraliser le remplissage/renvoi de valeurs vers/depuis mon Dictionary. J'utilise pour cela PostSharp (http://www.postsharp.org/). Je profite de cet outil pour vérifier à la compilation que les conditions citées plus haut sont bien remplies, que des attributs ne sont pas oubliés, que le type d'entré (type du paramètre de la méthode set) et de sorti (type de retour de la méthode get) correspondent,... En combinant ça avec des interfaces, on pourrait presque faire de l'héritage multiple...
Source
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using PostSharp.Laos;
- using System.Reflection;
- using PostSharp.Extensibility;
- using System.Text.RegularExpressions;
-
- namespace ExtensionProperties
- {
- /// <summary>
- /// Classe de base pour intercepter les méthodes d'extension "propriétés"
- /// </summary>
- [Serializable]
- public abstract class BasePropertyInterceptor : OnMethodBoundaryAspect
- {
- protected static PropertyContainer PropertyValues = PropertyContainer.GetInstance();
-
- public override void OnEntry(MethodExecutionEventArgs e)
- {
- OwnerType = e.Method.GetParameters()[0].ParameterType;
- Owner = e.GetArguments()[0];
- if (e.GetArguments().Length > 1)
- Value = e.GetArguments()[1];
- }
-
- public override void CompileTimeInitialize(MethodBase method)
- {
- MethodName = method.Name;
- }
-
- /// <summary>
- /// Type de l'objet portant la méthode-"propriété".
- /// </summary>
- public Type OwnerType { get; set; }
-
- /// <summary>
- /// Instance de l'objet portant la méthode-"propriété".
- /// </summary>
- public object Owner { get; set; }
-
- /// <summary>
- /// Nom de la méthode-"propriété".
- /// </summary>
- public virtual string MethodName { get; set; }
-
- /// <summary>
- /// Valeur de la propriété.
- /// </summary>
- public object Value { get; set; }
-
- [Serializable]
- protected class PropertyContainer : Dictionary<Type, Dictionary<object, Dictionary<string, object>>>
- {
- // Design pattern singleton
- private static PropertyContainer instance = null;
-
- private PropertyContainer() { }
-
- public static PropertyContainer GetInstance()
- {
- if (instance == null)
- instance = new PropertyContainer();
- return instance;
- }
-
- // Redéfinition d'indexeurs
- public object this[Type objtype, object obj, string propertyName]
- {
- get
- {
- try { return base[objtype][obj][propertyName]; }
- catch { return null; }
- }
- }
-
- public Dictionary<string, object> this[Type objtype, object obj]
- {
- get
- {
- try { return base[objtype][obj]; }
- catch { return null; }
- }
- }
- }
- }
-
-
- /// <summary>
- /// Attribut à associer à la classe contenant les méthodes d'extension. Il intercepte les méthodes d'extension "Get*"/"Set*" et renvoit/stock les valeurs.
- /// </summary>
- [Serializable]
- public class InterceptProperties : BasePropertyInterceptor
- {
- private Get _Getter;
- private Set _Setter;
-
- public override void CompileTimeInitialize(MethodBase method)
- {
- base.CompileTimeInitialize(method);
- if (method.Name.StartsWith("Get"))
- {
- CheckAccessorAttributes(method);
-
- // la méthode "Get" doit être sans paramètre.
- if (method.GetParameters().Length != 1)
- throw new Exception(string.Format("Une méthode dont le nom commence par \"Get\" a été trouvée, mais elle possède un ou plusieurs paramètres sans modificateur \"this\".", method.Name));
- var instance = method.GetParameters()[0];
-
- // on cherche une methode portée par la même classe, de même nom et possédant un attribut "Set".
- var presumedSetters = method.DeclaringType.GetMethods().Where(m => m.Name.StartsWith("Set") && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString());
- if (presumedSetters.Count() == 0)
- throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} dont le nom commence par \"Set\".", method.Name, instance.ParameterType));
- }
- if (method.Name.StartsWith("Set"))
- {
- CheckAccessorAttributes(method);
-
- // on vérifie que la méthode ne possède qu'un seul paramètre.
- ParameterInfo param;
- if (method.GetParameters().Length != 2)
- throw new Exception(string.Format("La méthode \"{0}\" définie comme accesseur \"set\" doit posséder un unique paramètre sans modificateur \"this\".", method.Name));
- param = method.GetParameters()[1];
- var instance = method.GetParameters()[0];
-
- // on cherche une methode portée par la même classe, de même nom et possédant un attribut "Get".
- var presumedGetters = method.DeclaringType.GetMethods().Where(m => m.Name.StartsWith("Get") && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString());
- if (presumedGetters.Count() == 0)
- throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} dont le nom commence par \"Get\".", method.Name, instance.ParameterType));
-
- // la méthode "Get" doit avoir une valeur de retour du même type que le paramètre de la méthode "Set".
- presumedGetters = presumedGetters.Where(m => m.ReturnType == param.ParameterType);
- if (presumedGetters.Count() == 0)
- throw new Exception(string.Format("Une méthode dont le nom commence par \"Get\" a été trouvée, mais le type de son paramètre de retour ne correspond pas à celui du paramètre de la méthode \"Set\".", method.Name));
- }
- }
-
- protected void CheckAccessorAttributes(MethodBase method)
- {
- var setAttr = method.GetCustomAttributes(typeof(Set), false);
- var getAttr = method.GetCustomAttributes(typeof(Get), false);
- if (setAttr != null && setAttr.Length != 0)
- throw new Exception(string.Format("La méthode \"{0}\" ne doit pas avoir d'attribut \"Set\" associé",method.Name));
- if (getAttr != null && getAttr.Length != 0)
- throw new Exception(string.Format("La méthode \"{0}\" ne doit pas avoir d'attribut \"Get\" associé",method.Name));
- }
-
- public override void OnEntry(MethodExecutionEventArgs e)
- {
- base.OnEntry(e);
- if (e.Method.Name.StartsWith("Set"))
- Setter.OnEntry(e);
- }
-
- public override void OnExit(MethodExecutionEventArgs e)
- {
- if (e.Method.Name.StartsWith("Get"))
- Getter.OnExit(e);
- }
-
-
- public override string MethodName
- {
- get { return base.MethodName.Substring(3); }
- }
-
- // Il faut propager les infos OwnerType, Owner, MethodName et Value vers nos objets Getter et Setter car ceux-ci n'interceptent pas directement les méthodes.
- private Get Getter
- {
- get
- {
- if (_Getter == null || (_Getter!=null && _Getter.Owner!=this.Owner))
- _Getter = new Get
- {
- OwnerType = this.OwnerType,
- Owner = this.Owner,
- MethodName = this.MethodName,
- Value = this.Value
- };
- return _Getter;
- }
- }
-
- private Set Setter
- {
- get
- {
- if (_Getter == null || (_Getter!=null && _Getter.Owner!=this.Owner))
- _Setter = new Set
- {
- OwnerType = this.OwnerType,
- Owner = this.Owner,
- MethodName = this.MethodName,
- Value = this.Value
- };
- return _Setter;
- }
- }
- }
-
- /// <summary>
- /// Attribut à associer à une méthode d'extension pour que celle-ci remplace un accesseur "get" d'une propriété.
- /// </summary>
- [Serializable]
- [MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true)]
- public class Get : BasePropertyInterceptor
- {
- public override void CompileTimeInitialize(MethodBase method)
- {
- base.CompileTimeInitialize(method);
- // la méthode "Get" doit être sans paramètre.
- if (method.GetParameters().Length != 1)
- throw new Exception(string.Format("Une méthode \"{0}\" portant un attribut \"Get\" a été trouvée, mais elle possède un ou plusieurs paramètres sans modificateur \"this\".", method.Name));
- var instance = method.GetParameters()[0];
-
- // on cherche une methode portée par la même classe, de même nom et possédant un attribut "Set".
- var presumedSetters = method.DeclaringType.GetMethods().Where(m =>
- {
- if (m.Name == method.Name && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString())
- {
- var attrs = m.GetCustomAttributes(typeof(Set), false);
- if (attrs != null && attrs.Length != 0)
- return true;
- }
- return false;
- });
- if (presumedSetters.Count() == 0)
- throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} portant un attribut \"Set\".", method.Name, instance.ParameterType));
- }
-
- public override void OnExit(MethodExecutionEventArgs e)
- {
- e.ReturnValue = PropertyValues[OwnerType, Owner, MethodName];
- }
- }
-
- /// <summary>
- /// Attribut à associer à une méthode d'extension pour que celle-ci remplace un accesseur "set" d'une propriété.
- /// </summary>
- [Serializable]
- [MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData=true)]
- public class Set : BasePropertyInterceptor
- {
- public override void CompileTimeInitialize(MethodBase method)
- {
- base.CompileTimeInitialize(method);
- // on vérifie que la méthode ne possède qu'un seul paramètre.
- if (method.GetParameters().Length != 2)
- throw new Exception(string.Format("La méthode \"{0}\" définie comme accesseur \"set\" doit posséder un unique paramètre sans modificateur \"this\".", method.Name));
- var param = method.GetParameters()[1];
- var instance = method.GetParameters()[0];
-
- // on cherche une methode portée par la même classe, de même nom et possédant un attribut "Get".
- var presumedGetters = method.DeclaringType.GetMethods().Where(m =>
- {
- if (m.Name == method.Name && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString())
- {
- var attrs = m.GetCustomAttributes(typeof(Get), false);
- if (attrs != null && attrs.Length != 0)
- return true;
- }
- return false;
- });
- if(presumedGetters.Count()==0)
- throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} portant un attribut \"Get\".", method.Name, instance.ParameterType));
-
- // la méthode "Get" doit avoir une valeur de retour du même type que le paramètre de la méthode "Set".
- presumedGetters = presumedGetters.Where(m => m.ReturnType == param.ParameterType);
- if (presumedGetters.Count() == 0)
- throw new Exception(string.Format("Une méthode \"{0}\" portant un attribut \"Get\" a été trouvée, mais le type de son paramètre de retour ne correspond pas à celui du paramètre de la méthode \"Set\".", method.Name));
- }
-
- public override void OnEntry(MethodExecutionEventArgs e)
- {
- base.OnEntry(e);
- if (Value == null)
- return;
- if (!PropertyValues.ContainsKey(OwnerType))
- PropertyValues.Add(OwnerType, new Dictionary<object, Dictionary<string, object>>());
- if (!PropertyValues[OwnerType].ContainsKey(Owner))
- PropertyValues[OwnerType].Add(Owner, new Dictionary<string, object>());
- if (!PropertyValues[OwnerType, Owner].ContainsKey(MethodName))
- PropertyValues[OwnerType, Owner].Add(MethodName, Value);
- }
- }
- }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using System.Reflection;
using PostSharp.Extensibility;
using System.Text.RegularExpressions;
namespace ExtensionProperties
{
/// <summary>
/// Classe de base pour intercepter les méthodes d'extension "propriétés"
/// </summary>
[Serializable]
public abstract class BasePropertyInterceptor : OnMethodBoundaryAspect
{
protected static PropertyContainer PropertyValues = PropertyContainer.GetInstance();
public override void OnEntry(MethodExecutionEventArgs e)
{
OwnerType = e.Method.GetParameters()[0].ParameterType;
Owner = e.GetArguments()[0];
if (e.GetArguments().Length > 1)
Value = e.GetArguments()[1];
}
public override void CompileTimeInitialize(MethodBase method)
{
MethodName = method.Name;
}
/// <summary>
/// Type de l'objet portant la méthode-"propriété".
/// </summary>
public Type OwnerType { get; set; }
/// <summary>
/// Instance de l'objet portant la méthode-"propriété".
/// </summary>
public object Owner { get; set; }
/// <summary>
/// Nom de la méthode-"propriété".
/// </summary>
public virtual string MethodName { get; set; }
/// <summary>
/// Valeur de la propriété.
/// </summary>
public object Value { get; set; }
[Serializable]
protected class PropertyContainer : Dictionary<Type, Dictionary<object, Dictionary<string, object>>>
{
// Design pattern singleton
private static PropertyContainer instance = null;
private PropertyContainer() { }
public static PropertyContainer GetInstance()
{
if (instance == null)
instance = new PropertyContainer();
return instance;
}
// Redéfinition d'indexeurs
public object this[Type objtype, object obj, string propertyName]
{
get
{
try { return base[objtype][obj][propertyName]; }
catch { return null; }
}
}
public Dictionary<string, object> this[Type objtype, object obj]
{
get
{
try { return base[objtype][obj]; }
catch { return null; }
}
}
}
}
/// <summary>
/// Attribut à associer à la classe contenant les méthodes d'extension. Il intercepte les méthodes d'extension "Get*"/"Set*" et renvoit/stock les valeurs.
/// </summary>
[Serializable]
public class InterceptProperties : BasePropertyInterceptor
{
private Get _Getter;
private Set _Setter;
public override void CompileTimeInitialize(MethodBase method)
{
base.CompileTimeInitialize(method);
if (method.Name.StartsWith("Get"))
{
CheckAccessorAttributes(method);
// la méthode "Get" doit être sans paramètre.
if (method.GetParameters().Length != 1)
throw new Exception(string.Format("Une méthode dont le nom commence par \"Get\" a été trouvée, mais elle possède un ou plusieurs paramètres sans modificateur \"this\".", method.Name));
var instance = method.GetParameters()[0];
// on cherche une methode portée par la même classe, de même nom et possédant un attribut "Set".
var presumedSetters = method.DeclaringType.GetMethods().Where(m => m.Name.StartsWith("Set") && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString());
if (presumedSetters.Count() == 0)
throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} dont le nom commence par \"Set\".", method.Name, instance.ParameterType));
}
if (method.Name.StartsWith("Set"))
{
CheckAccessorAttributes(method);
// on vérifie que la méthode ne possède qu'un seul paramètre.
ParameterInfo param;
if (method.GetParameters().Length != 2)
throw new Exception(string.Format("La méthode \"{0}\" définie comme accesseur \"set\" doit posséder un unique paramètre sans modificateur \"this\".", method.Name));
param = method.GetParameters()[1];
var instance = method.GetParameters()[0];
// on cherche une methode portée par la même classe, de même nom et possédant un attribut "Get".
var presumedGetters = method.DeclaringType.GetMethods().Where(m => m.Name.StartsWith("Get") && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString());
if (presumedGetters.Count() == 0)
throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} dont le nom commence par \"Get\".", method.Name, instance.ParameterType));
// la méthode "Get" doit avoir une valeur de retour du même type que le paramètre de la méthode "Set".
presumedGetters = presumedGetters.Where(m => m.ReturnType == param.ParameterType);
if (presumedGetters.Count() == 0)
throw new Exception(string.Format("Une méthode dont le nom commence par \"Get\" a été trouvée, mais le type de son paramètre de retour ne correspond pas à celui du paramètre de la méthode \"Set\".", method.Name));
}
}
protected void CheckAccessorAttributes(MethodBase method)
{
var setAttr = method.GetCustomAttributes(typeof(Set), false);
var getAttr = method.GetCustomAttributes(typeof(Get), false);
if (setAttr != null && setAttr.Length != 0)
throw new Exception(string.Format("La méthode \"{0}\" ne doit pas avoir d'attribut \"Set\" associé",method.Name));
if (getAttr != null && getAttr.Length != 0)
throw new Exception(string.Format("La méthode \"{0}\" ne doit pas avoir d'attribut \"Get\" associé",method.Name));
}
public override void OnEntry(MethodExecutionEventArgs e)
{
base.OnEntry(e);
if (e.Method.Name.StartsWith("Set"))
Setter.OnEntry(e);
}
public override void OnExit(MethodExecutionEventArgs e)
{
if (e.Method.Name.StartsWith("Get"))
Getter.OnExit(e);
}
public override string MethodName
{
get { return base.MethodName.Substring(3); }
}
// Il faut propager les infos OwnerType, Owner, MethodName et Value vers nos objets Getter et Setter car ceux-ci n'interceptent pas directement les méthodes.
private Get Getter
{
get
{
if (_Getter == null || (_Getter!=null && _Getter.Owner!=this.Owner))
_Getter = new Get
{
OwnerType = this.OwnerType,
Owner = this.Owner,
MethodName = this.MethodName,
Value = this.Value
};
return _Getter;
}
}
private Set Setter
{
get
{
if (_Getter == null || (_Getter!=null && _Getter.Owner!=this.Owner))
_Setter = new Set
{
OwnerType = this.OwnerType,
Owner = this.Owner,
MethodName = this.MethodName,
Value = this.Value
};
return _Setter;
}
}
}
/// <summary>
/// Attribut à associer à une méthode d'extension pour que celle-ci remplace un accesseur "get" d'une propriété.
/// </summary>
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true)]
public class Get : BasePropertyInterceptor
{
public override void CompileTimeInitialize(MethodBase method)
{
base.CompileTimeInitialize(method);
// la méthode "Get" doit être sans paramètre.
if (method.GetParameters().Length != 1)
throw new Exception(string.Format("Une méthode \"{0}\" portant un attribut \"Get\" a été trouvée, mais elle possède un ou plusieurs paramètres sans modificateur \"this\".", method.Name));
var instance = method.GetParameters()[0];
// on cherche une methode portée par la même classe, de même nom et possédant un attribut "Set".
var presumedSetters = method.DeclaringType.GetMethods().Where(m =>
{
if (m.Name == method.Name && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString())
{
var attrs = m.GetCustomAttributes(typeof(Set), false);
if (attrs != null && attrs.Length != 0)
return true;
}
return false;
});
if (presumedSetters.Count() == 0)
throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} portant un attribut \"Set\".", method.Name, instance.ParameterType));
}
public override void OnExit(MethodExecutionEventArgs e)
{
e.ReturnValue = PropertyValues[OwnerType, Owner, MethodName];
}
}
/// <summary>
/// Attribut à associer à une méthode d'extension pour que celle-ci remplace un accesseur "set" d'une propriété.
/// </summary>
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData=true)]
public class Set : BasePropertyInterceptor
{
public override void CompileTimeInitialize(MethodBase method)
{
base.CompileTimeInitialize(method);
// on vérifie que la méthode ne possède qu'un seul paramètre.
if (method.GetParameters().Length != 2)
throw new Exception(string.Format("La méthode \"{0}\" définie comme accesseur \"set\" doit posséder un unique paramètre sans modificateur \"this\".", method.Name));
var param = method.GetParameters()[1];
var instance = method.GetParameters()[0];
// on cherche une methode portée par la même classe, de même nom et possédant un attribut "Get".
var presumedGetters = method.DeclaringType.GetMethods().Where(m =>
{
if (m.Name == method.Name && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.ToString() == instance.ParameterType.ToString())
{
var attrs = m.GetCustomAttributes(typeof(Get), false);
if (attrs != null && attrs.Length != 0)
return true;
}
return false;
});
if(presumedGetters.Count()==0)
throw new Exception(string.Format("Imposssible de trouver une méthode \"{0}\" de {1} portant un attribut \"Get\".", method.Name, instance.ParameterType));
// la méthode "Get" doit avoir une valeur de retour du même type que le paramètre de la méthode "Set".
presumedGetters = presumedGetters.Where(m => m.ReturnType == param.ParameterType);
if (presumedGetters.Count() == 0)
throw new Exception(string.Format("Une méthode \"{0}\" portant un attribut \"Get\" a été trouvée, mais le type de son paramètre de retour ne correspond pas à celui du paramètre de la méthode \"Set\".", method.Name));
}
public override void OnEntry(MethodExecutionEventArgs e)
{
base.OnEntry(e);
if (Value == null)
return;
if (!PropertyValues.ContainsKey(OwnerType))
PropertyValues.Add(OwnerType, new Dictionary<object, Dictionary<string, object>>());
if (!PropertyValues[OwnerType].ContainsKey(Owner))
PropertyValues[OwnerType].Add(Owner, new Dictionary<string, object>());
if (!PropertyValues[OwnerType, Owner].ContainsKey(MethodName))
PropertyValues[OwnerType, Owner].Add(MethodName, Value);
}
}
}
Sources du même auteur
Sources de la même categorie
Sources en rapport avec celle ci
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
Héritage multiple [ par zigxag ]
Bonjour !je souhaite faire un héitage multiple en C#, mais je sais très bien que l'on peut pas. Mais alors, comment contourner la contrainte
[C#]Comment faire de l'héritage multiple ? [ par oberown ]
Comment faire pour qu'une classe A hérite de la classe B et C ?
Héritage multiple [ par ricklekebekoi ]
prenons trois classesBaseMorpionGame;BaseTwoPlayerGame;BaseIAGame;Ensuite, deux autres classes:SingleMorpionGame;TwoPJMorpionGame;Je souhaiterais que
migration vb6 vers c# [ par lagoelle ]
Bonjour,Je migre de VB6 à C# et des différences au niveau des controles m'étonne.Par exemple, le label en VB6 a une propriété
datagrid [ par penchu ]
bonjour, je recherche le moyen de cacher une de mes colonnes de ma datagrid en asp, ca va, je la cache dans le code html ou bien je met la proprié
Recuperer le nom d un fichier sans son extension dans un textbox [ par amirirn1 ]
Bonjour,j aimerai recuperer le nom d un fichier que j ai ouvert dans un textbox sans l extension du fichier.aaa.txt je veux textbox.Text = aaa
Protected ou private ? [ par thiosyiasar ]
Protected ou Private ? Salut à tous. En fait j'ai une petite question qui me turlupine un peu depuis quelques temps. J'ai débuté la pro
Word saveAs (changer l extension) [ par amirirn1 ]
SalutJ ai pris un element web Browser de windows que j ai ajouté , j arrive bien à avoir des pages web dessus.Mais ce que j aimerai ,c est d
C# Excel Format de cellule [ par blundle ]
Bonjour,je suis en train de créer un outil d'export permettant présenter les données d'une DB Access dans une feuille MS Excel.Le probl
APP.CONFIG et value multiple [ par winz611 ]
Bonjour,je voudrais alimenter des listbox avec un contenu issu du fichier app.config.Est il possible d'avoir plusieur values pour une seule clé ?
|
Téléchargements
Logiciels à télécharger sur le même thème :
|