Checking a C# property without a reference to its class? - c#

I have a class 'App' which has instances of both a 'DeviceManager' class and a windows form 'MainForm' stored as fields.
MainForm is made visible by clicking a system tray icon. DeviceManager has Docked and Undocked methods, with Docked starting a synchronization operation. The Docked method should only start the operation if the MainForm is not visible.
DeviceManager doesn't have access to App's members, so it can't use App's reference to MainForm to check the form's status. Having App pass itself into DeviceManager's constructor seems like a lot of coupling when DeviceManager has no other need for such a reference (MainForm and DeviceManager are thus far unaware of each other).
I'm now considering having the setter of the App.IsUserActive property raise an event that DeviceManager can use to maintain its own 'IsUserActive' field.
Are there any other approaches I could look into?
Edit: added code to illustrate:
internal class App
{
private DeviceManager _deviceMgr;
private MainForm _mainForm;
internal App()
{
_deviceMgr = new DeviceManager();
_mainForm = new MainForm { Visible = false };
}
}
internal class DeviceManager
{
private void Docked()
{
if (!_mainForm.Visible) //can't see MainForm or App from here
{
Connect();
StartSynchronization();
}
}
private void Undocked()
{
Disconnect();
}
}

There is a global reference to the forms you can use.
Here's a quick example:
//Inside of DeviceManager class
private bool CheckFormVisibility<TForm>() where TForm : Form
{
TForm form = System.Windows.Forms.Application.OpenForms.OfType<TForm>().SingleOrDefault();
return form != null && form.Visible;
}
Then call CheckFormVisibility<MyForm>() or remove the generics and use specifically for your MyForm.
**I'm going under the assumption here that you will only have zero/one instance of a form.

Related

Circular dependency when adding reference (Delegates)

Basically I have 2 projects, a form and a user control.
I need both of them to be in different projects but the form need to refer to the user control as it is using the user control. And the user control will need to refer to the form as it is using one of the form class. When I add the second one because it need the , VS will complain circular dependency which is understandable. How do I solve this?
Logically the form should depend on the user control. You could create an interface to replace the form within the user control project, and then have the form implement that interface.
Example user control project;
public interface IForm
{
string MyString { get; }
}
public class MyUserControl : UserControl
{
public IForm Form { get; set; }
private void ShowMyString()
{
String myString = Form.MyString;
...
}
}
Example Form project
public class MyForm : Form, IForm
{
public MYString { get "My String Value"; }
}
I think the root cause of your problem is that you haven't separated your concerns between the form and the control properly.
Since you have a (somewhat generic) control, it shouldn't depend on the form. All of the logic of the control should reside within the control itself. The form should only black-box consume the control: add it, set public fields, call public methods, etc. anything else is a violation of encapsulation.
Sometimes, controls may need to know things about their parent form. In this case, I would suggest something as simple as adding a Parent field to the child control.
if you need something more specific from the form, you can always add an interface; the interface should only list those things that the control needs from the form. For example, if you need the size, you can add:
public interface IControlParent {
int Width { get; }
int Height { get; }
}
This way, you clearly see the dependencies (what the control needs from the parent), and if the parent type/contract changes, you don't need to do as much to change your control class.
You must sepárate your code, its never a good idea to have a reference to an application assembly, if you try to reuse it in the future, the applications exe should go with the control.
So, take the class from the form project and move it to the control project or create a library project, put the class on it and reference it from your control and your app projects.
You should use an event (delegate). Let's assume that inside your form project you created one class: Form1. And inside user control you defined UserControl1.
UserControl1 needs to instantiate and call a method from Form1:
public class Form1
{
public void Execute(string sMessage)
{
Console.WriteLine(sMessage);
Console.ReadLine();
}
}
UserControl1:
public class UserControl
{
public Func<object, object> oDel = null;
public void Execute()
{
oDel?.Invoke("HELLO WORLD!");
}
}
And from the class that instantiate UserControl, let's call it ParentClass:
public class ParentClass
{
public void Execute()
{
UserControl oUserControl = new UserControl();
oUserControl.oDel = Form1Action;
oUserControl.Execute();
}
public object Form1Action(object obj)
{
string sObj = Convert.ToString(obj);
Form1 oForm = new Form1();
oForm.Execute(sObj);
return null;
}
}
This approach gives the responsibility of handling an event to the high level class.

C# - Call a function in a parent form

I'm trying to call a function in a main form from another form... Already got to call a simple function, by declaring it public static in main form, yet I can't call the needed one.
The function to call:
public static void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
MainForm.txtSendKeys.Text = dial;// Here it asks me for a reference to an object.
foreach (char c in txtSendKeys.Text)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
txtSendKeys.Clear();
}
The procedure I use to call it from a child form:
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Hoho";
MainForm.spotcall();
}
I completely admit that I lack some theory about C#, but as it often happens, I just have to do it for my work, so I expect to get help if by chance I don't get to the solution by myself. Thank you :)
You cannot reference instances of controls on your MainForm in a static method. Like the compiler is telling you, you need an instance of the form in order to update things like TextBoxes. Without an instance, where would the values you are trying to update go?
I'm not sure exactly how the child form is being created, but one way you could call methods on your MainForm would be to provide a reference to your MainForm instance directly to the child form. This could be through the constructor or some public property.
For example
public class ChildForm : Form {
public MainForm MyParent { get; set; }
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Hoho";
// Now your child can access the instance of MainForm directly
this.MyParent.spotcall();
}
}
Assuming you are creating ChildForm inside of MainForm the code to give the child a reference is pretty simple:
var childForm = new ChildForm();
childForm.MyParent = this; // this is a `MainForm` in this case
childForm.Show();
You would also need to make spotcall an instance method and not a static method, and remove the static reference to MainForm in your code:
public void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
// Now it no longer asks you for a reference, you have one!
txtSendKeys.Text = dial;
foreach (char c in txtSendKeys.Text)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
txtSendKeys.Clear();
}
I think the correct way to do this is to use delegates. This way your form (window) does not have to know anything about the parent form (the form can be opened from different parent forms).
Let's say we want to call a function in the parent form when the child form is closed (not showing the form as modal).
At the top of your child form create a delegate:
public delegate void CloseEvent();
public CloseEvent WindowClosed;
Create the form closing event and have it call your delegate:
private void child_FormClosing(object sender, FormClosingEventArgs e)
{
WindowClosed();
}
A button in the parent form can show the child form and set the callback:
private ChildForm childform = null;
private void buttonShowChildForm_Click(object sender, EventArgs e)
{
if (childform == null)
{
childform = new ChildForm();
childform.WindowClosed += childClosed;
childform.Show();
} else
{
childform.BringToFront();
}
}
private void childClosed()
{
childform = null;
}
In this example we use a button to open a new form that does not block the parent form. If the user tries to open the form a second time, we just bring the existing form to the front to show it to the user. When the form is closed we set the object to null so that next time we click the button a new form is opened because the old was disposed when closed.
Best regards
Hans Milling...
You can not access non-static members in static context, which means you have to made txtSendKeys static, or make your function non-static.
If you create a static function, you may not reference global variables inside the function that aren't static as well.
So in order for spotcall to be static, you have to remove the reference to the txtSendKeys (I'm assuming this is a text box that you have created elsewhere in the form) or txtSendKeys must be declared within the static function.
Additional:
You obtained the value for txtSendKeys.Text in the previous line, via variable dial. Instead of referencing txtSendKeys.Text at all, I imagine you could simply use the variable dial to complete the function and leave the function static (you clear it at the end anyway).
public static void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
foreach (char c in dial)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
}
Although, that wouldn't overcome the same issue you would likely run into with checkBoxPrivate.Checked.
You could change it to take a boolean argument.
public static void spotcall(Boolean PrivateChecked)
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
foreach (char c in dial)
{
sideapp.Keyboard.SendKey(c.ToString(), PrivateChecked);
}
}
You can put the shared code in a third class that's visible to both forms. So, for example:
public class static HelperFunctions
{
public static void spotcall()
{
. . .
}
}
Then replace
MainForm.spotcall()
with
HelperFunctions.spotcall()
The MainForm is just a class. It has the structure of the class. But the only data you can get from it is static data.
But an instance of that class appears when you do: MainForm MyFormInstance = new MainForm();
The MainForm can be used only to access static members (methods, properties...).
When you want to get the txtSendKeys, you must get it from an instance (object reference). That's because the textbox is not static, so it only exists in instances of the form.
So, you should do the following:
Make spotcall NOT static.
Put in child form a variable MainForm MyParentMainForm;
When you call the child, set that MyParentMainForm with the instance of the mainform. If it's being called from the main form, you can get the instance with the this keyword.
Inside child form, call MyParentMainForm.spotcall
PS: I'm not sure if there's something like a real child form or if you just call new forms from another. If there's really a child form, you can get the Parent property to access the instance of the main form.
This is sort of a "design pattern" issue, which I'll elaborate on, but I can try to explain the most direct way to solve this if you don't expect this program to change very much. "Static" things only exist once - once in the entire application. When a variable or function is static, it's much easier to access from anywhere in the program; but you can't access an object's associated data, because you're not pointing to a particular instance of that object (ie, you have seven MainForms. Which one are you calling this function on?) Since standard WinForm design expects you could have seven copies of MainForm displaying, all variables associated are going to be instance variables, or non-static. However, if you expect never to have a second MainForm, then you can take the "singleton" approach, and have an easy way of accessing your one instance.
partial class MainForm {
// only including the code that I'm adding; I'm sure there's a lot of stuff in your form.
public static MainForm Instance { public get; private set; }
protected void onInitialize() { // You need to hook this part up yourself.
Instance = this;
}
}
partial class SubForm {
protected void onImportantButton() {
MainForm.Instance.doImportantThing()
}
}
Putting too much active data-changing logic in form classes is a pretty common issue with many beginners' code. That's not a horrible thing - you wouldn't want to be making 5 controlling classes just for a simple thing you're trying. As code gets more complex, you start to find some things would make more sense to move to a "sublevel" of classes that don't interact with the user (so, some day, if this is being re-coded as a server program, you could throw away the form classes, and just use the logic classes - theoretically speaking). It also takes some time for many programmers to understand the whole concept of object "instances", and the "context" that a function is called in.

Change TopMost property on a DIFFERENT form?

So in my program I have a settings page. On the settings page, there is an option to set the program "Always on Top". When this option is checked and unchecked, it properly saves the setting, but it does not actually change TopMost property itself.
The program's main form is called "MainForm", but the settings page is called "SettingsForm". How would I change the "TopMost" property on "MainForm", from within the "SettingsForm"?
You could create an event on Settings form:
public event EventHandler TopMostEvent;
private void OnTopMostEvent()
{
if (TopMostEvent != null)
{
TopMostEvent(this, EventArgs.Empty);
}
}
On CheckedChanged event call the method after saving settings:
OnTopMostEvent();
And in Main form subscribe to the event and set the forms TopMost property
One approach would be to simply give SettingForm a reference to MainForm, e.g. via a constructor parameter which is then stored to a field where it can later be accessed when necessary.
For example:
public class SettingsForm
{
public SettingsForm(MainForm mainForm)
{
this.mainForm = mainForm;
}
public void Apple()
{
this.mainForm.TopMost = true;
}
private readonly MainForm mainForm;
}
public class MainForm
{
public void Banana()
{
var settingsForm = new SettingsForm(this);
settingsForm.ShowDialog();
}
}
(However, it may not be necessary to do this if the owner of SettingsForm is already the insntance of MainForm but this I cannot tell from what you have given.)
This is a good place for a mediator pattern. (Similar to a controller) The idea is you have one object that creates all of your windows and passes a reference to itself into each form through the constructor. You can call a method in the mediator from either form and the mediator will focus the MainForm. It's a very common practice in Windows Forms.
So you'll make a mediator class like so:
public class MyMediator
{
Form mainForm {get;set;}
Form settingsForm{get;set;}
public MyMediator()
{
mainForm = new MainForm(this);
mainForm.Show();
}
...
public FocusMainForm() // call this from settings form
{
mainForm.TopMost = true;
}
}

c# Accessing WinForm control properties from another class

How does one access WinForm controls such as ProgressBar properties from another class?
Please see my code below. I know this might not be the best option to expose WinForm class and its members as public but I am trying to clear the concept at this point.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void button1_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
c.loop();
}
public void PBSetup()
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
}
public void PBUpdate(int recno)
{
progressBar1.Value = Class1.recno;
}
}
}
namespace WindowsFormsApplication1
{
class Class1
{
public static int recno;
public void loop()
{
//How do I access Form1.PBSetup()??
for (recno = 0; recno <= 100; recno++)
{
//How do I access Form1.PBUpdate(recno)??
}
}
}
}
You do not want your business logic (your class) interacting with your UI (your form). The business logic should be agnostic of the presentation layer.
If you want the form to respond to things that happen inside the class, you could consider exposing an Event inside the class that the form could subscribe to, much like it would subscribe to a button's click event. The class instance could fire off the event completely unaware of who might be listening, and any subscribers would be notified.
This looks like a big time code smell :).
You would need an instance of Form1 inside of Class1 in order to PBUpdate.
Something tells me what you are doing is just not right.
Explain what you are trying to do and we can help. Otherwise there is no way to access PBUpdate unless you either made it a static function where you could call it like Form1.PBUpdate() or you had an instance of Form1 within your class Class1
You can change the access modifiers of the progress bar from private to Internal or public , you can do this operation from properties pane .
Keep in mind that you have to pass to the second class the instance of the form and then you can change the value of the progress bar directly from the second class.
However is a tricky solution, the best should be keep the presentation layer implementation separated and work with an event.
I do not recommend to use this method, for simple reason as mentioned here by one of the comments. But if you really want to access that form control, here is how:
1) Select that control and set its access modifier to internal.
2) Assume your form id is "Form1" and control id is "control1"
Inside your method:
Form1 form = (Form1)Application.OpenForms["Form1"];
// form.control1 should now be available.

Showing a hidden form

How do i show a from that have been hidden using
this.Hide();
I have tried
MainMenuForm.Show();
and this just says i need an object ref. I then tried:
MainMenuForm frmMainMenu = new MainMenuForm();
frmMainMenu.Show();
Which seems to show the appropriate form. But when you exit the app, it is still held in memory because it hasn't shown the form that was hidden, instead it has shown a new version of the form. In effect having 2 instances of the form (one hidden, one visible).
Just to clarify, the MainMenuForm is the startup form. When (for example) Option 1 is clicked, the MainMenuForm then hides itself while opening up the Option 1 form. What i would like to know is how to i make the Option 1 form that the MainMenuForm opens "unhide" the MainMenuForm and then close itself.
What's the correct procedure here?
Thanks in advance.
When you do the following:
MainMenuForm frmMainMenu = new MainMenuForm();
frmMainMenu.Show();
You are creating and showing a new instance of the MainMenuForm.
In order to show and hide an instance of the MainMenuForm you'll need to hold a reference to it. I.e. when I do compact framework apps, I have a static classes using the singleton pattern to ensure I only ever have one instance of a form at run time:
public class FormProvider
{
public static MainMenuForm MainMenu
{
get
{
if (_mainMenu == null)
{
_mainMenu = new MainMenuForm();
}
return _mainMenu;
}
}
private static MainMenuForm _mainMenu;
}
Now you can just use FormProvider.MainMenu.Show() to show the form and FormProvider.MainMenu.Hide() to hide the form.
The Singleton Pattern (thanks to Lazarus for the link) is a good way of managing forms in WinForms applications because it means you only create the form instance once. The first time the form is accessed through its respective property, the form is instantiated and stored in a private variable.
For example, the first time you use FormProvider.MainMenu, the private variable _mainMenu is instantiated. Any subsequent times you call FormProvider.MainMenu, _mainMenu is returned straight away without being instantiated again.
However, you don't have to store all your form classes in a static instance. You can just have the form as a property on the form that's controlling the MainMenu.
public partial class YourMainForm : Form
{
private MainMenuForm _mainMenu = new MainMenuForm();
protected void ShowForm()
{
_mainMenu.Show();
}
protected void HideForm()
{
_mainMenu.Hide();
}
}
UPDATE:
Just read that MainMenuForm is your startup form. Implement a class similar to my singleton example above, and then change your code to the following in the Program.cs file of your application:
Application.Run(FormProvider.MainMenu);
You can then access the MainMenuForm from anywhere in your application through the FormProvider class.
The simplest and easiest way is to use LINQ and look into the Application.OpenForms property. I'm assuming you have only 1 instance of the form (hopefully!), otherwise make sure to have to have some public property on the hidden form to be able to differentiate it.
The following code will un-hide the form for you:
var formToShow = Application.OpenForms.Cast<Form>()
.FirstOrDefault(c => c is MainMenuForm);
if (formToShow != null)
{
formToShow.Show();
}
You need to keep a reference to the first form when it's created and then the code that holds that reference can call Show on it.
If you don't open that form from somewhere but it's set as the startup form, then you either need to change it so that you have a Main method that opens that form or you can have that form store a reference to itself somewhere that can be accessed from other places.
For example, an quick and ugly way would be to, add a public static property to your mainform and then when you hide the form it also writes this to that property which can then be retrieved when needed by other parts of the code.
Practically This works for me....
public class MainWindow : Form
{
Form _mainMenuForm = new MainMenuForm();
}
calling it through a button click event.
private void buttonclick()
{
if (_mainMenuForm.Visible)
{
_mainMenuForm.Visible = false;
}
else
{
_mainMenuForm.Visible = true;
}
}
Store a reference to the form and call .Hide() and .Show() on that.
For example:
public class MainWindow : Form
{
private Form _mainMenuForm = new MainMenuForm();
public void btnShowMenuForm_Click(...)
{
_mainMenuForm.Show();
}
public void btnHideMenuForm_Click(...)
{
_mainMenuForm.Hide();
}
//etc
}
This example assumes you have a form which is launching the MainMenuForm.
Call the referenced form.
Like:
Calling parent
----------
public MyForm f {get;set;}
void DoStuff()
{
f = new MyForm();
f.Show();
}
MyForm
----------
void DoOtherStuff()
{
this.hide();
}
Parent
----------
void UnHideForm()
{
f.show();
}
Another simpler method to achieve this is to loop through the open forms to see which are still running and open it...
foreach (Form oForm in Application.OpenForms)
{
if (oForm is MainMenuForm)
{
oForm.Show();
break;
}
}

Categories