1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace ExcelDataGrid
{
/// <summary>
/// DataGridView évoluée, qui reproduit un comportement proche d'Excel en ce qui concerne la saisie des nombre
/// </summary>
public partial class ExcelDataGrid : DataGridView
{
private bool isDecimalPressed = false;
private char decimalCharacter;
/// <summary>
/// Instancie une nouvelle grille reproduisant le comportement d'Excel
/// </summary>
public ExcelDataGrid() : this(5, 10)
{
}
public ExcelDataGrid(int nbColumns, int nbRows)
{
string name;
for (int i = 0; i < nbColumns; i++)
{
name = IntToExcelColumn(i + 1);
this.Columns.Add(name, name);
}
this.Rows.Add(nbRows);
string decimalSeparator = Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator;
if (decimalSeparator.Length != 1)
{
throw new Exception("Séparateur des décimales invalide : vous devez utiliser un séparateur qui utilise un et un seul caractère.");
}
decimalCharacter = decimalSeparator[0];
InitializeComponent();
this.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(ExcelDataGrid_Editing);
this.CellBeginEdit += new DataGridViewCellCancelEventHandler(ExcelDataGrid_CellBeginEdit);
this.CellEndEdit += new DataGridViewCellEventHandler(ExcelDataGrid_CellEndEdit);
this.DataError += new DataGridViewDataErrorEventHandler(ExcelDataGrid_DataError);
}
private void ExcelDataGrid_Editing(object sender, DataGridViewEditingControlShowingEventArgs args)
{
args.Control.KeyDown += new KeyEventHandler(Control_KeyDown);
args.Control.KeyPress += new KeyPressEventHandler(Control_KeyPress);
args.Control.KeyUp += new KeyEventHandler(Control_KeyUp);
}
private void Control_KeyDown(object sender, KeyEventArgs args)
{
if (args.KeyData == Keys.Decimal)
{
isDecimalPressed = true;
}
}
private void Control_KeyUp(object sender, KeyEventArgs args)
{
isDecimalPressed = false;
}
private void Control_KeyPress(object sender, KeyPressEventArgs args)
{
if (args.KeyChar == '.' && isDecimalPressed)
{
args.KeyChar = decimalCharacter;
}
}
private void ExcelDataGrid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs args)
{
DataGridViewCell cell = (sender as DataGridView)[args.ColumnIndex, args.RowIndex];
cell.ErrorText = string.Empty;
}
private void ExcelDataGrid_CellEndEdit(object sender, DataGridViewCellEventArgs args)
{
DataGridViewCell cell = (sender as DataGridView)[args.ColumnIndex, args.RowIndex];
if (cell.ValueType.Name.ToLower() == "object")
{
double val = 0;
if (double.TryParse((string)cell.Value.ToString(), out val))
{
cell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
}
}
}
private void ExcelDataGrid_DataError(object sender, DataGridViewDataErrorEventArgs args)
{
DataGridViewCell cell = (sender as DataGridView)[args.ColumnIndex, args.RowIndex];
cell.Style.Alignment = DataGridViewContentAlignment.MiddleLeft;
cell.ErrorText = args.Exception.Message;
args.Cancel = false;
}
/// <summary>
/// Permet de déterminer pour un indice donnée, la notation "colonne Excel"
/// </summary>
/// <param name="i">Indice pour lequel on veut déterminer le nom de "colonne Excel"</param>
/// <returns>Nom de "colonne Excel"</returns>
public static string IntToExcelColumn(int i) /* rajout du this pour méthode d'extension */
{
// Vérification de la validité du paramètre
if (i < 1)
{
throw new Exception("L'indice d'une colonne Excel est strictement suppérieur à 1");
}
// Un StringBuilder est ce qu'il y a de plus rapide en ce qui concerne
// la manipulation de chaîne de caractères, même si dans le cas de chaînes
// de petite taille, c'est certainement inutile
StringBuilder sb = new StringBuilder();
while (i > 0)
{
// Astuce de notation. Il ne faut pas trop chercher à comprendre...
// Gardez juste en tête qu'il ne s'agit pas d'un simple changement de base.
sb.Insert(0, (char)('A' + ((i-- - 1) % 26))); /* remplacement de Append par Insert(0, ... en début donc */
i /= 26;
}
return sb.ToString(); /* grâce à l'Insert plus besoin de reverse ici */
}
/* plus simple (et sans doute plus performant) qu'une regex
* on pourrait carrément la mettre dans la méthode je pense) */
private const string allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/// <summary>
/// Permet de déterminer l'indice d'un nom de "colonne Excel"
/// </summary>
/// <param name="s">Nom de "colonne Excel"</param>
/// <returns>Indice de la "colonne Excel"</returns>
public static int ExcelColumnToInt(string s)
{
s = s.ToUpperInvariant(); /* des fois que la chaine passée soit en minuscules */
// Vérification que le nom de la colonne ne contient que des caractères autorisés
/* remplacement de la regex ;
* littéralement: au moins 1 caractère dans s n'est pas contenu dans ceux autorisés (perso je trouve ça propre) */
if (s.Any(c => !allowedChars.Contains(c)))
{
throw new Exception("Une colonne Excel ne comporte que des caractères de A à Z");
}
int ret = 0, currentIndex, lastIndex;
currentIndex = lastIndex = s.Length - 1; /* initialisations multiples */
while (currentIndex >= 0)
{
// Idem que pour la fonction IntToExcelColumn : il ne s'agit pas d'un
// simple changement de base. Les "erreurs" sont volontaires.
ret += (s[currentIndex] - 'A' + 1) * (int)Math.Pow(26, lastIndex - currentIndex--);
}
// On n'a pas réussi à déterminer l'indice de la colonne.
if (ret == 0)
{
throw new Exception(string.Format("Impossible de déterminer l'indice de la colonne '{0}'.", s));
}
return ret;
}
}
} |
Partager