I am using the NavigationItem to drive the navigation of the application. For all of my controllers inheriting from DialogViewController, the back button doesn't show, even if I do
BackButtonBarItem = news UIBarButtonItem(..)
The back button never shows only for dialogs, but does for every other type of controller. Am I missing something?
Use the overloaded DialogViewController constructor
public DialogViewController (RootElement root, bool pushing)
the bool pushing parameter should be true if your DVC is being pushed onto a NavigationController.
use
public YourDialog () : base (UITableViewStyle.Grouped, null, true)
instead of
public YourDialog () : base (UITableViewStyle.Grouped, null)
Related
I need to respond to the NSStatusBarButton click to show a menu, which unfortunately only has the property "Action" to add a click handler.
The "Action" property requires a selector.
The method I use to handle the click is the following:
[Action("StatusBarClicked:")] public void StatusBarClicked(NSObject sender)
{
// do something
}
And I do the following to assign the button action:
statusBarButton.Action = new Selector("StatusBarClicked:");
Now, it all works when I do this in the DidFinishLaunching method of the app delegate and the StatusBarClicked method is a method of the AppDelegate class.
When I wrap the status bar code in a separate class and declare the method and the selector in that class, it doesn't work (StatusBarClicked is not called).
Strangely enough, if I keep the StatusBarClicked method also in the AppDelegate, that one is called instead:
public class SomeClass
{
public void Test()
{
var statusItem = NSStatusBar
.SystemStatusBar.CreateStatusItem(NSStatusItemLength.Square);
var button = statusItem.Button;
button.Image = NSImage.ImageNamed("test");
button.Action = new Selector("StatusBarClicked:");
}
[Action("StatusBarClicked:")] public void StatusBarClicked(NSObject sender)
{
// never called
}
}
I must be missing something, any idea?
When you set an action the selector is sent to the Target property of the NSControl when the action triggers.
If the target is null the application travels up the responder chain to find the first object that responds to the selector. The app delegate is in the responder chain, that is why that works.
https://developer.apple.com/documentation/appkit/nscontrol/1428885-target?language=objc
If you want to use your custom class as the target I think it needs to subclass NSObject
After you make sure your class extends NSObject you can add this to your constructor:
button.Target = this;
I'm trying to put some stuff that I use a lot into separate classes so it's easier to implement when starting a new project.
One of the things that I would like to do, is dynamically create a statusbar on my mainform. I have done this in a previous project and there it worked fine. So I copied the code and I changed the NameSpace for the mainform.
When I run the code it stops at the line
MainForm.Controls.Add(status);
When I look, it says Mainform is null.
Other than the Namespace I haven't changed anything.
Does anybody have an idea why this is happening?
Thanks
Kenneth
//THIS IS THE SEPARATE CLASS
public class Tools
{
public Form MainForm;
public void setupForm()
{
// LINK THE FORM
MainForm = myNamespace.Form1.MainForm;
// CREATE A STATUSBAR
StatusStrip status = new StatusStrip();
status.Name = "status";
// I'VE REMOVED SOME OF THE DYNAMIC CREATION STUFF FOR READABILITY
// ADD THE STATUSSTRIP TO THE FORM
MainForm.Controls.Add(status);
}
//THIS IS THE MAINFORM
public static Form1 MainForm;
public myNameSpace.Tools tools;
private void setupForm()
{
this.KeyPreview = true;
// LINK THE TOOLS CLASS
tools = new myNameSpace.Tools();
// SETUP THE FORM
tools.setupForm();
}
You have to pass a refernece of your main form to the Tools class. You can do this when you initialize tools or when you call the method setupForm. I implemented the second possibility for you:
//the call:
tools.setupForm(this);
//the implementation of the method
private void setupForm(Form1 MainForm)
{
//your method code
}
The normal way to separate responsibility is to inject the object you want to affect - not hijack it with a hardcoded reference.
Try injecting the form when you create your tools object:
tools = new myNameSpace.Tools(this);
Its null because you don't initiate or have a refference to the main window. You just create an alias for the namespace but not for the instance.
Pass the mainWondow as a parameter to the setupForm function. Then it will work.
I have a problem. I am trying to access a panel in a user control.When I access it in a form it works. Earlier on I did this.
I accessed a panel in a form from a user control and it worked. Below is the code I used:
Form1 form = Application.OpenForms.OfType<Form1>().FirstOrDefault();
form.Panel1.Controls.Clear();
ManageControl user = new ManageControl();
form.Panel1.Controls.Add(user);
But when I try to use the very same concept in a user control it does not work.
It throws a null error: Object reference not set to an instance of an object.
Below is the code:
//this is in ManageControl.cs
public Panel Panel2
{
get { return panelmanage; }
}
//this is in another userControl.Trying to access panelImage
ManageControl form = Application.OpenForms.OfType<ManageControl>().FirstOrDefault();
form.Panel2.Controls.Clear();//it throws the error here
ReportControl user = new ReportControl();
form.Panel2.Controls.Add(user);
What am I doing wrong because I am using the same concept?
EDIT:
This is my ManageControl.cs
public partial class ManageControl : UserControl
{
public ManageControl()
{
InitializeComponent();
}
public Panel Panel2
{
get { return panelmanage; }
}
This is how I try to access it in BookingListControl
public partial class BookingListControl : UserControl
{
ManageControl form = Application.OpenForms.OfType<ManageControl>().FirstOrDefault();
public BookingListControl()
{
InitializeComponent();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ManageControl form = Application.OpenForms.OfType<ManageControl>().FirstOrDefault();
ReportControl user = new ReportControl();
form.Panel2.Controls.Add(user);
}
ManageControl is a UserControl not a Form. Thus, when you look for open forms of type ManageControl, you get nothing (no surprises here). Then, we you call FirstOrDefault it returns null (since there were no matching elements in the empty collection), and your next line blows up.
This approach is doomed from the start, because even if you had a whole bunch of forms overlaying each other and could make it work (bad idea), it would break once you had two ManageControl objects and needed to access the second.
Instead, first ask yourself, "Why do my UserControl objects need to access each other?". This is an important question, because in general UserControls are independent. They likely have methods to give data back to their parent, but thats it. They certainly don't interact with other UserControls.
If you decide that you really need this dependency, then I would pass the parent Form object to both UserControls and have a public property on the Form that allows them to see the other UserControl. From here you can access it normally (without needing any OpenForms nonsense). Honestly though, this is a massive code smell and it sounds like the whole design should be looked at to see where you have dependencies that could be removed.
To do this you need to expose the ManageControl on the form:
public class ParentForm : Form
{
public ManageControl Manager { get { return manageControlInstance; } }
...
}
Then access it in your child control. The easiest way would be through the Parent property, but you could pass it on the constructor or an Init function as well.
public class ChildControl : UserControl
{
private void SomeFunction()
{
(Parent as ParentForm).Manager.Panel2.Controls.Add(new ReportControl());
}
}
The code's pretty ugly, and I wouldn't recommend it (its also not safe if you put the ChildControl into anything other than a ParentForm). That being said it would work.
Assuming that the NullReferenceException occurred based on accessing the "Panel2" property, your issue is that "panelmanage" is null. Is the code that finds and populates "form.Panel2" in a form or control constructor? If so, try restructuring it to run after ManageControl is fully initialized - perhaps by putting it into a Loaded event.
I have MDI Forms application and I use SingletoneFormProvider because it's required to have only one instance of a form in a time.
I have Edit forms that can be used for creating new records or changing existing ones. The problem is with the editing of an existing record.
What I've done so far :
I have a method that calls the static method from the SingletoneProviderForm class and get fresh instance of the form :
protected void LoadForm<T>(ToolStripItem formButton, string buttonText, long? loadEntityId = null, bool closeAlreadyOpened = true) where T : BaseForm
{
//Some code..
openForm = SingletonFormProvider.GetInstance<T>(parentFrm, closeAlreadyOpened);
openForm.LoadEntityId = loadEntityId;
openForm.MdiParent = parentFrm;
openForm.Dock = DockStyle.Fill;
openForm.Show();
openForm.Activate();
//More code..
}
I have three delcarations of the SingletonFormProvider.GetInstance<T> :
static public T GetInstance(Form owner)
where T : Form
static public T GetInstance(Form owner, bool closeAllButThis)
where T : BaseForm
static public T GetInstance(Form owner, bool closeAllButThis, params object[] args)
where T : Form
Till now to get a fresh instance of any form I did this :
LoadForm();
If I want to send an Id then just :
LoadForm(ID);
And here comes the problem. Because I try to populate the Form fields with data from the record with the provided ID I make a query :
entity = anyForm.Find(LoadEntityId.Value);
And all this actually worked fine while I was doing it in the form_load event. But now when I move this part in the constructor everytime I do the check :
if (LoadEntityId.HasValue)
It never has because it seems that the way the form instance is constructed I don't have access to the ID from the constructor.
What I did :
Made a change in the LoadForm() method:
if (loadEntityId == null)
{
openForm = SingletonFormProvider.GetInstance(parentFrm, closeAlreadyOpened);
}
else
{
openForm = SingletonFormProvider.GetInstance(parentFrm, closeAlreadyOpened, loadEntityId);
}
Made my default constructor protected and remove everything from it :
protected AnyForm()
{}
Made another constructor that takes one argument:
public AnyForm(long? LoadEntityId)
{
long? AnyFormID = LoadEntityId.Value;
InitializeComponent();
//All the stuff that I needed
}
So it was amazing for me that it works. I have big doubts that this is the way to accomplish this. So please review and guide me to the correct way of doing this. And of course, if this is somehow the correct way please verify because I'm in doubt if it's ok to put this in production code.
P.S
As Matthew Watson mentioned the form behave weird without default constructor so I end up with one default (no argument) constructor and another one taking one argument - the ID I need. Which makes it even worse, now I have duplicated code. I'm pretty sure that's not how it's done.
I want to either enable or disable a button from another file,what should I do?
This is the form class declaration:
public partial class Form1 : Form
I tried with
Form.btnName.enabled = false/true
but there's no btnName member.
Thanks in advance!
Simply expose a public method:
public void EnableButton(bool enable)
{
this.myButton.Enabled = enable;
}
Correction:
public void EnableButton()
{
this.myButton.Enabled = true;
}
You need to expose the btnName member to other classes by making it public or using a property of sorts. For example add the following code to Form1
public Button ButtonName { get { return btnName; } }
Now you can use form.ButtonName for any instance of Form1
I really suggest to read more information on how forms fit in .net. You have a couple issues in that sample code "Form.btnName.enabled = false/true"
Your form is called Form1, it inherits from Form.
Forms are instances, in fact you can have different form instances in an application belonging to the same class.
Because of the above, it would not make sense to access Form1.btnName. You have to do it through the specific instance.
Form's controls are not public by default, define a method for that.
Windows forms projects, usually have a main that runs the form. There you can access the form instance and hand it to something else in the app.
The above answers the specific question. Note that there are multiple ways to achieve different scenarios, and what you really want to do might not need the above approach.
This is because by default, the controls on a form are not public (unlike in VB6 which all controls were exposed publicly).
I believe you can change the visibility accessor in the designer to public, but that's generally a bad idea.
Rather, you should expose a method on your form that will perform the action on the button, and make that method accessible to whatever code you want to call it from. This allows for greater encapsulation and will help prevent side effects from occurring in your code.
You'll have to specify it on your specific instance of Form1.
Ie: If you have something like Form1 myForm = new Form1(...);, then you can do myForm.btnName.Enabled = false;
This will also require that btnName is public. It would be "better" to make a property or accessor to retrieve it than directly provide public access to the, by default, private button field member.
You need to add a public property, or method to set the button.
public void DisableBtnName()
{
this.btnName.Enabled=false;
}
public Button BtnName
{
get { return this.btnName;}
}
In Form1, create a object for external class(add button name in the parameter)
Class1 obj_Class1 = new Class1(btnName);
In Class1 , create a private button
private System.Windows.Forms.Button btnName;
In Class1 Construct
public Class1(System.Windows.Forms.Button btnName)
{
this. btnName = btnName;
}
then you can access your button like,
btnName.enabled = false/true;