I am struggling with using clipboard to copy / paste object, so I created a very simple example to demonstrate the issue.
What is very frustrating is that the same code was working earlier and stopped recently and I am unable to figure out what is wrong.
Basically, the problem is that dataObject.GetData() always returns null even if dataObject.GetDataPresent() returned true earlier.
I am running on .Net 4.5.
using System;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var a = new TestClass();
a.Name = "Test";
a.Index = 1;
a.Live = true;
IDataObject dataObj = new DataObject();
// Method 1 : Not working
//dataObj.SetData(a);
// Method 2 : also not working
DataFormats.Format format = DataFormats.GetFormat(a.GetType().FullName);
dataObj.SetData(format.Name, false, a);
Clipboard.SetDataObject(dataObj, false);
}
private void button2_Click(object sender, EventArgs e)
{
IDataObject dataObject = Clipboard.GetDataObject();
// Method 1 : Not working
//if (dataObject.GetDataPresent(typeof(TestClass)))
//{
// // Issue => retrievedObj is ALWAYS null
// var retrievedObj = dataObject.GetData(typeof(TestClass));
//}
// Method 2 : also not working
if (dataObject.GetDataPresent(typeof(TestClass).FullName))
{
// Issue => retrievedObj is ALWAYS null
var retrievedObj = dataObject.GetData(typeof(TestClass).FullName);
}
}
}
public class TestClass
{
public string Name;
public int Index;
public bool Live;
}
}
Any ideas please ?
I am answering my own question to share my experience.
To keep a long story short, in the original code I wanted to copy / paste an object that was referencing an type (XmlFont, a wrapper type I created to allow serialization of Font type) which was not explicitly marked with Serializable attribute. The funny part though, is that this object was successfully serialized to / from a file using XmlSerializer, so this part is still unclear for me. But marking the XmlFont type as Serializable instantly solved the problem.
Related
I'm trying to pass a value(an element id) from a WinForm back to the Command.cs file but I'm getting an error:
System.NullReferenceException: Object reference not set to an instance of an object.
at BatchSheetMaker.Command.Execute(ExternalCommandData commandData, String& message, ElementSet elements)
I'm following the youtube tutorial here and it seems fairly easy and straight forward but passing back to the Command.cs is another layer of complexity.
I have the Command.cs code wrapped in a try/catch block which just tells me that there's nullReferenceException however it doesn't tell me which line it's occurring at. I've looked around but havn't found any tips on how to make the debug show the error line. If anyone has any other pointers, that'd be helpful.
Form1.cs
public partial class Form1 : System.Windows.Forms.Form
{
private UIApplication uiapp;
private UIDocument uidoc;
private Autodesk.Revit.ApplicationServices.Application app;
private Document doc;
private string myVal;
public string MyVal
{
get { return myVal; }
set { myVal = value; }
}
public Form1(ExternalCommandData commandData)
{
InitializeComponent();
uiapp = commandData.Application;
uidoc = uiapp.ActiveUIDocument;
app = uiapp.Application;
doc = uidoc.Document;
}
public delegate void delPassData(System.Windows.Forms.ComboBox text);
private void Form1_Load(object sender, EventArgs e)
{
//Create a filter to get all the title block types.
FilteredElementCollector colTitleBlocks = new FilteredElementCollector(doc);
colTitleBlocks.OfCategory(BuiltInCategory.OST_TitleBlocks);
colTitleBlocks.WhereElementIsElementType();
foreach(Element x in colTitleBlocks)
{
comboBox1TitleBlockList.Items.Add(x.Name);
}
}
private void button1Continue_Click(object sender, EventArgs e)
{
MyVal = comboBox1TitleBlockList.Text;
}
Command.cs
Form1 form1 = new Form1(commandData);
String elementString = form1.MyVal.ToString();
Element eFromString = doc.GetElement(elementString);
ElementId titleBlockId = eFromString.Id;
ViewSheet sheet = ViewSheet.Create(doc, titleBlockId);
Run your entire add-in inside the Visual Studio debugger and step through your code line by line. That will show you exactly where the exception is thrown and enable you to easily identify what is causing the problem.
Changed my code to this and it started working:
form1.cs
public string MyVal;
//{
//get { return myVal; }
//set { myVal = value; }
//}
this link was helpful along with tutorials on youtube on how to pass values from form to form.
So I'm trying to copy and paste an object and having trouble getting it right. I've searched through the topics but I still can't seem to get it to work. Here is the code:
In one solution in Visual studio I have the the class:
namespace test4
{
[Serializable]
public class copypaste
{
public string test = "a";
}
}
and the copy part of the code:
private void btn1_Click(object sender, EventArgs e)
{
var copy_obj = new copypaste();
DataObject d = new DataObject(copy_obj);
Clipboard.SetDataObject(d);
}
And in another solution I have:
namespace test4
{
[Serializable]
public class copypaste
{
public string test = "a";
}
}
and the paste part of the code:
private void btnTest_Click(object sender, EventArgs e)
{
var d = Clipboard.GetDataObject();
if (d.GetDataPresent("test4.copypaste"))
{
var o = d.GetData("test4.copypaste");
Debug.WriteLine( ( (copypaste)o ).test );
}
}
However, I end up with the following error on the final line:
'System.InvalidCastException: 'Unable to cast object of type 'System.IO.MemoryStream' to type 'test4.copypaste'.'
I have gone through other questions which suggest this way of copy/pasting code but none seem to return memory stream when they call the GetData method. I am unsure how to extract the object from the memory stream.
Thanks
With this reference in mind and with your serializable class, this works as expected:
private void copyButton_Click(object sender, EventArgs e)
{
DataFormats.Format myFormat = DataFormats.GetFormat("test4.copypaste");
var copy_obj = new copypaste();
DataObject myDataObject = new DataObject(myFormat.Name, copy_obj);
Clipboard.SetDataObject(myDataObject);
}
private void pasteButton_Click(object sender, EventArgs e)
{
var d = Clipboard.GetDataObject();
if (d.GetDataPresent("test4.copypaste"))
{
var o = d.GetData("test4.copypaste");
Debug.WriteLine(((copypaste)o).test);
}
}
I have 2 Forms: V.Batch and V.BatchEdit and a Class: M.Batch
In V.Batch there is a DataGrid. I want to pass the value I get from the DataGrid to V.BatchEdit and the get set method is in M.Batch.
The problem here is that the value isn't passed properly in V.BatchEdit. It returns 0.
Here is the code
V.Batch:
M.Batch bt;
public Batch()
{
bt = new M.Batch();
InitializeComponent();
}
private void metroButton3_Click_1(object sender, EventArgs e)
{
bt.batchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
V.BatchEdit bEdit = new V.BatchEdit();
this.Hide();
bEdit.Show();
}
M.Batch:
public int batchNum;
public int BatchNum
{
set { batchNum = value; }
get { return batchNum; }
}
V.BatchEdit
static M.Batch bt = new M.Batch();
DataSet a = bt.getBatch(bt.batchNum);
public BatchEdit()
{
db = new Database();
InitializeComponent();
System.Windows.Forms.MessageBox.Show(bt.batchNum.ToString() + "Batchedit");
try
{
metroTextBox1.Text = a.Tables[0].Rows[0][2].ToString();
}
catch (Exception exceptionObj)
{
MessageBox.Show(exceptionObj.Message.ToString());
}
}
I'm new to coding and c#. I'm not sure if I placed static even though it should not be static or what.
Yes, you are using static improperly here.
The easiest way to see what's going wrong is to notice that you are calling new M.Batch() twice. That means you have two different instances of M.Batch in your application. And nowhere in your code do you attempt to share those instances.
What you should be doing is passing your instance of M.Batch from one form to another, e.g. in the constructor:
// V.Batch
bt.batchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
V.BatchEdit bEdit = new V.BatchEdit(bt);
this.Hide();
bEdit.Show();
// V.BatchEdit
private M.Batch bt;
private DataSet a;
public BatchEdit(M.Batch batch)
{
this.bt = batch;
this.a = this.bt.getBatch(bt.batchNum)
// Rest of your code here.
}
If You don't need the 'M.Batch' class for something else and you only use it for passing the value to V.BaychEdit, just declare a public property in V.BatchEdit like you did in M.Batch and use it like this:
V.BatchEdit bEdit = new V.BatchEdit();
bEdit.BatchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
Your problem is that although your using statics you're still assigning a new instance to the static field.
I am currently working with a WCF application. I must display the data received from callback in a DataGridView. These are my codes:
From FrmMain form:
private void button1_Click(object sender, EventArgs e)
{
InstanceContext callbackInstance = new InstanceContext(new StockExchangeUpdates());
SubscribingClient.RegisterSubscriberServiceClient proxy = new SubscribingClient.RegisterSubscriberServiceClient(callbackInstance);
proxy.RegisterSubscriber(Guid.NewGuid());
}
Class StockExchangeUpdates
[CallbackBehavior(UseSynchronizationContext = false)]
public class StockExchangeUpdates : IRegisterSubscriberServiceCallback
{
int ctr = 0;
FrmMain main = new FrmMain();
public void passGeneratedNumber(int num)
{
try
{
ctr = ctr + 1;
main.dgRandom.Rows.Add(DateTime.Now.ToString("h:mm:ss"), num, ctr);
// this is not working..
// Error: "dgRandoms" is inaccessible due to its protection level
}
catch (Exception)
{
throw;
}
}
}
I got use to try different ways but it is still not working. Please help! Thanks a lot!
I think you might need to set a different "Modifier" on your DataGridView.
That is, in the properties window for "dgRandom", change the property "Modifiers" to "Public" (or "Internal", if that is enough). If it is set to "Private", it will only be "seen" from within the same class.
(Edit: I assume you are using Windows forms and not WPF)
I have a C# .dll that is envoked from within a C# application useing "System.Reflection" at runtime. The .dll contains a WinForm class which is used to display information to the user. The .dll is envoked using the following code:
DLL = Assembly.LoadFrom(strDllPath);
classType = DLL.GetType(String.Format("{0}.{0}", strNsCn));
classInst = Activator.CreateInstance(classType, paramObj);
Form dllWinForm = (Form)classInst;
dllWinForm.ShowDialog();
Now, my problem is now that I want to return a string from the WinForm .dll. This could be an error or just to show that the process completed correctly. I know how this is done when calling a method from within a requested .dll, as follows:
System.Reflection.Assembly LoadedAssembly = System.Reflection.Assembly.Load("mscorlib.dll");
System.Console.WriteLine(LoadedAssembly.GetName());
object myObject = LoadedAssembly.CreateInstance("System.DateTime", false, BindingFlags.ExactBinding, null, new Object[] {2000, 1, 1, 12, 0, 0}, null, null);
MethodInfo m = LoadedAssembly.GetType("System.DateTime").GetMethod("ToLongDateString");
string result = (string) m.Invoke(myObject, null);
but how do you do this for my case of a WinForm called from a .dll at runtime?
Any suggestions would be most appreciated.
Okay, so to boil the question and the comments down, what we're trying to do here is have a C# app that loads a dll that was implemented by a 3rd party at a later date, and the app needs to get some status information from the component in the loaded dll (the fact that the component uses WinForms vs. some other UI seems completely inconsequential).
The best way to do it is to start out with an interface or base class that can be shared between the hosting application and the loaded component. In order to achieve this, the interface needs to be in a separate dll. So first we create a class library project and add the following class:
using System;
using System.Windows.Forms;
namespace SimplePluginShared
{
public class PluginBase : Form
{
public virtual String GetStatus()
{
return null;
}
}
}
Then add a reference to that class library from the project that implements the component you're loading via reflection (or share it with your third party for them to implement). Here is an example implementation of Plugin Base:
using System;
using System.Windows.Forms;
using SimplePluginShared;
namespace SimplePluginExample
{
public partial class MyForm : PluginBase
{
private String _status = "Unspecified";
public MyForm()
{
InitializeComponent();
}
public override string GetStatus()
{
return _status;
}
private void btnGive_Click(Object sender, EventArgs e)
{
_status = "Give Him The Stick.";
this.DialogResult = DialogResult.OK;
this.Close();
}
private void btnDontGive_Click(object sender, EventArgs e)
{
_status = "Don't Give Him The Stick!";
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}
}
And lastly the code to load and call the component:
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using SimplePluginShared;
namespace SimplePluginHost
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void btnBrowse_Click(Object sender, EventArgs e)
{
OpenFileDialog openPluginDlg = new OpenFileDialog() { DefaultExt = "dll", Multiselect = false, Title = "Open Plugin DLL", Filter = "DLLs|*.dll" };
if (openPluginDlg.ShowDialog() == DialogResult.OK)
{
txtPluginPath.Text = openPluginDlg.FileName;
}
}
private void btnGo_Click(Object sender, EventArgs e)
{
Assembly pluginDll = Assembly.LoadFrom(txtPluginPath.Text);
Type pluginType = pluginDll.GetTypes().Where(t => typeof(PluginBase).IsAssignableFrom(t)).First();
PluginBase pluginInstance = (PluginBase)Activator.CreateInstance(pluginType);
pluginInstance.ShowDialog();
MessageBox.Show(pluginInstance.GetStatus());
}
}
}
He are some screenshots:
Why can't you add the dll as a reference to your project and call it that way? (Just like any other assembly?)