Τελικά δεν άντεξα. Με έπιασαν τα στερητικά μου (ήδη από εχθές). Ως προς το θέμα μας τώρα, θα συμφωνήσω με τον Παναγιώτη Καναβό. Όντως δεν είναι απλό. Όσον αφορά στο serialization παραθέτω τα εξής από εδώ:
Objects placed on the clipboard must be serializable. Unfortunately, .NET doesn't raise an exception if you place a non-serializable object in the clipboard or data object. It includes the format, but stores null. This makes it look like the clipboard isn't working. If your object can't be serialized, consider creating a byte array and storing it in the clipboard as a memory stream. You will also have to retrieve it as a memory stream and reconstitute the object from the byte array.
Αν σε ενδιαφέρουν μόνο τα data μπορείς να τα μετατρέψεις σε CVS format που αναγνωρίζεται από την DataFormats class. Είναι η "εύκολη" λύση που σου προτείνει και ο Γιώργος Καπνιάς. Κάτι που εγώ θέλω, μιας κι έχω αδυναμία στα datasets και στα datatables, είναι να μετατρέπω τα δεδομένα σε datatable για πολλούς και διάφορος λόγους. Συν τοις άλλοις, μπορώ να τα κάνω copy-paste προς και από τον clipboard με xmlserialization. Ο κώδικας που παραθέτω πιο κάτω είναι διερευνητικός. Αν κάποιος ενδιαφέρεται να κάνει προτάσεις βελτίωσης, βεβαίως και είναι ευπρόσδεκτες.
Αν και το θεωρώ περιττό, πρέπει να αναφέρω ότι για να λειτουργήσει ο κώδικας χρειάζονται τα παρακάτω statements:
using
System.Reflection;
using System.Xml.Serialization;
using
System.IO;
Σε μία κενή φόρμα έβαλα ένα datagridview και δύο buttons (btnCopy και btnPaste). Πρόσθεσα ένα LinqToSql Classes και στον designer έκανα drag n' drop ένα πίνακα από βάση δεδομένων με test data. Τα ονόματα είναι της δικής μου βάσης και πιστεύω ότι δε θα δημιουργήσουν πρόβλημα κατανόησης. Στα αντίστοιχα button events πρόσθεσα τον παρακάτω κώδικα. Νομίζω ότι τα σχόλια είναι κατανοητά και δε χρειάζεται περαιτέρω επεξήγηση.
private void btnCopy_Click(object sender, EventArgs e)
{
// Καθάρισε το DataSource για checking...
dataGridView1.DataSource = null;
//
// Όρισε το ling query
//
VSFDataClassesDataContext db = new VSFDataClassesDataContext();
var femData = from female in db.FemaleDatas
select female;
//
// Πάρε το mapping του row
//
System.Data.Linq.Mapping.MetaType rowType=db.Mapping.GetTable(typeof(FemaleData)).RowType;
//
// Καθόρισε τις ιδιότητες των DataColumns και δημιούργησέ τα
//
DataColumn[] columns = new DataColumn[rowType.DataMembers.Count];
//
int i = 0;
foreach (var member in rowType.DataMembers)
{
string memberName = member.Name; // το όνομα του column
string memberDbType = member.DbType;
bool canBeNull = member.CanBeNull;
//
Type t = typeof(string); // Default Type
switch (memberDbType.ToUpper())
{
case "SMALLDATETIME":
t = typeof(DateTime);
break;
case "DATETIME":
t = typeof(DateTime);
break;
case "SMALLINT":
t = typeof(short);
break;
case "INT":
t = typeof(int);
break;
case "FLOAT":
t = typeof(double);
break;
}
columns[ i ] = new DataColumn(memberName);
columns[ i ].DataType = t;
columns[ i ].AllowDBNull = canBeNull;
i++;
//
}
//
// Δημιούργησε το DataTable
//
DataTable table = new DataTable();
table.TableName = "Females";
table.Columns.AddRange(columns);
//
// Linq... η χαρά του Reflection
//
foreach (FemaleData f in femData)
{
PropertyInfo[] propInfo = f.GetType().GetProperties();
DataRow dr = table.NewRow();
for (i = 0; i < propInfo.Length; i++)
{
dr.BeginEdit();
if (propInfo[ i ].GetValue(f, null) != null)
{
string typeStr = propInfo[ i ].PropertyType.ToString();
if (typeStr.Contains("Nullable"))
{
int fIdx = typeStr.IndexOf("[");
int lIdx = typeStr.IndexOf("]");
typeStr = typeStr.Substring(fIdx+1, lIdx - fIdx-1);
}
switch (typeStr)
{
case "System.String":
dr[propInfo[ i ].Name] = propInfo[ i ].GetValue(f, null).ToString();
break;
case "System.Int16":
dr[propInfo[ i ].Name] = (short)propInfo[ i ].GetValue(f, null);
break;
case "System.Int32":
dr[propInfo[ i ].Name] = (int)propInfo[ i ].GetValue(f, null);
break;
case "System.DateTime":
dr[propInfo[ i ].Name] = (DateTime)propInfo[ i ].GetValue(f, null);
break;
case "System.Double":
dr[propInfo[ i ].Name] = (Double)propInfo[ i ].GetValue(f, null);
break;
}
//
// Αυτό δουλεύει, αλλά...
// dr[propInfo[ i ].Name] = propInfo[ i ].GetValue(f, null); ...AutoUnboxing ΜΠΛΙΑΑΑΑΧ!!!
//
}
else
{
dr[propInfo[ i ].Name] = System.DBNull.Value;
}
dr.EndEdit();
}
table.Rows.Add(dr);
}
table.AcceptChanges();
//
// Serialize το DataTable και βάλτο στον ClipBoard
//
Stream stream = new MemoryStream();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(DataTable));
xmlSerializer.Serialize(stream, table);
Clipboard.Clear();
Clipboard.SetDataObject(stream);
//
MessageBox.Show("Τα δεδομένα βρίσκονται στον Clipboard...");
}
και
private void btnPaste_Click(object sender, EventArgs e)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(DataTable));
DataObject dObj = (DataObject)Clipboard.GetDataObject();
Stream stream = dObj.GetData(typeof(MemoryStream)) as Stream;
DataTable newTable = xmlSerializer.Deserialize(stream) as DataTable;
Clipboard.Clear();
//
dataGridView1.DataSource = newTable;
}
Τα παραπάνω δουλεύουν στο παράδειγμά μου και, αν δεν έχω κάνει καμιά πατάτα στο copy - paste πρέπει να δουλέψουν και σε σένα. Βέβαια, ο κώδικας είναι ατελής μιας και δεν περιλαμβάνονται όλα τα datatypes παρά μόνο εκείνα που χρησιμοποίησα στον πίνακα. Όπως ανέφερα και παραπάνω, προτάσεις βελτίωσης είναι ευπρόσδεκτες.
Τώρα αν δε σε ικανοποιεί αυτό μπορείς να χρησιμοποιήσεις LinqToSql serialization. Ρίξε μόνο μια ματιά σ' αυτό το άρθρο. Επίσης στο CodeProject βρήκα αυτήν τη library. Δεν την έχω χρησιμοποιήσει, αλλά μπορείς να το κάνεις εσύ και να μας δώσεις feedback. Αξίζει ακόμα να ρίξεις μια ματιά στην XmlSerializer class. Ένα λεπτό σημείο που αναφέρεται εκεί είναι το:
The XmlSerializer cannot serialize or deserialize the following:
Ελπίζω να βοήθησα.
Σημ.: Συγνώμη για τα [ i ], αλλά στην προεπισκόπηση, χωρίς τα κενά, μου έβγαζε... γλόμπους!
Ακόμα κι ένας άνθρωπος μπορεί ν' αλλάξει τον κόσμο. Μη θέλεις να κυβερνήσεις. Απλά δείξε το μονοπάτι κι ο κόσμος θ' ακολουθήσει!!