Reputation: 63317
I don't know why the method GetData() requires a parameter of Type, I thought the Type is to specify that of which class/type the object should be. I have a structure called GraphicsPathWrap, it's made serializable by implementing ISerializable. I tried the following copy function:
private void Copy(GraphicsPathWrap gpw){
DataObject obj = new DataObject();
obj.SetData(typeof(GraphicsPathWrap), gpw);
Clipboard.SetDataObject(obj);
}
Then tried the following paste function:
private GraphicsPathWrap Paste()
{
return (GraphicsPathWrap)Clipboard.GetDataObject().GetData(typeof (GraphicsPathWrap));
}
It should work, but the GetData(...) returns an object of type MemoryStream and an InvalidCastException was thrown. I don't understand why it's type of MemoryStream. I thought it should be able to be casted to GraphicsPathWrap? I can work around this by using a BinaryFormatter to deserialize the MemoryStream but it's too ridiculous when Clipboard can't do all the thing for me?
Thanks!
Upvotes: 1
Views: 2685
Reputation: 56083
I'm using Clipboard.GetData
with an object of type SerializedClipboardFragment
-- which is a type of my own, a class, which I've marked with the [Serializable()]
attribute:
[Serializable()]
public class SerializedClipboardFragment
{
readonly string[] m_ancestors;
readonly string m_fragment;
readonly int m_numberOfNodes;
readonly bool m_isInsertingBlock;
readonly bool m_isInsertingTable;
public SerializedClipboardFragment(
string[] ancestors,
string fragment,
int numberOfNodes,
bool isInsertingBlock,
bool isInsertingTable
)
{
m_ancestors = ancestors;
m_fragment = fragment;
m_numberOfNodes = numberOfNodes;
m_isInsertingBlock = isInsertingBlock;
m_isInsertingTable = isInsertingTable;
}
internal static DataFormats.Format s_format;
static SerializedClipboardFragment()
{
s_format = DataFormats.GetFormat(typeof(SerializedClipboardFragment).FullName);
}
... etc -- various get-only properties ...
}
My code to get this data from the clipboard looks like this:
public static SerializedClipboardFragment getSerializedFragment()
{
if (!Clipboard.ContainsData(SerializedClipboardFragment.s_format.Name))
{
return null;
}
object o = Clipboard.GetData(SerializedClipboardFragment.s_format.Name);
return (SerializedClipboardFragment)o;
}
When I test this I find that it usually works.
But it sometimes fails in my automated regression-testing, where I try to get data from the clipboard immediately after setting it ... e.g. it may fail after 30 successive successful set-then-get attempts.
If it fails it's as you describe, i.e. the object is a MemoryStream
.
Note that it shouldn't fail because Clipboard.ContainsData
recently returned true
.
I realise that there's only one system clipboard; when running this test my hands are off the keyboard, I'm not trying to interfere with the clipboard myself while this is happening.
Anyway, the problem seems to go away if I write the code as follows:
public static SerializedClipboardFragment getSerializedFragment()
{
if (!Clipboard.ContainsData(SerializedClipboardFragment.s_format.Name))
{
return null;
}
// sometimes return MemoryStream object instead under automated-testing
// possibly a timing problem
object o = null;
for (int i = 0; i < 10; ++i)
{
o = Clipboard.GetData(SerializedClipboardFragment.s_format.Name);
if (o is SerializedClipboardFragment)
return (SerializedClipboardFragment)o;
System.Threading.Thread.Sleep(100);
}
return (SerializedClipboardFragment)o;
}
So unless I'm mistaken this may be some revolting timing bug, associated with storing custom data types in the clipboard.
Upvotes: 1
Reputation: 1
[DataObject(true)]
public class EmployeeDAL
{
[DataObjectMethod(DataObjectMethodType.Update, true)]
public int UpdateEmployeeSalary(int percentage, int deptid, int posid)
{
OracleConnection con = new OracleConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
OracleCommand cmd = new OracleCommand("GIVE_RAISE_SP", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("PV_PERCENTAGE_RAISE_I", OracleType.Int32).Value = percentage;
cmd.Parameters.AddWithValue("PV_POSITIONID_I", OracleType.Int32).Value = posid;
stac cmd.Parameters.AddWithValue("PV_DEPTID_I", OracleType.Int32).Value = deptid;
cmd.Parameters.AddWithValue("PV_NUM_EMPLOYEES_O", OracleType.Int32).Direction = ParameterDirection.Output;
OracleDataAdapter da = new OracleDataAdapter(cmd);
try
{
con.Open();
da.UpdateCommand = cmd;
cmd.ExecuteNonQuery();
}
catch (Exception)
{
}
finally
{
con.Close();
}
return Convert.ToInt32(cmd.Parameters["PV_NUM_EMPLOYEES_O"].Value);
}
Upvotes: 0
Reputation: 2673
EDIT : I've exactly simulated your situation, the thing is which was saying MemoryStream when you have implemented the ISerializable Interface and hadn't deserialized it properly.
The GetData() Returns Memory Stream in this below scenario :
[Serializable]
public struct GraphicsPathWrap : ISerializable
{
private static string myValue = "This is the value of the class";
// Creates a property to retrieve or set the value.
public string MyObjectValue
{
get
{
return myValue;
}
set
{
myValue = value;
}
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
#endregion
}
Then, I implemented serialization\deserialization properly when GetData() gives the correct type object
[Serializable]
public struct GraphicsPathWrap : ISerializable
{
private static string myValue = "This is the value of the class";
public GraphicsPathWrap(SerializationInfo info, StreamingContext ctxt) // Deserialization Constructor
{
myValue = (string)info.GetValue("MyValue", typeof(string));
}
// Creates a property to retrieve or set the value.
public string MyObjectValue
{
get
{
return myValue;
}
set
{
myValue = value;
}
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("MyValue", myValue); // Serialize the value
}
#endregion
}
I hope the above answer will help you
Upvotes: 3
Reputation: 1518
The Clipboard.SetDataObject(object data) method name is a bit misleading since it is not specifically asking for a DataObject as a parameter, but just an Object which must be Serializable.
You could try passing gpw directly like this:
private void Copy(GraphicsPathWrap gpw){
Clipboard.SetDataObject(gpw);
}
It should work if GraphicsPathWrap is serializable.
EDIT: After testing myself, it turns out that the method works both ways, either passing the Serializable object directly or encapsulating it in a DataObject. I confirmed it by checking the .Net source code for that particular Clipboard method, where I found this:
if (data is DataObject)
{
dataObject = (DataObject)data;
}
So, as Ramesh says in the other answer, you may want to check if your object is properly set as Seriaizable.
Upvotes: 1