Invoke of a UserControl doesn't work - c#

I have a form (MainPage) and I set a UserControl in it some times, So I write a method in that form like this to invoke:
delegate void containerPanelCallback(UIPart uiPart);
public void IncludeUIPart(UIPart uiPart)
{
if (this.containerPanel.InvokeRequired)
{
containerPanelCallback d = new containerPanelCallback(IncludeUIPart);
containerPanel.Invoke(d, new object[] { uiPart });
}
else
{
containerPanel.Controls.Clear();
containerPanel.Controls.Add(uiPart);
}
uiPart.Size = this.containerPanel.Size;
uiPart.Dock = DockStyle.Fill;
}
UIPart class inherit from UserControl that my UserControls inherit from UIPart.
This method and invoke launched like this:
public class myClass
{
...
private static MainPage _frmMain;
private static myUIPart6 UIP6;
...
public static void aMethod(/* Some arguments */)
{
UIP6 = new myUIPart6 { /* Some settings of properties */ };
_frmMain.IncludeUIPart(UIP6);
_frmMain.Show(); /*Throws an error*/
}
...
}
The error is:
Cross-thread operation not valid: Control 'MainPage' accessed from a thread other than the thread it was created on.
I found many questions and many answers here about this error, But I can't figure it out why it is throwing at _frmMain.Show();?, Should I invoke something else? Or Am I in a wrong way? Is it related to creation of Handle of my UserControl?

Try adding the following code:
public static void aMethodCaller(){
if (_frmMain.InvokeRequired)
_frmMain.Invoke(new Action(aMethod));
else
aMethod();
}
and replace all references to aMethod() in your code to aMethodCaller()
Below is the sample code:
class Foo
{
static Form _frmMain;
public static void aMethod()
{
_frmMain.Show();
}
public static void aMethodCaller()
{
if (_frmMain.InvokeRequired)
_frmMain.Invoke(new Action(aMethod));
else
aMethod();
}
}

The _frmMain.Show() isn't guarded by any invocation requirement check. So you're probably calling it in a background thread.

Related

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));
}
}
}

Accessing GUI from another class via a static helper class

I just wanted to ask if the following code is a valid method to access the GUI from another class, or if it is bad practice. What I want to do is to write log messages into a RichTextBox in Form1.
If it's bad practice, would it be better to pass a reference of my Form1 to the other class to be able to access the RichTextBox.
I have the following code to access the GUI in my Form1 from another class:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Logger.Init(this.rtbLog);
MyOtherClass myOtherClass = new MyOtherClass();
myOtherClass.DoSomething();
}
}
public class MyOtherClass
{
public void DoSomething()
{
Logger.AppendText("text...");
Logger.AppendText("text...");
Logger.AppendText("text...");
}
}
public static class Logger
{
private static RichTextBox _rtb;
public static void Init(RichTextBox rtb)
{
_rtb = rtb;
}
public static void AppendText(String text)
{
_rtb.AppendText(text);
_rtb.AppendText(Environment.NewLine);
}
}
With Events (thanks to Ondrej):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Logger.EntryWritten += Logger_EntryWritten;
MyOtherClass myOtherClass = new MyOtherClass();
myOtherClass.DoSomething();
}
void Logger_EntryWritten(object sender, LogEntryEventArgs args)
{
rtbLog.AppendText(args.Message);
rtbLog.AppendText(Environment.NewLine);
}
}
public class MyOtherClass
{
public void DoSomething()
{
Logger.AppendText("text...");
Logger.AppendText("text...");
Logger.AppendText("text...");
}
}
public static class Logger
{
public static event EventHandler<LogEntryEventArgs> EntryWritten;
public static void AppendText(string text)
{
var tmp = EntryWritten;
if (tmp != null)
tmp(null, new LogEntryEventArgs(text));
}
}
public class LogEntryEventArgs : EventArgs
{
private readonly String message;
public LogEntryEventArgs(String pMessage)
{
message = pMessage;
}
public String Message
{
get { return message; }
}
}
It's probably fine for a small throw-away project, but otherwise a logger should not know anything about used platform. Then it would be good to use events for example. Raise an event whenever there's a new log entry written and consumers interested in logged entries will subscribe to a delegate.
Also be careful with threads. If you log a message from a different thread than UI you will end up with an exception because you would access a GUI control from a different thread which is forbidden.
EDIT:
Something along these lines. LogEntryEventArgs is a type you have to create and you can give it properties like Message, TimeWritten, Severity, etc.
public static class Logger
{
public static event EventHandler<LogEntryEventArgs> EntryWritten;
public static void AppendText(string text)
{
var tmp = EntryWritten;
if (tmp != null)
tmp(null, new LogEntryEventArgs(text));
}
}
consumer:
Logger.EntryWritten += Logger_OnEntryWritten;
void Logger_OnEntryWritten(object sender, LogEntryEventArgs args)
{
_rtb.AppendText(args.Message);
_rtb.AppendText(Environment.NewLine);
}
Also, don't forget to invoke on a form/dispatch the body of Logger_OnEntryWritten in order to avoid cross-thread access exception (in case you are considering using threads).

Access WPF Name properties in static method

I have a WPF application. In one of the XAML I have used Name attribute like as follows
x:Name="switchcontrol"
I have to access the control/property in .cs file using this.switchcontrol
My question is, I need to access the control in static method like
public static getControl()
{
var control = this.switchcontrol;//some thing like that
}
How to achieve this?
this is not accessible in static method. You can try save reference to your instance in static property, for example:
public class MyWindow : Window
{
public static MyWindow Instance { get; private set;}
public MyWindow()
{
InitializeComponent();
// save value
Instance = this;
}
public static getControl()
{
// use value
if (Instance != null)
var control = Instance.switchcontrol;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Instance = null; // remove reference, so GC could collect it, but you need to be sure there is only one instance!!
}
}
Some alternatives to Tony's method - you could pass in the window (or whatever xaml construct you are using) as a reference to the method, e.g.
public static void GetControl(MainWindow window)
{
var Control = window.switchcontrol;
}
if you are going to be passing several different derived types of Window, you could also do this:
public static void GetControl(Window window)
{
dynamic SomeTypeOfWindow = window;
try
{
var Control = SomeTypeOfWindow.switchcontrol;
}
catch (RuntimeBinderException)
{
// Control Not Found
}
}

implementing delegates in c#

This would be the first time I'd use delegates in c# so please bear with me. I've read a lot about them but never thought of how/why to use this construct until now.
I have some code that looks like this:
public class DoWork()
{
public MethodWorkA(List<long> TheList) {}
public void MethodWork1(parameters) {}
public void MethodWork2(parameters) {}
}
I call MethodWorkA from a method outside the class and MethodWorkA calls MethodWork 1 and 2. When I call methodA, I'd like to pass some sort of parameter so that sometimes it just does MethodWork1 and sometimes it does both MethodWork1 and MethodWork2.
So when I call the call it looks like this:
DoWork MyClass = new DoWork();
MyClass.MethodA...
Where does the delegate syntax fit in this?
Thanks.
public void MethodWorkA(Action<ParamType1, ParamType2> method) {
method(...);
}
You can call it using method group conversion:
MethodWorkA(someInstance.Method1);
You can also create a multicast delegate that calls two methods:
MethodWorkA(someInstance.Method1 + someInstance.Method2);
For what you described, you don't need delegates.
Just do something like this:
public class DoWork
{
public void MethodWorkA(List<long> theList, bool both)
{
if (both)
{
MethodWork1(1);
MethodWork2(1);
}
else MethodWork1(1);
}
public void MethodWork1(int parameters) { }
public void MethodWork2(int parameters) { }
}
If you're just experimenting with delegates, here goes:
public partial class Form1 : Form
{
Func<string, string> doThis;
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
}
void Form1_Shown(object sender, EventArgs e)
{
doThis = do1;
Text = doThis("a");
doThis = do2;
Text = doThis("a");
}
string do1(string s)
{
MessageBox.Show(s);
return "1";
}
string do2(string s)
{
MessageBox.Show(s);
return "2";
}
}
Considering that all methods are inside the same class, and you call MethodWorkA function using an instance of the class, I honestly, don't see any reason in using Action<T> or delegate, as is I understood your question.
When I call methodA, I'd like to pass some sort of parameter so that
sometimes it just does MethodWork1 and sometimes it does both
MethodWork1 and MethodWork2.
Why do not just pass a simple parameter to MethodWorkA, like
public class DoWork()
{
public enum ExecutionSequence {CallMethod1, CallMethod2, CallBoth};
public MethodWorkA(List<long> TheList, ExecutionSequence exec)
{
if(exec == ExecutionSequence.CallMethod1)
MethodWork1(..);
else if(exec == ExecutionSequence.CallMethod2)
MethodWork2(..);
else if(exec == ExecutionSequence.Both)
{
MethodWork1(..);
MethodWork2(..);
}
}
public void MethodWork1(parameters) {}
public void MethodWork2(parameters) {}
}
Much simplier and understandable for your class consumer.
If this is not what you want, please explain.
EDIT
Just to give you an idea what you can do:
Example:
public class Executor {
public void MainMethod(long parameter, IEnumerable<Action> functionsToCall) {
foreach(Action action in functionsToCall) {
action();
}
}
}
and in the code
void Main()
{
Executor exec = new Executor();
exec.MainMethod(10, new List<Action>{()=>{Console.WriteLine("Method1");},
()=>{Console.WriteLine("Method2");}
});
}
The output will be
Method1
Method2
In this way you, for example, can push into the collection only functions you want to execute. Sure, in this case, the decision logic (which functions have to be executed) is determined outside of the call.

C# Set Form Parent after calling method from another class

I've searched Google all day and can't find the correct answer to my issue, hoping someone here can help me.
So, in the "Main" form I have the method to show a form that needs to be centered directly above the parent form (frmMain). Normally I would call ShowDialog(this) to see the parent, but for some reason I have to set the loadNewsFeedItem to static in order to see the method from the flpNewsFeedHeader : Label derrived class (below). The OnClick event triggers the method loadNewsFeedItem().
When I call this to set the parent, I'm getting the message "Keyword 'this' is not valid in a static property, static method, or static field initializer"
namespace NewsFeeds
{
public partial class FrmMain : Form
{
public static void loadNewsFeedItem()
{
frmNewsFeedView frmFeedView = new frmNewsFeedView(FrmFuncs.selFeedID);
frmFeedView.ShowDialog(this); // Error occurs on this line, when calling this via a static method
}
}
}
public class flpNewsFeedHeader : Label
{
private int FeedID = 0;
public int theFeedID
{
get { return FeedID; }
set { FeedID = value; }
}
protected override void OnClick(EventArgs e)
{
FrmFuncs.selFeedID = FeedID;
Thread thrShowFeed = new Thread(new ThreadStart(FrmMain.loadNewsFeedItem));
thrShowFeed.Start();
}
}
Can someone please give me a corrected code example or a hint as to how to get the loadNewsFeedItem() to be visible without setting the accessor to static, or how to work around this in a static accessor?
Thanks in advance!
Chris
Edit: used ActiveForm for owner.
public partial class FrmMain : Form
{
public static void loadNewsFeedItem(Form owner)
{
frmNewsFeedView frmFeedView = new frmNewsFeedView(FrmFuncs.selFeedID);
frmFeedView.ShowDialog(owner);
}
}
}
public class flpNewsFeedHeader : Label
{
private int FeedID = 0;
public int theFeedID
{
get { return FeedID; }
set { FeedID = value; }
}
protected override void OnClick(EventArgs e)
{
FrmFuncs.selFeedID = FeedID;
// Shouldn't need a new thread. Already on the GUI thread.
FrmMain.loadNewsFeedItem (System.Windows.Forms.Form.ActiveForm);
}
}
may be you mean this:
frmFeedView.Owner = System.Windows.Forms.Form.ActiveForm;
frmFeedView.ShowDialog();
In a static method, this is meaningless. One option is to skip the parameter
frmFeedView.ShowDialog();
The other option is to setup a static variable as shown below (but beware, it can have side effects if you try to open multiple instances of FrmMain)
public partial class FrmMain : Form
{
private static FrmMain staticInstance;
public FrmMain()
{
staticInstance = this;
InitializeComponent();
...
}
public static void loadNewsFeedItem()
{
frmNewsFeedView frmFeedView = new frmNewsFeedView(FrmFuncs.selFeedID);
frmFeedView.ShowDialog(staticInstance );
}

Categories

Resources