I have a little problem with windows forms in c#.
Let's keep it simple: I have a method which sets the default background color and foreground color. I have multiple forms from which I want to call it and I want have only one method (keep the possibility to add a default background image, etc... ). How should I do it ?
This is the basic code:
public void LoadGraphics() {
this.BackColor = Graphics.GraphicsSettings.Default.BackgroundColor;
this.ForeColor = Graphics.GraphicsSettings.Default.ForegroundColor;
this.BackgroundImage = new Bitmap(Graphics.GraphicsResources.bg_small);
}
Create a parent class that implements the method and derive your Forms from that parent class:
class Foo : Form {
void LoadGraphics() {
this.BackColor = Graphics.GraphicsSettings.Default.BackgroundColor;
this.ForeColor = Graphics.GraphicsSettings.Default.ForegroundColor;
this.BackgroundImage = new Bitmap(Graphics.GraphicsResources.bg_small);
}
}
class YourForm : Foo {
void someFunction() {
LoadGraphics();
}
}
You can create a static class that contains the code to be shared between your forms:
static class Utils
{
public static void ChangeColor(Form form, Color color)
{
form.BackColor = color;
}
}
Then you can call this function from any other form:
Utils.ChangeColor(this, Color.Red);
Related
Inside my public partial class frmMain : Form I have private class Tile.
Inside the class Tile I have private PictureBox pic = new PictureBox();.
I have registered a click event for those pictureBoxes (inside the Tile class):
public void Initialize()
{
pic.Click += new EventHandler(swap);
}
When I click a pictureBox I want to be able to see textbox1.Text.
private void swap(object sender, EventArgs e)
{
// code here //
if (won)
{
MessageBox.Show(textBox1.Text);
}
}
How can I make textbox1.Text and some other fields of my frmMain instance visible?
The quickest solution would be to pass frmMain as parameter to Tile class. Tile class would be:
private class Tile {
private frmMain frm;
//constructor
public Tile(frmMain frm) {
this.frm = frm;
}
... your code ...
//now you can
private void swap(object sender, EventArgs e)
{
if (won)
{
MessageBox.Show(frm.textBox1.Text);
}
}
}
Remember that the textBox1 have to be public accessible from frmMain.
The cleanest solution would be to expose interface from frmMain and pass it as parameter to Tile class.
Interface
public interface IfrmMain_GetText
{
string gettextBox1Text();
}
frmMain
public partial class frmMain : Form, IfrmMain_GetText
{
... your code ...
public string gettextBox1Text(){
return textBox1.Text;
}
}
Tile class
private class Tile {
private IfrmMain_GetText frmInterface;
//constructor
public Tile(IfrmMain_GetText frmInterface) {
this.frmInterface = frmInterface;
}
... your code ...
//now you can
private void swap(object sender, EventArgs e)
{
if (won)
{
MessageBox.Show(frmInterface.gettextBox1Text());
}
}
}
How can I make textbox1.Text and some other fields of my frmMain instance visible?
In C# windows forms, controls are private by default. To change the access modifier, you can change it like below:
Right click the control > Properties > Change dropdown as shown below
I would change it to internal, no need to make it public in your case.
That means classes outside the class where the control is (frmMain) can access the control. But your inner class will need a reference to frmMain so it can access the controls of frmMain. To do that, you can do it in the following ways:
Option 1
You can pass the frmMain reference to a class in the constructor:
public class SomeClass
{
private frmMain someForm;
public SomeClass(frmMain someForm)
{
this.frmMain = someForm;
// Now you can do this
var ctrl = this.frmMain.WhateverControlYouNeedToAccess;
string controlText = ctrl.Text; //assuming it has Text property
}
}
During creation of the SomeClass, pass your form to it:
// this reference to the current instance
// this code will be in your form
SomeClass some = new SomeClass(this);
Option 2
In your class have a property so it is settable from outside like this:
public class SomeClass
{
public frmMain SomeForm { get; set; }
}
You will set the property like this:
SomeClass some = new SomeClass();
some.SomeForm = this;
Option 3
Only give the class the minimum it needs. You do not need to give it a reference to the whole form but only one control (or more), then pass the control specifically like this:
SomeClass some = new SomeClass();
some.PictureBoxA = this.pictureBox1;
some.Button1 = this.button1;
For this to work, your class needs to have the properties for the above. So your class will have properties like this:
public class SomeClass
{
public PictureBox PictureBoxA { get; set; }
public Button Button1 { get; set; }
}
You need to pass a form-reference to the nested class. You can do so through constructor.
private class Tile
{
private frmMain _frm;
public Tile (frmMain frm)
{
_frm = frm;
}
private void swap(object sender, EventArgs e)
{
if (won) {
MessageBox.Show(_frm.textBox1.Text);
}
}
}
You either need to make textBox1 public or to encapsulate it in a public property.
A cleaner way is to hold the information in a data class and to use object binding to bind its properties to the textboxes. Then you can pass this data object to the Tile class.
See: A Detailed Data Binding Tutorial on CodeProject.
I don't know what kind of data is displayed on your form, but if tile data is displayed, then the Tile class could act as data class and you could bind the tile object to your form.
I have a windows forms application in C#. I want to give facility to user to change the font size of all the screens.
Is there any way to do this in C# windows forms application?
You could create an event which you fire whenever you change the font size which takes the new value
public delegate void FontSize(int size);
public event FontSize OnFontSizeChanged;
public void WhereYouChangeFontSize()
{
// Change font size
OnFontSizeChanged(newFontSize)
}
And then hook up to it in all of your screens
SomeClass.OnFontSizeChanged += FontSizeChanged;
private void FontSizeChanged(int newValue)
{
controls.FontSize = newValue;
}
The elegant and acceptable way to do it is using a resource file. You should investigate in that way.
You can create BaseForm for all your Screens. This BaseForm subscribe to ChangeFontMessage. For messaging you can use any EventAggregator library. This example use MVVM Light Messenger.
public class BaseForm : Form
{
public BaseForm()
{
Messenger.Default.Register<ChangeFontMessage>(this, message =>
{
SetFont(message.FontSize);
});
}
private void SetFont(float fontSize)
{
Font = new Font(Font.FontFamily, fontSize);
//If you need to change font size of child controls
foreach (var control in Controls.OfType<Control>())
{
control.Font = new Font(control.Font.FontFamily, fontSize);
}
}
}
public class ChangeFontMessage
{
public float FontSize { get; set; }
}
Then you can raise this message in any place:
Messenger.Default.Send(new ChangeFontMessage { FontSize = 20 });
In my application I have got an option to customise display a bit. Basically, now it is about changing buttons' and pictureboxes' images, changing form icons etc. As you can see in the code I provided bellow. So, in this case, I have done this by simply loading a bunch of resources (images) and then when I change theme:
if (Properties.Settings.Default.Theme == "Purple")
{
foreach (var form in Application.OpenForms.Cast<Form>())
{
form.Icon = Properties.Resources.Purple;
}
Main f1 = (Main)Application.OpenForms["Main"];
Settings f2 = (Settings)Application.OpenForms["Settings"];
f1.btn_Exit.Image = Properties.Resources.EXIT_purple;
f2.btn_SaveSettings.Image = Properties.Resources.SaveSettings_purple;
f1.pictureBox1.Image = Properties.Resources.Preview_purple;
}
This dramatically increases the size of the application, so I came up with changing BackColor instead of loading another images. I have tried to solve this in the following way:
if (Properties.Settings.Default.Theme == "Purple")
{
foreach (var form in Application.OpenForms.Cast<Form>())
{
form.Icon = Properties.Resources.Purple;
}
Main f1 = (Main)Application.OpenForms["Main"];
Settings f2 = (Settings)Application.OpenForms["Settings"];
f1.btn_Exit.BackColor = Color.FromArgb(164, 57, 226);
f2.btn_SaveSettings.BackColor = Color.FromArgb(164, 57, 226);
f2.pictureBox1.BackColor = Color.FromArgb(164, 57, 226);
}
So, form icon should be changed on all running forms and I have managed to successfully do so, but when it comes to buttons and pictureboxes, I am not seeing any results, that is buttons and pictureboxes are simply not changing. Besides form icons, I should change btn_Exit located on the Main form, btn_SaveSettings located on the Settings form, and pictureBox1 located on the Settings form as well.
How can I solve this issue?
If it was me doing it I would make an interface. This interface would have a reference to the correct picture/icon/color for your different controls. The Settings for you application would hold an instance of the correct theme. Each form I would OnLoad update all the necessary items and call it a day. It's probably not the most elegant solution, but it would make it easy and quick when you go to add more themes.
This is what I was thinking about. I hope this clears up my approach.
public class Settings
{
public static ITheme Theme {get {return theme;}{set theme = value}}
theme = new DefaultTheme();
}
public interface ITheme
{
public Color BackgroundColor {get;}
public Color ButtonBackgroundColor { get;}
//... etc
}
public class DefaultTheme : ITheme
{
public Color BackgroundColor {get{ return Color.White;}}
public Color ButtonBackgroundColor { get { return Color.Gray;}}
}
from here you have 2 approaches. Both require you to put a Event "OnLoad" If you have 1 or 2 forms maybe put this code each Form (not recommended) or you could use an extension like method.
here is first approach. put the code directly in each frame.
public class SaveFrame : Frame
{
public SaveFrame()
{
InitializeComponents();
}
public Form_OnLoad()
{
var theme = Settings.Theme;
this.Background = theme.BackgroundColor;
foreach(Button b in this.Controls)
{
if(b != null)
b.Background = theme.ButtonBackgroundColor;
}
}
}
or create an extension method. and put it in the OnLoad
public static class FormExtensions
{
public static void UpdateTheme(this Form form, ITheme theme)
{
form.Background = theme.BackgroundColor;
foreach(Button b in form.Controls)
{
if(b != null)
b.Background = theme.ButtonBackgroundColor;
}
}
}
public class SaveFrame : Form
{
public SaveFrame()
{
InitializeComponents();
}
public Form_OnLoad()
{
this.UpdateTheme(Settings.Theme);
}
}
public class MainFrame :Form
{
public MainFrame()
{
InitializeComponents();
}
public Form_OnLoad()
{
this.UpdateTheme(Settings.Theme);
}
}
I have changed my app.config file to allow the user to change the color scheme of the program. I can figure out how to change the background color of the form they are on where they change these settings:
Color colBackColor = Properties.Settings.Default.basicBackground;
this.BackColor = colBackColor;
But how can I change all of my forms background color? It's like I still want to pass all my forms to a function. I already asked that question and someone told me to use the app.config file. Now that I have done that, am I using it wrong?
It's simply that you need a base form from which all your forms in your project have to inherit:
public class FormBase : Form {
protected override void OnLoad(EventArgs e){
Color colBackColor = Properties.Settings.Default.basicBackground;
BackColor = colBackColor;
}
}
//Then all other forms have to inherit from that FormBase instead of the standard Form
public class Form1 : FormBase {
//...
}
public class Form2 : FormBase {
//...
}
UPDATE
public interface INotifyChangeStyle {
void ChangeStyle();
}
public class FormBase : Form, INotifyChangeStyle {
protected override void OnLoad(EventArgs e){
ChangeStyle();
}
public void ChangeStyle(){
//Perform style changing here
Color colBackColor = Properties.Settings.Default.basicBackground;
BackColor = colBackColor;
//--------
foreach(var c in Controls.OfType<INotifyChangeStyle>()){
c.ChangeStyle();
}
}
}
public class MyButton : Button, INotifyChangeStyle {
public void ChangeStyle(){
//Perform style changing here
//....
//--------
foreach(var c in Controls.OfType<INotifyChangeStyle>()){
c.ChangeStyle();
}
}
}
//... the same for other control classes
I have a windows forms application with some controls added to the designer. When I want to change something (LIKE) enabling a text box from inside the Form1.cs, I simply use:
textBox1.Enabled = true;
but now I have a separated class called class1.cs.
How could I enable textBox1 from a static function class1.cs?
NOTE: I did not try any code because I am totally clueless about doing this.
EDIT: Lot of edit.
public partial class Form1 : Form
{
// Static form. Null if no form created yet.
private static Form1 form = null;
private delegate void EnableDelegate(bool enable);
public Form1()
{
InitializeComponent();
form = this;
}
// Static method, call the non-static version if the form exist.
public static void EnableStaticTextBox(bool enable)
{
if (form != null)
form.EnableTextBox(enable);
}
private void EnableTextBox(bool enable)
{
// If this returns true, it means it was called from an external thread.
if (InvokeRequired)
{
// Create a delegate of this method and let the form run it.
this.Invoke(new EnableDelegate(EnableTextBox), new object[] { enable });
return; // Important
}
// Set textBox
textBox1.Enabled = enable;
}
}
This is just another method:
TextBox t = Application.OpenForms["Form1"].Controls["textBox1"] as TextBox;
You shouldn't really change UI controls in your Form from your class1, but instead create a method or a property in class1 that would tell if the textbox should be enabled or not.
Example:
// I changed the name class1 to MySettings
public class MySettings
{
public bool ShouldTextBoxBeEnabled()
{
// Do some logic here.
return true;
}
// More generic
public static bool SetTextBoxState(TextBox textBox)
{
// Do some logic here.
textBox.Enabled = true;
}
// Or static property (method if you like)
public static StaticShouldTextBoxBeEnabled { get { return true; } }
}
Then in your form:
MySettings settings = new MySettings();
textBox1.Enabled = settings.ShouldTextBoxBeEnabled();
// Or static way
textBox1.Enabled = MySettings.StaticShouldTextBoxBeEnabled;
// Or this way you can send in all textboxes you want to do the logic on.
MySettings.SetTextBoxState(textBox1);
You can pass the instance of your Form to the class
MyForm frm = new MyForm();
MyClass c = new MyClass(frm);
Then your class can take that instance and access the textbox
public class MyClass
{
public MyClass(MyForm f)
{
f.TextBox1.Enabled = false;
}
}
The design does not look OK
It is better to call the class in your form and based on the value returned, manipulate the textbox
//MyForm Class
MyClass c = new MyClass();
c.DoSomethings();
if(c.getResult() == requiredValue)
textBox1.enabled = true;
else
textBox1.enabled = false;
//MyForm Class ends here
UPDATE
public class Class1
{
public static int SomeFunction()
{
int result = 1;
return result;
}
public static void SomeFunction(out int result)
{
result = 1;
}
}
Usage
if(Class1.SomeFunction() == 1)
textBox1.Enabled = true;
else
textBox1.Enabled = false;
OR
int result = 0;
Class1.SomeFunction(out result);
if(result == 1)
textBox1.Enabled = true;
else
textBox1.Enabled = false;
You could let your class1 have an event to enable the Textbox.
public class Class1
{
public event Action<object, EventArgs> subscribe ;
private void raiseEvent()
{
var handler = subscribe ;
if(handler!=null)
{
handler(this,EventArgs.Empty);//Raise the enable event.
}
}
}
Let the class containing the TextBox subscribe to it somehow. In TextBox wrapper class
public class TextBoxWrapper
public void EnablePropertyNotification(object sender, EventArgs args)
{
TextBox1.Enabled = true ; //Enables textbox when event is raised.
}
public TextBoxWrapper()
{
class1Instance.subscribe+=EnablePropertyNotification ;
}
To access/modify a Form Element property, just write this in your outside Class.
Form1.ActiveForm.Controls["textBox1"].Enabled = true;
Where textBox1 is the variable name of TextBox.
What this actually does: Gets the active Form object's control specified by the name in string.
WARNING: Active form means the form which is currently open and focused on. If you do something else on your computer, with your minimized WindowsForm application, the Form1.ActiveForm will not get the form, instead, it will give null, which can lead to errors later. Be careful!
based on the answer from #vr_driver you can do that to avoid problems with other containers like groupbox, panels...
TextBox t = Application.OpenForms["Form1"].Controls.Find("textBox1", true)[0] as TextBox;
In this example you have a form called Main.cs and a class called MyClass:
In MyClass (Note: the name of my Form Class = 'Main'):
Main ui = new Main();
ui.toolStripProgressBarStickers.PerformStep();
In (FormName).Designer.cs so in my case Main.designer.cs change the appropriate control from 'private' to 'public':
public System.Windows.Forms.ToolStripProgressBar toolStripProgressBarStickers;
This solved it for me.
Thanks, Ensai Tankado
I had to do this at work and didn't find that any of these answers matched what I ended up doing, so I'm showing how I made it work.
First, initialize a copy of your class in your load event.
NameOfClass newNameofClass;
Then you want to bind to your class (in the load event):
textBox1.DataBindings.Add(new Binding("Enabled", newNameofClass, "textBox1Enabled"));
In your class, do the following:
private bool textBox1Enabled = false;
public bool TextBox1Enabled
{
get
{
return textBox1Enabled;
}
set
{
textBox1Enabled = value;
}
}
The false setting will initialize your textbox to being disabled
Set textBox1Enabled to true if you want to enable by default.
If you have other logic to enable/disable the textbox, simply modify the value of textBox1Enabled accordingly.
Very easy:
Create an Instance of your Form Object where want to access the Elements from.
Form1 ui = new Form1();
and now change the Form Elements to "public" - like this in the Designer Code:
...
public System.Windows.Forms.TextBox textBox6;
...
Now you can access them like this in your Code:
ui.textBox6 ...
I had the same problem. I used windows forms & Visual Studio to generate a UI in a utility with textbox, checkbox, and button controls but ALL the code was in the same class.
I'm rewriting the utility now that I know "more" OOP concepts and created actual objects and separate classes. I too had problems getting the separate classes to be able to access the form controls and any shared methods that are in the form class. I tried the various suggestions in this thread as well as other threads but none of those solutions worked for me.
What worked for me (not sure if its the right thing to do or not) was I had each class that needed to access the controls and forms methods inherit from the Form.
Here is the relevant part of the Form.cs file:
namespace Utility
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void WriteNote(string noteText, bool asHeading = false)
{
//Writes Messages to the Message box in the UI
Font font1 = new Font(this.ResultsTB.Font, FontStyle.Bold);
Font font2 = new Font(this.ResultsTB.Font, FontStyle.Regular);
if (asHeading)
this.ResultsTB.Font = font1;
else
this.ResultsTB.Font = font2;
this.ResultsTB.AppendText(noteText + "\r\n");
}
My Form contains a textbox called DirTB and a method called "WriteNote" that writes info to another textbox called ResultsTB. Here is the class (at least as far down as the first successful call of the WriteNote method from the Form:
namespace Utility
{
public class AppServerDTO : Form1
{
#region App Server attributes
//attributes listed here
#endregion App Server attributes
#region AppServerDTO Constructor
public AppServerDTO()
{
//These methods verify and set all the attributes
VerifyInstallFolder();
}//end of constructor AppServer
#endregion AppServerDTO Constructor
#region AppServerDTO class methods
public void VerifyInstallFolder()
{
string keypath = string.Empty;
string locationVerification = DirTB.Text + #"\SomeText";
for (int i = 0; i < 4; i++) //allows 3 attempts to get the install folder right
{
if (Directory.Exists(locationVerification))
{
i = 4;//Kills the loop
}
else if (!Directory.Exists(locationVerification))
{
locationVerification = DirTB.Text + #"\SomeMoreText";
}
else if (!Directory.Exists(locationVerification))
{
WriteNote("The directory listed in the Install Directoy box is not reachable.");
WriteNote("Please select the correct directory.");
WriteNote("The correct directory is the folder that contains the ApplicationUpdates & UpdateManager folders.");
WriteNote(#"i.e. C:\Somewhere or D:\Someplace\Somewhere");
var folderpath = FolderPrompt(#"C:\");
DirTB.Text = folderpath; //updates the install folder textbox to the new location
keypath = folderpath;
i++;
}
}//end for loop
}//end VerifyInstallFolder
As long as you are very careful with what you mark as public vs private, it should be ok.
This is how you should do :
I wrote the code below in my form class :
public static Form1 form = null;
private delegate void SetImageDelegate(Image image);
public Form1()
{
InitializeComponent();
form = this;
}
public static void SetStaticImage(Image image)
{
if (form != null)
form.pic1.Image = image;
}
private void setImage(Image img)
{
// If this returns true, it means it was called from an external thread.
if (InvokeRequired)
{
// Create a delegate of this method and let the form run it.
this.Invoke(new SetImageDelegate(setImage), new object[] { img });
return; // Important
}
// Set textBox
pic1.Image = img;
}
and the code below should be in anouther class :
Form1 frm= Form1.form;
frm.pic1.Image = image;
Note that i changed private static Form1 form = null; to public static Form1 form = null;
Good Luck ... Written by Hassan Eskandari :)