I'm working on a plugin application. I have a frmDatasheet.cs (backend) and it's DatasheetPlugin.cs (frontend). I am working on a broadcast event between this datasheet and a model plugin, so that if someone goes back to the datasheet, makes some changes, and then goes back to modeling, modeling will be know of the new state and update itself.
The problem I'm having is the call to Broadcast is in the frmDatasheet, which goes to the datasheetPlugin to Raise the Broadcast Request, and I'm getting nulls because I'm leaving the plugin then coming back to it and everything is lost in that back and forth. Here's my code:
//in the frmDatasheet.cs, click GoToModeling, this is last few lines
IDictionary<string, object> packedState = new Dictionary<string, object>();
packedState = PackState(); <----packs up state to send
frmState.Broadcast(packedState); <----- had to instantiate new Plugin to get at .Broadcast
at the top of frmDataSheet.cs
private DatasheetPlugIn frmState = new DatasheetPlugIn();
Is this possibly the problem?? getting new DatasheetPlugin..does that clear it out, but how else can I get at the .Broadcast?
Here's my code in the DatasheetPlugin.cs
public void Broadcast(IDictionary<string,object> packedState)
{
signaller.RaiseBroadcastRequest(packedState);
}
I don't get an error, but the signaller shows the events (BroadcastState, ProjectOpened, ProjectSaved), but they have all null values. Then it goes to the signaller, checks to see if BroadcastState has any subscribers, fails because signaller is null.
How do I ensure that when I get back to the Plugin from the frmDatasheet, everything is still intact? If I put the call to .Broadcast locally in the plugin on some click event, the signaller is not null. So, I'm pretty sure its due to the back and forth and not preserving information.
Update: I should probably include code related to the signaller. Each plugin has:
private VBTools.Signaller signaller; //declared at top of plugin
//This function imports the signaller from the VBProjectManager
//Happens when app loads for each plugin.
[System.ComponentModel.Composition.Import("Signalling.GetSignaller", AllowDefault = true)]
public Func<VBTools.Signaller> GetSignaller
{
get;
set;
}
public void OnImportsSatisfied()
{
//If we've successfully imported a Signaller, then connect its events to our handlers.
signaller = GetSignaller();
signaller.BroadcastState += new VBTools.Signaller.BroadCastEventHandler<VBTools.BroadCastEventArgs>(BroadcastStateListener);
signaller.ProjectSaved += new VBTools.Signaller.SerializationEventHandler<VBTools.SerializationEventArgs>(ProjectSavedListener);
signaller.ProjectOpened += new VBTools.Signaller.SerializationEventHandler<VBTools.SerializationEventArgs>(ProjectOpenedListener);
this.MessageSent += new MessageHandler<VBTools.MessageArgs>(signaller.HandleMessage);
}
Thanks for any insight!!
When you instantiate a new DatasheetPlugin class that has this line:
private VBTools.Signaller signaller; //declared at top of plugin
The "signaller" object will be null until you instantiate it, which appears you do so when calling OnImportsSatisfied(). So when you say that you are calling...
private DatasheetPlugIn frmState = new DatasheetPlugIn();
...at the top of the file, if you never call "OnImportsSatisfied()" the signaller will be null.
It may be that you are getting confused between class member variables and static variables. http://msdn.microsoft.com/en-us/library/79b3xss3(v=vs.80).aspx
To solve this problem, I put the GoToModeling click event in the DSPlugin (was in frmDSheet).
void btnGoToModeling_Click(object sender, EventArgs e)
{
IDictionary<string, object> packedState = new Dictionary<string, object>();
packedState = _frmDatasheet.PackState();
Broadcast(packedState);
}
Then the packedState is populated with the current state of the datasheet to be sent to the Broadcast() which is in the same DSPlugin, so I don't lose anything going back and forth between the 2 classes. I took what I needed from the GoToModeling click method in the frmDSheet and moved it up to the PackState so that I can grab that too before broadcasting.
Related
I have a program that runs in the system tray that communicates with our server and "syncs" data based on a users preferenced jobs. The idea is similar to Dropbox, but for our surveying software called 12d Synergy. The idea is that users can sync data without needing to navigate through the softwares Client.
I want to add the functionality so that when the program is syncing, the icon in the system tray changes to indicate that its still syncing, but i can't figure out how to get access to the original object within the portion of the program where the event is located.
My program stucture is as follows (in c#):
Program.cs
using (ProcessingIcon pi = new ProcessingIcon())
{
pi.SetIcon(Resources._12d);
pi.Display();
Application.Run();
}
ProcessingIcon.cs
NotifyIcon ni;
public void SetIcon(Icon path)
{
ni.Icon = path;
}
public void Display()
{
ni.Text = "Sunrise Surveying 12d Synergy Sync Tool";
ni.Visible = true;
ni.ContextMenuStrip = new ContextMenus().Create();
}
ContextMenus.cs
public ContextMenuStrip Create()
{
// Sync Now
item = new ToolStripMenuItem();
item.Text = "Sync Now";
item.Click += new EventHandler(syncNow_Click);
item.Image = Resources.Sync.ToBitmap();
cms.Items.Add(item);
}
void syncNow_Click(object sender, EventArgs e)
{
string[] jobs = Sync.GetSharedFiles();
string[] files = Sync.GetDataToSync(jobs);
Sync.SyncData(files);
}
What i want to happen, is in the syncNow_click, call the ProcessingIcon.SetIcon() to change the icon, but i can't figure out how to get access to an object that exists 3 layers up in the program.
I should note that i am not a programmer, i'm a surveyor with an interest in programming. I am completely self taught, so i know there is probably something relatively simple i'm missing. This is also my first post in StackOverflow, so i'm not 100% how to use this site to the full capability, so if this has been answered somewhere i apologise.
Any help or advice would be greatly appreciated.
So i worked out a way to answer my own question. Just putting it here in case anyone has the same issue. It turned out to be incredibly simple, and purely just by me not fully understanding the classes/objects structure.
I added a constructor for my ContextMenus object which passed in the the NotifyIcon that was calling it. This was passed to a NotifyIcon variable in that class which i could then access.
class ContextMenus
{
public NotifyIcon ni;
public ContextMenus(NotifyIcon ni)
{
this.ni = ni;
}
}
i have a DataStruct[] that gets used multiple times on a form. It is created from reading a CSV file. The structure has 5 "columns", each with about 100,000 rows
in particular, i have a bunch of ChangeEvents (trackbars, textboxes, etc.), where each change event is re-making the data structure, but i feel it's slowing down the changes (they are rendering on a graph, and it's slow to react)
once i have the actual structure, i don't need to change it, i just need to work with the data. I don't know how i can create the DataStruct[] only once, then pass that struct into the various change events without rebuilding it
the following code currently exists in all my change events (edited for brevity):
string[] fileArray = File.ReadAllLines(tempfile);
DataStruct[] data = new DataStruct[fileArray.Length];
for (int i = 0; i < fileArray.Length; i++)
{
List<string> dataList = fileArray[i].Split(',').ToList<string>();
data[i].X = (Convert.ToSingle(dataList[0]));
}
my confusion is: i have a variety of void() methods that use the structure, and it's easy to pass into those. however i don't understand how to pass into a change event, since the handler refreshes every time the change occurs, i don't know where to call the pass. For example:
private void trackBar1_Scroll(object sender, EventArgs e)
{
label282.Text = trackBar1.Value.ToString();
chart17.Series[0].Points.Clear();
VoltageChanger();
}
how would i call this Scroll change without re-doing the struct? VoltageChanger() uses the struct to do some stuff and make a new graph based on the trackbar value.
or...am i silly in thinking that there may be a slowdown here, and it's just the graph rendering that won't get any better?
thanks (edit: i have been reading but event handling outside of the winforms defaults is currently new to me)
I suggest you read up on variable scope here. For your example, you need to read the information once and have it accessible to all the methods within the class. That is what a module-level variable will do. In the code snippet below, notice the declaration for DataStruct is within the class definition, not within any individual method. This makes the variable visible to that class' methods.
public partial class Form1 : Form
{
private DataStruct[] _data; // <-- Module level variable
public Form1()
{
InitializeComponent();
LoadData();
}
private void LoadData()
{
// Open file code omitted
_data = new DataStruct[fileArray.Length];
// Read data into file omitted
}
private void Method1()
{
// _data will be accessible here because it is a module-level variable
}
}
So, around a week ago I asked a question about activex and UDP. Here it is:
C# UDP Socket client and server
Now, I created two applications, one (the sender) to send pre-defined strings via UDP. The other is activex component that is called from a webpage, and it's thread is working in the background. Once an UDP message arrives, then it's doing it's stuff (writing in database, writing in log.txt, and so on).
The last thing i need is to return data (it's yet to be said if it will be string or something else). However, the method in the activex which is called must be a void, because if it's made to be string, the threading wont work, and only the first message will arrive.
My question is, how to do that? How to return data from a void function? For example, the web app now is calling the activex DLL like this:
ClassLibrary1.Class1 activex = new ClassLibrary1.Class1();
activex.StartThread();
And the StartThread() calls the listening thread and it's working in the background, and once UDP msg arrives, its doing some stuff like i said above.
How can i return value with the threads (events) and the web app will catch it and use it?
Thanks a lot.
You can use events (which implement the Observable pattern) to alert any listener that a new message has arrived:
public class NewMessageArgs : EventArgs
{
public string Message { get; private set; }
public NewMessageArgs(string message)
{
Message = message;
}
}
public class ActiveXComponent
{
public event EventHandler<NewMessageArgs> OnMessage;
public void StartThread()
{
while (true)
{
//do stuff
//raise "message received" event
if (OnMessage != null)
OnMessage(this, new NewMessageArgs("hi"));
}
}
}
You can then listen to these events like so:
ActiveXComponent activex = new ActiveXComponent();
activex.OnMessage += ProcessMessage;
activex.StartThread();
public void ProcessMessage(object sender, NewMessageArgs args)
{
var msg = args.Message;
//process
}
Basically you have to store some data in a spot where you can access it from both places (from the thread, and from the place where you started the thread). So you have a couple of options from the top of my head.
Store it in a database
Create a specific object (whatever type you need), and store it in a place where it is accessible from both places. For example, a singleton. A simpler better solution is to create a property on your ClassLibrary.Class1 class: set it from within the Class1-class, and get it from the place where you created an instance of your Class1-class.
Add an event to your Class1-class which fires when it is finished doing its job. And add some data to the EventArgs.
I'm assuming here you get notified when your thread is done doing whatever it is doing.
Edit: added events
The threading function can change the fields values of the class and you can access those fields, also your thread can fire events that other classes can subcribe to and then act on it.
Class1
{
private string value;
public string Value{get{return value;} set{value=value; FireTheEvent();}}
}
I have a legacy C# class that extends UIAlertView for prompting the user to enter an IP address. It also will persist the IP address and load it as the default entry value.
The original code hacked the iOS4.x UIAlertView to add a text field and it had several layout problems. I converted the code to use the iOS5 UIAlertView's AlertViewStyle setting and everything worked fine.
I was asked to enable the Done key on the popup keyboard so that it could be a shortcut to the Connect button. I added code as follows to the class's constructor:
public AddressAlertView()
{
AddButton("Cancel");
AddButton("Connect");
AlertViewStyle = UIAlertViewStyle.PlainTextInput;
// NEW - get input text field, enable Done on the keyboard and
// allow Done to be the same as the Connect button
UITextField fld = GetTextField(0);
fld.ReturnKeyType = UIReturnKeyType.Done;
fld.ShouldReturn = (textField) =>
{
DismissWithClickedButtonIndex(1, true);
return textField.ResignFirstResponder();
};
// *** end of new code ***
// restore saved IP address
string savedAddress = NSUserDefault.StandardUserDefaults.StringForKey(AddressKey);
if (savedAddress != null)
fld.Text = savedAddress;
}
The class gets used like so:
....
AddressAlertView addrPrompt = new AddressAlertView();
addrPrompt.Title = "IP Address";
addrPrompt.Message = "Enter address";
addrPrompt.Dismissed += (sender, e) =>
{
if (e.ButtonIndex == 1)
{
// use IP address...
....
}
};
addrPrompt.Show();
....
Now the problem. When running this code, the AddressAlertView shows the popup dialog correctly and everything works as before. However, if I tap Done on the keyboard, the app bombs out in UIAlertView.
I tried moving the new code to the public override void LayoutSubviews() method, but it still crashes.
Any ideas?
You need to declare the AddressAlertView as a class member:
AddressAlertView addrPrompt;
public void DoSomething()
{
addrPrompt = new AddressAlertView();
//etc
}
The problem in this type of situations (I had many similar with UIAlertViews) is that the AddressAlertView object is GC'd and the event handler causes the crash. To make sure this is the issue, do not change anything, just remove/comment the addrPrompt.Dismissed += ... line and test it again. If it does not crash, than this is the solution.
According to Apple's documentation, you should not be subclassing UIAlertView:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html
Figured it out. In looking at the trace dump output from MonoTouch, there was a mention of an invalid object. Instead of getting the text field as a local variable, I save a reference to it as an instance variable:
private UITextField _addressTextField = null;
I changed the code in the constructor to:
_addressTextField = GetTextField(0);
Voila! That solved the crash. The local UITextField variable was undoubtedly getting GC'd, causing the crash when the ShouldReturn lambda expression was getting called.
I am debugging a WCF project with two-way communication. I have a callback with data that I store in an array the client, a WinForm, and use that for painting a control. As you can guess, the data disappears from writing in the array (really a list) to when I read the data.
For debugging I would like to see if I am writing and reading the same object so that the callback function isn't making some kind of copy and throw it away. For example I want to see the address of the this - pointer. How do I do that in VS2010 Exp?
Edit
Some code:
Field declaration:
// the cards that the player have
private List<Card> cards = new List<Card>();
callback handler:
private void btnDraw_Click(object sender, EventArgs e)
{
Tuple<Card, string> update = PressedDraw(this);
cards.Add(update.Item1);
PaintCards();
}
paint event:
private void cardPanel_Paint(object sender, PaintEventArgs e)
{
int counter = 0;
Point fromCorner = new Point(20,12);
int distance = 50;
foreach (Card card in cards)
{
Point pos = fromCorner;
pos.Offset(counter++ * distance, 0);
Bitmap cardBitmap =
cardFaces[Convert.ToInt32(card.suit),
Convert.ToInt32(card.rank)];
Rectangle square = new Rectangle(pos, cardBitmap.Size);
e.Graphics.DrawImage(cardBitmap, square);
}
When I debug I enter first in the callback handler and adds a Card in cards
PaintCards() calls Invalidate and the paint event is run. When in cardPanel_Paint, cards.Count is zero again.
Best regards.
Görgen
In the Watch/Locals/Autos windows, you can right-click on an object and select "Make Object ID" to give the object an identification number. This number is effectively the same as a native object's address; it serves to identify.
The identity of an object is tracked across garbage collections and compactions, so across the lifetime of your application, you can tell if a certain object is the one you originally tagged. This feature might help in your situation.
This blog post has a quick run-through of the feature.
The address of an object in c# can be changed by the garbage collector so you can not use that (and there is no straight forward method to do so).
But you can use Object.ReferenceEquals to compare to objects to see if they are really the same.
Edit:
But it looks like you have messed things up something like this.
var a = new List<string> { "String1" };
var b = a;
a = new List<string> { "String 2" }; // really GetListFromWcf();
Console.WriteLine(b[0]);
this prints String1, not String2.
If you can not figure it out you need to post some code to show where thing get wrong.