How to access a non-static property from another class - c#

I have a non-static property inside a non-static MainForm class:
public string SelectedProfile
{
get { return (string)cbxProfiles.SelectedItem; }
set { cbxProfiles.SelectedItem = value; }
}
I would like to get the value of this property, from another non-static class. Using MainForm.SelectedProfile gives an error saying "An object reference is required for the non-static field, method or property".
Usually I would solve this problem by making SelectedProfile static, but I can't, since cbxProfiles (a ComboBox control) can't be made static.
So how do I access the property's value without making it static?

You access non-static members the same way you always do: by using a reference to an instance of the object.
So whatever code you want to be able to use that property, you need to pass it a reference to a MainForm object.

As said in the compilation error, you need to have a reference of the existing MainForm instance to act on it.
// You surely do this somewhere in your code
MainForm mainForm = new MainForm();
// ...
// Use the reference to your mainForm to access its public properties
String selectedProfile = mainForm.SelectedProfile;

I might be late to the party but my solution might help someone someday down the road. You can directly access an open form's controls (even private ones) using Application.OpenForms[n]...
For example, let's assume you have created a MainForm and then created a comboBox such that it is inside MainForm => Tab (named tabControl) => TabPage (named tabPageMain) => Panel (named pnlMain) => ComboBox (named cmbSeconds). Then you can access this last control as follows:
ComboBox combo = Application.OpenForms[0].Controls["tabControl"].Controls["tabPageMain"].Controls["pnlMain"].Controls["cmbSeconds"] as ComboBox;
string SelectedProfile = (string)combo.SelectedItem;
// OR
bool isMaximized = Application.OpenForms[0].WindowState == FormWindowState.Maximized;
i.e. you've got to traverse the path from the top-level form down to that particular control. Document Outline view of Visual Studio (View menu => Other Windows => Document Outline) might help you in this bcoz you might be overlooking some transparent containers in between.
Use it cautiously. For example, if the handles of any of the referenced controls are not yet created, you might see runtime exceptions.

Related

Setting the properties of form values from another class

I have been working on an assignment with forms for a while now and everything works, however my professor want us to devide everything in seperate classes.
So what I have now is:
MainForm.cs
MainForm.Designer.cs
MainForm.resx
program.cs
In the MainForm.cs is where i have all my code and where i call the buttons, labels, textboxes etc from.
What I want to do, is to have a strucutre with other classes such as
MainForm.cs
MainForm.Designer.cs
MainForm.resx
program.cs
class1.cs
class2.cs
I tried doing this, but from my class1 i couldn't call the Design(name) of the Form since it dont exist in the context. I have been searching a lot but haven't found anything that matched my problem or how to solve it.
How can I solve this problem?
For any property to be accessible from another class (another form for instance as Form itself is a class), the property must be public, so for example lets say you have a textbox named txtSomething and you need to access its text, you may create a public property that lets you get and set its Text property:
public string SomeProperty { get { return txtSomething.Text;} set {txtSomething.Text = value;}}
You can of course change MainForm.Designer.cs and make all controls (for example textboxes) public where they are defined, but it is not a good choice at all. because you should always give public access only where it is needed. for example if you need a controls text, let just its text property be accessible (the code above).
Even, if the second form has just to get the textbox value and does need to set it, you may give a readonly access. so the code above would be:
public string SomeProperty { get { return txtSomething.Text;} }
Then assuming that the instance of FrmMain is frmMain you can access the Text property of that textbox like:
string propertyValue = frmMain.SomeProperty;

Visual Studio C# variable in properties

Is there any functionality to use a global variable in form properties window?
public partial class MainForm : Form
{
string title = "This is title";
// constructor,etc.
}
No, there isn't such functionality in the designer. The nearest you can get is using resources. You can't wire your resources directly in the designer (unless you are doing localization tricks), but you can edit your Designer.cs file and change:
this.MainForm.Text = "whatever";
for
this.MainForm.Text = Properties.Resources.MainFormTitle;
// or whatever other resource property you wish
This only works for resources (they are considered Global Objects by the designer and respected upon serializing): if you try to set any other variable (not a resource-generated property), it'll get overriden when the form is serialized again (upon saving).
Otherwise, you can just set your properties in the constructor, after InitializeComponents(), but they won't be seen in design-time unless you are inheriting that form.

Get the owner Form of a NotifyIcon?

In C# or Vb.Net, using managed or unmanaged code, how I could retrieve the owner Form of a NotifyIcon?
I've checked the base types of NotifyIcon Class and also the ComponentConverter trying to find out a possible type-casting to obtain the Form, but I was not able to.
I also seen the NotifyIcon.ContextMenuStrip.FindForm() function, but for any reason when I assign a contextmenustrip the FindForm() function always returns a null-reference exception, and anyways even if it could work will not be a safe approach because I could have a notifyicon without a contextmenu.
My intention is to pass a NotifyIcon as an argument to some methods that will perform common tasks to save me time and reduct code.
Pseudo example:
Public Shared Sub MinimizeToSystray(ByVal ntfy As NotifyIcon)
If (ntfy Is Nothing) Then
Throw New ArgumentNullException(paramName:="ntfy")
ElseIf (ntfy.Icon Is Nothing) Then
Throw New ArgumentException(message:="The NotifyIcon doesn't have an icon.",
paramName:="ntfy")
Else
Dim f As Form = ntfy.GetOwnerForm()
f.WindowState = FormWindowState.Minimized
f.Hide()
ntfy.Visible = True
End If
End Sub
The FindForm() method can only find the form for controls. The kind that derive from Control and are embedded in a form through its Parent property. But NotifyIcon is not a control, it is Component. A Component only has a Site property, its value is only defined at design time.
There is a casual relationship between a component and the form, Winforms promises to automatically dispose any components that have a constructor overload that takes an IContainer argument. Not all them do, OpenFormDialog and BackgroundWorker for example don't, NotifyIcon does. They omit the constructor when they don't need disposal.
Which makes it technically possible to find the form back. You'd need to iterate Application.OpenForms(). And use reflection to iterate their private components collection. Do note that this can only work when the form was actually opened, its Show() method must have been called.
That's a solution that scores -100 points, it is both ugly and error prone. The simple and always-correct solution is to just add an extra argument to the method to allow passing the "owner" form. With the assumption that, since the caller needs to know the NotifyIcon instance, it also should know the form. Typically Me.
Component doesn't have a FindForm method like controls and if you need such property you should customize the component and apply a workaround. Unfortunately NotifyIcon is sealed and can not be inherited.
As mentioned by Ivan Stoev in comments, you can use Tag property to store a reference to the container form.
But If you are looking for a designer based solution that works without writing such initialization codes to set the Tag, as a good option, you can create an extender component that adds a property ParentForm to your NotifyIcon component and set the property at design-time, then you can simply use it at run-time and when setting the ParentForm property at design-time you can set Tag property in component code, and then use it at run-time.
And here is the usage:
Add a the ComponentExtender (see the code at the end of post) to your project, then build the project. Then:
Add the ComponentExtender from toolbox to your form
Set the ParentForm on componentExtender1 property of your notifyIcon1 at designer using property grid:
Use this code to find the parent form of your notify icon:
var parent = this.notifyIcon1.Tag as Form`
Code
Here is an implementation that I used and works properly:
[ProvideProperty("ParentForm", typeof(NotifyIcon))]
public class ComponentExtender
: System.ComponentModel.Component, System.ComponentModel.IExtenderProvider
{
private Hashtable components;
public ComponentExtender() : base() { components = new Hashtable(); }
public bool CanExtend(object extendee)
{
if (extendee is Component)
return true;
return false;
}
public Form GetParentForm(NotifyIcon extendee)
{
return components[extendee] as Form;
}
public void SetParentForm(NotifyIcon extendee, Form value)
{
if (value == null)
{
components.Remove(extendee);
component.Tag = null;
}
else
{
components[extendee] = value;
component.Tag = value;
}
}
}

Access control's properties from another class in C# WPF

I'm in a mess with visibility between classes. Please, help me with this newbie question.
I have two controls (DatePickers from default WPF toolbox) which are in different windows, so in different classes. I can easily access these controls properties like datePicker1.Text from within its native class, i.e. in its native window, but when I try to reach datePicker1.Text from another window I get nothing.
I try to assign value of one datePicker to another, using reference to the window in my code:
string testStr;
...
AnotherWindow aw = new AnotherWindow();
testStr = aw.datePicker2.Text;
datePicker1.Text = testStr;
and it doesn't work
also I tried to do it through public property of a class, like:
public partial class AnotherWindow : Window
{
....
public string dateNearest
{
get { return datePicker2.Text; }
set { datePicker2.Text = value; }
}
....
and then use it in another window:
string testStr;
...
AnotherWindow aw = new AnotherWindow();
testStr = aw.dateNearest;
but also no value assigned.
Please, help me to understand this basic issue. I know there are other ways of accessing values in WPF like databinding, but I would like to understand basics first.
Unfortunately, the basics of WPF are data bindings. Doing it any other way is 'going against the grain', is bad practice, and is generally orders of magnitude more complex to code and to understand.
To your issue at hand, if you have data to share between views (and even if it's only one view), create a view model class which contains properties to represent the data, and bind to the properties from your view(s).
In your code, only manage your view model class, and don't touch the actual view with its visual controls and visual composition.
I'm using VS 2010 beta 2 right now which crashes regularly doing the simplest WPF coding, like trying to duplicate your question's code :) : but consider :
Is it possible that using this syntax will "do the right thing" :
public string dateNearest
{
get { return this.datePicker2.Text; }
set { this.datePicker2.Text = value; }
}
Edit 1 : Okay, I got a WPF replication of your code that didn't crash : using the above syntax I can both get and set the property in the "other window."
Edit 2 : The code also works using your original code :) Which, seemed to me to be "proper" the first time I read it. Are you setting that property before you read it ? : to my knowledge a DateTimePicker's Text property will be an empty string by default when first created.
Edit 3 : in response to Rem's request :
the main window has a button, 'button1 : which tests setting and getting the Public Property DTContent defined in the instance of the second Window named : 'WindowAdded : here's the 'Click event handler for that button in the main window's code :
private void button1_Click(object sender, RoutedEventArgs e)
{
WindowAdded wa = new WindowAdded();
wa.DTContent = DateTime.Now.ToString();
Console.WriteLine("dt = " + wa.DTContent);
}
Edit 4 : a better "real world" example : most cases you are going to want to create that instance of another window, and hold on to it, for re-use: imho : not have it exist only within the scope of a button's Click event. So consider, please :
Somewhere in the scope of the main window's code define a "place-holder" for the window(s) you will add : private WindowAdded wa;
In the event you select as most appropriate for creating the instance of that window : create the instance, and assign to your "place-holder" variable : then re-use it as needed. In WinForms I most often create required secondary windows that I will need to re-use references to the instances of to access something on them in the main form's load or shown events.
Discussion : of course, if your intent is to create "temporary" windows, and you don't need to re-use that reference to the new window's instance again, then creating it in the scope of some function is fine.
And, if the only thing you ever need to access on your second Window is the DateTimePicker, then you use the same technique suggested above, but create and hold to a reference to the instance of the DateTimePicker only.
As the others already pointed out, this is probably not the way to go, but you can use:
<object x:FieldModifier="public".../>
To set the object public.
See msdn for more info.

UserControl cannot be displayed in Designer - null object reference

I have a created a UserControl with a combobox in it. This combobox is populated from a xml, when this is not present, it is loaded from resource file.
It works fine in the program, but it can't be displayed in designer - it says: "Object reference not set to an instance of an object."
In the class responsible for loading the list from xml the null reference check is skipped for reasons beyond my understanding...
public SortedDictionary<string, string> Countries
{
get
{
if (object.ReferenceEquals(countries, null))
{
GetCountryList();
}
return countries;
}
}
Populating of the comboBox goes like this:
comboBoxCountry.DataSource = new BindingSource(Program.language.Countries, null);
Program.language is initialized in Program, but it does not help for the Designer.
The question is, how (when, at what event) should I populate the ComboBox (=load list from xml) to be able to display my control in designer.
If possible, you want to check for this.DesignMode and then simply not load the ComboBox at design-time.
Does GetCountryList() set a member variable? If so, move that call to a method. Property get accessors and the ToString() method are assumed pure: the program state before and after must be identical. Violating this assumption can cause all sorts of problems, especially designer/debugger/runtime inconsistency. Various rants have taken place, but the best thing to do is understand the assumption, follow it, and let it work to your advantage as you debug.

Categories

Resources