How can I add to a listbox from another class? - c#

I've seen this posted a few times but I cannot seem to get it to apply for me.
I want to add items to a listbox which is in my home class from my ServerController class.
Not entirely sure on how to go about this so any help would be grateful.
Home Class:
public partial class frmHome : Form
{
serviceController sc = new serviceController();
public ListBox lb = new ListBox();
public frmHome()
{
lb = new ListBox();
InitializeComponent();
serviceController sc = new serviceController();
}
//ListBox Add
public void addItem(string item)
{
lb_msg.Items.Add(item);
lb_msg.Items.Add(item);
lb_msg.Items.Add("");
}
}
Service Class:
public class serviceController
{
ServiceController[] scServices;
//Start the service
public void startServices()
{
scServices = ServiceController.GetServices();
try
{
foreach (ServiceController scTemp in scServices)
{
if (scTemp.ServiceName == "MSSQL$SQLSERVER" || scTemp.ServiceName == "SQLBrowser")
{
if (scTemp.Status == ServiceControllerStatus.Stopped)
{
//Check to see if service is disabled
home.addItem("Attempting to start " + scTemp.ServiceName);
scTemp.Start();
scTemp.WaitForStatus(ServiceControllerStatus.Running);
}
if (scTemp.Status == ServiceControllerStatus.Running)
{
home.addItem(scTemp.ServiceName + " is running");
}
}
else if (scTemp.Status == ServiceControllerStatus.Running)
{
home.addItem(scTemp.ServiceName + " is already running");
}
}
serverStatus();
}
catch (Exception ex)
{
throw ex;
}
}
On the Service Class I want to use home.addItem
Am I write in thinking I need to make a public listbox in my home class and link it with the one in my design?
What I want to achieve is this:
I want it to check x amount of services to see the status of it. If it is stopped, then check if it is disabled and report back - if it is disabled attempt to set as automatic, else attempt to start it. I want to write a log as it does this.
Hope this gives a bit more clarification.
Thanks!!!

You could pass it through the constructor
public class serviceController
{
private ListBox home;
public serviceController(ListBox lb)
{
home = lb;
}
public void foo()
{
home.Items.Add("")
}
}
Now, when you create a new object of serviceController, you need to pass a listbox.
serviceController sv = new serviceController(lb_msg);
Then, when you are in the serviceController, you can use the variable lb like you could in the main class
another way of accessing other form controls is
frmHome form = new frmHome();
ListBox lb = (ListBox)form.Controls["nameOfControl"];
lb.Items.Add("");
also: serviceController should start with an uppercase, ServiceController

I have written a sample code to demonstrate what I suggested. You have to match it with the actual types (classes e.g.) you're using. Added code comments, so I'm not going to write much here again. If you want to play with this code, here's a fiddle: https://dotnetfiddle.net/t0MyNk
public class Program // Consider this your home form
{
public void Main()
{
var serviceController = new ServiceController();
serviceController.ServiceStateChange += ServiceController_ServiceStateChanged;
serviceController.StartServices();
}
private void ServiceController_ServiceStateChanged(object sender, ServiceControllerEventArgs e)
{
// Add to listbox or do whatever you want. I am just printing.
Console.WriteLine(e.Message);
}
}
public class ServiceController
{
public event EventHandler<ServiceControllerEventArgs> ServiceStateChange;
public void StartServices()
{
Service[] services = new Service[] // Sample services data
{
new Service { Name = "MSMQ", Status = "Stopped" },
new Service { Name = "W3SVC", Status = "Running" }
};
string message = null;
foreach(Service s in services)
{
if(s.Status == "Stopped")
{
s.Start();
// Assuming it starts almost immediately. If not, you could follow same pattern for
// Service class where an event will be raised once service is "actually" started.
if(s.Status == "Running") {
message = string.Format("Service {0} is {1}", s.Name, s.Status);
}
}
else if(s.Status == "Running") {
message = string.Format("Service {0} is already {1}", s.Name, s.Status);
}
// Now tell subscriber (home form) about this.
OnServiceStateChange(message);
}
}
private void OnServiceStateChange(string message)
{
var serviceStateChangeHandler = ServiceStateChange;
if(serviceStateChangeHandler != null)
{
serviceStateChangeHandler(this, new ServiceControllerEventArgs { Message = message });
}
}
}
// You could have custome delegate type for event to return string message.
// But IMO this is much cleaner, and as per MSFT guidelines.
public class ServiceControllerEventArgs : EventArgs
{
// You can also return Service instance
// Just including Message for now
public string Message { get; set; }
}
// for demo purpose, represents individual service.
public class Service
{
public string Name { get; set; }
public string Status { get; set; }
public void Start() {
Status = "Running";
}
}
Output:
Service MSMQ is Running
Service W3SVC is already Running

I would strongly recommend you adopt binding instead of manipulating the controls on your forms directly, with the goal of keeping data and UI separate. You could for instance have a list (it would have to be a BindingList<> if memory serves) of strings in your class, and the list control would bind to it; that way the contents of the list are reflected in the UI and you can freely update the list from somewhere else as just data, without having to think about the intricacies of how the data is presented.
In the end you could come up with something similar to MVVM (model-view-viewmodel) or MVC, which in my opinion are superior solutions to manipulating controls manually.

Related

Call non static method from partial class

I have two forms in the same namespace.
One is the main form that displays the list of accounts.
public partial class Server : Form
{
public Server()
{
InitializeComponent();
LoadAcounts();
}
public void LoadAcounts()
{
AccountDB acc = new AccountDB();
userListBox.DataSource = acc.ListUsers();
this.userListBox.SelectedIndex = 0;
}
}
Second is the registration form.
public partial class RegForm : Form
{
public RegForm()
{
InitializeComponent();
passBox.Text = "";
passBox.PasswordChar = '*';
passBox.MaxLength = 14;
passBox2.Text = "";
passBox2.PasswordChar = '*';
passBox2.MaxLength = 14;
}
private void button1_Click(object sender, EventArgs e)
{
if (passBox.TextLength >= 4 && passBox.Text == passBox2.Text && usernameBox.TextLength >= 4)
{
AccountDB acc = new AccountDB();
if (acc.UserExist(usernameBox.Text))
{
MessageBox.Show("User already exists!");
}
else
{
string user = usernameBox.Text;
string pw = PasswordHash.HashPassword(passBox.Text);
WriteDB(user, pw);
this.Close();
MessageBox.Show("Registration successful!");
//LoadAccounts();
}
}
}
}
I am currently stuck on how can I call LoadAccounts() after Registration successful so the userListBox will be refreshed to include the newly added account.
I am not sure about your design, but you can create an instance property of the "Server" class in your "RegForm" class. BUT, I should say that is increasing class coupling and definitely is not a good pattern.
public partial class RegForm : Form
{
public Server serverFormInstance {get; set;}//must be filled from caller code
...
...
If you have only one instance of the Server form, you can make it as a singleton to be able to call the method.
public class Server : Form
{
static internal readonly Server Instance = new Server ();
private Server()
{
InitializeComponent();
LoadAcounts();
}
}
Or any singleton implementation you like.
So you'll change all access to Server by Server.Instance.
Now you can call:
Server.Instance.LoadAcounts();
But if you plan to allow several instances of Server, a registration method may be used but it requires more code of your project to think about that.
You can also read this:
Communicate between two windows forms in C#

How to instantiate a new Windows Form with Quartz.NET without problems?

I'm using the Quartz.NET library to create a job in my C# application.
I have some registers in my database, so I have a table wich contains a column called "start_date". The job runs every 50 seconds, so I compare the dates from the column "start_date" with the date of my computer, and if the dates are equal, I want to instantiate a new Windows Form with a message and a button.
At the moment, the new Windows Form is opening at the right moment, but the message is not showed and the window stops to respond.
Basically, in my code I have something like this:
FormMessage.cs
public partial class FormMessage : Form
{
public FormMessage()
{
InitializeComponent();
}
public FormMessage(double minutes)
{
InitializeComponent();
string message = string.Format("You have {0} minutes!", minutes);
lblMessage.Text = message ;
}
private void btnOK_Click(object sender, EventArgs e)
{
this.Close();
}
}
JobMessage.cs
public class JobMessage: IJob
{
List<Information> informations;
public void Execute(IJobExecutionContext context)
{
//Class with methods to get registers from database.
InformationAPI infoAPI = new InformationAPI();
informations = infoAPI.GetInformations();
foreach (Information info in informations)
{
DateTime computerDateTime = DateTime.Now;
DateTime infoDateTime = info.StartDate;
double difference;
if (DateTime.Compare(computerDateTime, infoDateTime) < 0)
{
difference = Math.Round(infoDateTime.Subtract(computerDateTime).TotalMinutes);
if (difference == 5)
{
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
}
}
}
}
Someone have some idea of the reason why the FormMessage window stops to respond?
Thank you for your attention!
You can try Quartz Listeners to let them open the form to show the data and keep the execution out of the job scope:
Action<IJobExecutionContext, JobExecutionException> listenerAction = (c, e) => {
var dataMap = context.GetJobDetail().GetJobDataMap();
var difference = dataMap.GetIntValue("difference");
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
var listener = new SyncJobListener(listenerAction);
And add the listener in to the scheduler:
scheduler.ListenerManager.AddJobListener(listener,
GroupMatcher<JobKey>.GroupEquals("GroupName"));
Using this SyncJobListener:
public class SyncJobListener : IJobListener
{
private readonly Action<IJobExecutionContext, JobExecutionException> _syncExecuted;
public string Name { get; private set; }
public SyncJobListener(
Action<IJobExecutionContext, JobExecutionException> syncExecuted
)
{
Name = Guid.NewGuid().ToString();
_syncExecuted = syncExecuted;
}
public void JobToBeExecuted(IJobExecutionContext context)
{
}
public void JobExecutionVetoed(IJobExecutionContext context)
{
}
public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
{
_syncExecuted(context, jobException);
}
}
I have not tested this so if the dataMap does not have any data, you are going to need to allow the persistance:
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class JobMessage: IJob {}

Access statusbar on form from cplex callback function c#

I'm working with an C# .Net application that uses Cplex DLL's for an optimization operation, and during that operation I want to write status progress to a statusbar on the that initiated the operation.
This is the general layout of the specific form;
namespace ActResMain
{
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//...
cplex.Use(new Cplex_ContinuousCallback());
cplex.Solve()
}
public void Update_OptimizeStatusbarPanel(String strText)
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
//From here I want to edit the statusbar at FormOptimize. I can write progress to console without any problems, but cannot reach function "Update_OptimizeStatusbarPanel".
//If I include "FormOptimize formOpt = new FormOptimize" here, i get Visual studio exception on illegal window reference.
}
}
}
}
I have also tried invoking the Update_OptimizeStatusbarPanel function like this:
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
FormCollection fc = Application.OpenForms;
var mpc = fc[1];
Type type = mpc.GetType();
MethodInfo dynMethod = type.GetMethod("Update_OptimizeStatusbarPanel");
dynMethod.Invoke(mpc, new object[] { String.Format("Running Optimization: {0} iterations ", Niterations)});
}
}
But then I get an exception from visual studio stating that an object created by one thread cannot be modified from another thread.
Maybe this is something stupid that I have missed, but help is greatly appriciated
EDIT: I edited the code as per Mohammad Dehghans suggestion,
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
cplex.Use(new Cplex_ContinuousCallback(this));
cplex.Solve()
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
if (Niterations % 10 == 0)
{
_formOptimize.Update_OptimizeStatusbarPanel(0, String.Format("Running Optimization: {0} iterations ", Niterations), 0);
}
}
}
public void Update_OptimizeStatusbarPanel(short panelIndex, String strText, short severity)
{
if (statusBar1.InvokeRequired)
statusBar1.Invoke(new Action<short, string, short>(Update_OptimizeStatusbarPanel), panelIndex, strText, severity);
else
{
if (panelIndex == 0)
{
//...
statusBarPanel_0.Text = strText;
}
else if (panelIndex == 1)
{
//...
statusBarPanel_1.Text = strText;
}
statusBar1.Refresh();
}
}
}
But by doing that I apparently broke something, as the application just ..stops after statusBar1.Invoke() is called the first time. If I pause the debugger it says that cplex.Solve() is executing, but then nothing more happens.
First of all, you need to pass the instance of your form to the implemented callback class, so when the Main method is called, you have access to the exact instance that is being shown on the screen.
Secondly, you need to use Invoke method to update the UI controls from anther thread (I've not worked with CPLEX so far, but I guess the callback is invoked from another thread. That's usual).
Read this for more information.
The complete code could be:
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//Misc code
cplex.Use(new Cplex_ContinuousCallback(this)); // <-- passing `this`
cplex.Solve()
//Misc code
}
public void Update_OptimizeStatusbarPanel(String strText)
{
if (statusBarPanel_1.InvokeRequired)
statusBarPanel_1.Invoke(Action<string>(Update_OptimizeStatusbarPanel), strText);
else
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
//...
_formOptimize.Update_OptimizeStatusbarPanel(String.Format("Running Optimization: {0} iterations ", Niterations));
}
}
}

How to Control the time between each Post Using Facebook Sdk C#

I am using Facebook Sdk C# To develop a desktop program for publication posts in Facebook groups. The software will publish very quickly I need a way to control the speed of publication and capping for publication not less than 10 seconds between each publication and the other. How to do like this methods ?
My class
public static string UploadPost(string groupid, string intTitle, string inMessage, string inLinkCaption, string inLinkUrl, string inLinkDescription, string inLinkUrlPicture)
{
object obj;
Facebook.JsonObject jsonObj;
FacebookClient client;
string access_token = AppSettings.Default.AccessToken.ToString();
client = new FacebookClient(access_token);
var args = new Dictionary<string, object>();
args["message"] = inMessage;
args["caption"] = inLinkCaption;
args["description"] = inLinkDescription;
args["name"] = intTitle;
args["picture"] = inLinkUrlPicture;
args["link"] = inLinkUrl;
if ((obj = client.Post("/" + groupid + "/feed", args)) != null)
{
if ((jsonObj = obj as Facebook.JsonObject) != null)
{
if (jsonObj.Count > 0)
return jsonObj[0].ToString();
}
}
return string.Empty;
}
internal static bool UploadPost(string p1, string p2)
{
throw new NotImplementedException();
}
}
}
My submit button
private void btnPost_Click(object sender, EventArgs e)
{
for (int i = 0; i < lstgroupsbox.Items.Count; i++)
{
if (Class1.UploadPost(lstgroupsbox.Items[i].ToString(), "amine", txtStatus.Text, "googl", txtLink.Text, "seach", txtImagePath.Text) != string.Empty)
label23.Text=""+lstgroups.Items[i].Text;
}
//foreach (var item in lstgroupsbox.Items)
//{
// if (Class1.UploadPost(item.ToString(), "amine", txtStatus.Text, "googl", 2, "seach", txtImagePath.Text) != string.Empty)
// label23.Text=""+lstgroups.Items[i].Text;
//}
}
I suggest you look into the Timer class. This one way you could do this:
Create a ConcurrentQueue (not a normal Queue because it's not thread-safe, look into thread safety) in your main class. This is your upload "job list". Add also a Timer and create an event handler method for its Elapsed event (refer to the first link in this answer for instructions). The event handler method is what will execute your jobs.
Then create a new class that holds a post's info and the details needed to upload it. This is your upload job class.
In your for loop inside your submit button's event handler, instead of uploading the images, you create instances of that job class and enqueue them into your job list (the ConcurrentQueue). After they're all added, you start your main class' Timer. The Elapsed event handler method mentioned earlier will take the next item from the queue (or stop if it's empty?) and upload it to Facebook.
EDIT:
So, in your form class, add a using statement first:
using System.Collections.Concurrent;
Then add these near the top of the class:
private System.Timers.Timer _jobTimer = new Timer(10000);
private ConcurrentQueue<UploadJob> _jobQueue = new ConcurrentQueue<UploadJob>();
These go to your form's constructor (after the InitializeComponent() method call):
_jobTimer.SynchronizingObject = this;
// ^ The Elapsed event will be run on the same thread as this.
// This way we won't get exceptions for trying to access the form's labels
// from another thread (the Timer is run on its own thread).
_jobTimer.Elapsed += OnJobTimedEvent;
Then you add the OnJobTimedEvent method somewhere in your form class:
private void OnJobTimedEvent(object sender, ElapsedEventArgs e)
{
UploadJob job;
if (_jobQueue.TryDequeue(out job)) // Returns false if it fails to return the next object from the queue
{
if (Class1.UploadPost(job.Group,
job.Name,
job.Message,
job.Caption,
job.Link,
job.Description,
job.Picture) != string.Empty)
{
// Post was uploaded successfully
}
}
else
{
// I believe we can assume that the job queue is empty.
// I'm not sure about the conditions under which TryDequeue will fail
// but if "no more elements" isn't the only one, we could add
// (_jobQueue.Count == 0)
// to the if statement above
_jobTimer.Stop();
// All uploads complete
}
}
As you can see, it uses UploadJob. That class can be separated into a separate file altogether:
public class UploadJob
{
public string Group { get; protected set; }
public string Name { get; protected set; }
public string Message { get; protected set; }
public string Caption { get; protected set; }
public string Link { get; protected set; }
public string Description { get; protected set; }
public string Picture { get; protected set; }
public UploadJob(string group,
string name,
string message,
string caption,
string link,
string description,
string picture)
{
Group = group;
Name = name;
Message = message;
Caption = caption;
Link = link;
Description = description;
Picture = picture;
}
}
and finally we get to your button click event handler:
private void btnPost_Click(object sender, EventArgs e)
{
for (int i = 0; i < lstgroupsbox.Items.Count; i++)
{
_jobQueue.Enqueue(lstgroupsbox.Items[i].ToString(),
"amine",
txtStatus.Text,
"googl",
txtLink.Text,
"seach",
txtImagePath.Text);
}
_jobTimer.Start();
}
Personally I would probably separate all this into an UploadManager class or something and let that class worry about the timer and everything, but this should work just fine.

Custom event and invocation on main thread

I was given a generic API class, that contains a custom event which always needs to be invoked by the main UI thread.
My job is to banish these invocation call from the custom class, to make it "painless".
It should be synchronized like the default events in WinForms (eg the Timer "Elapsed" event, which also needs no invocation when it published values to a text box)
Is it possible to solve this, since the custom class needs to know where to invoke?
Here's the (important part of the) code:
public class ContactSensorHelper
{
public event OnReleaseStateChanged ReleaseStateChanged;
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (ReleaseStateChanged != null)
ReleaseStateChanged(new ContactSensorEventArgs()
{
State = recentReleaseState
});
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}
The call from main UI:
public void SensorInit()
{
//....
sensorHelper.ReleaseStateChanged += releaseStateChanged;
//....
}
private void releaseStateChanged(ContactSensorEventArgs e)
{
//example
textBox1.Text = e.State.ToString(); // Thread exception (obviously)
}
Does anybody have me a hint to start?
You could do this by using your own event calling, and storing a reference to the thread, when the event is attached.
With the event add/remove syntax, you can have the caller attach to the event like before, but internally you store a list, with a reference to the thread (using an AsyncOperation) and the delegate to be called (used a Tuple containing both in the example)
Below is an example. I tested it, and it worked as expected when testing, but you might have to add some locking of the list to make it thread safe in case events are added/removed simultaneously.
public class ContactSensorHelper:IDisposable
{
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (statechangedList.Count > 0)
{
var e = new ContactSensorEventArgs()
{
State = recentReleaseState
};
statechangedList.ForEach(t =>
t.Item1.Post(o => t.Item2((ContactSensorEventArgs)o), e));
}
}
List<Tuple<AsyncOperation, OnReleaseStateChanged>> statechangedList = new List<Tuple<AsyncOperation,OnReleaseStateChanged>>();
public event OnReleaseStateChanged ReleaseStateChanged
{
add
{
var op = AsyncOperationManager.CreateOperation(null);
statechangedList.Add(Tuple.Create(op, value));
}
remove
{
var toremove = statechangedList.Where(t => t.Item2 == value).ToArray();
foreach (var t in toremove)
{
t.Item1.OperationCompleted();
statechangedList.Remove(t);
}
}
}
public void Dispose()
{
statechangedList.ForEach(t => t.Item1.OperationCompleted());
statechangedList.Clear();
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}

Categories

Resources