Creating a Common Class to share MenuControls - c#

I have a program with several different forms, most of these forms have some repetition in them especially when it comes to form manipulation controls. How do I create a common class so I don't have the same code on each form, but am still able to manipulate individual forms. For instance the code below is a ToolStripMenuControl for making the form Always on top. I have this code repeated 4 times in other forms, but I cant seem to wrap my head around how to go about creating a shared control for this. What am I missing here?
public bool onTop = false;
public bool ToggleTop()
{
if (onTop)
{
onTop = false;
this.TopMost = false;
keepOnTopToolStripMenuItem.Checked = false;
return onTop;
}
if (!onTop)
{
onTop = true;
this.TopMost = true;
keepOnTopToolStripMenuItem.Checked = true;
return onTop;
}
else return onTop;
}
private void keepOnTopToolStripMenuItem_Click(object sender, EventArgs e)
{
ToggleTop();
}

You don't need to store a separate bool onTop. Form.TopMost already tracks the exact same value.
For this simple task, your code is too long. You can rewrite it simply as:
public class Form5 : Form {
CheckBox cbTopMost = new CheckBox { Text = "Top Most" };
public Form5() {
Controls.Add(cbTopMost);
cbTopMost.CheckedChanged += delegate {
this.TopMost = cbTopMost.Checked;
};
}
}
If you really want, have all your Forms extend a base form, such as the example.

In your case I would use some base form with common implementation. It means you will move this code to some separate base class and all your forms with this ToolStripMenuControl will derive from it.
protected class FormWithToolStripControl : Form
{
//Initialization of ToolStripControl will be somewhere here
//And here your common code for all forms derived from this class
public bool onTop = false;
public bool ToggleTop()
{
if (onTop)
{
onTop = false;
this.TopMost = false;
keepOnTopToolStripMenuItem.Checked = false;
return onTop;
}
if (!onTop)
{
onTop = true;
this.TopMost = true;
keepOnTopToolStripMenuItem.Checked = true;
return onTop;
}
else return onTop;
}
private void keepOnTopToolStripMenuItem_Click(object sender, EventArgs e)
{
ToggleTop();
}
}

Related

How to use user defined property in construction method for a winform?

I made a textbox that only accepts numbers and a "-" for negative numbers. I would like there to be an option to disable negative numbers.
In the constructor method I want to reference the allowNegatives bool that is defined properties editor and do different things depending on if it allows negatives values. I'm running into the problem that the 'allowNegatives' bool is always its default value in the constructor. If I reference it elsewhere it is the correct value.
Is there an way to get the assigned property value rather than the default value in the constructor?
public partial class ControlIntEntry : TextBox
{
private bool allowNegatives = false;
[Description("Allow negative values"), Category("Behavior")]
public bool AllowNegatives
{
get { return allowNegatives; }
set { allowNegatives = value; }
}
public ControlIntEntry()
{
// user sets AllowNegatives to true using properties editor
InitializeComponent();
Console.WriteLine(allowNegatives); // returns false
if (allowNegatives)
{
//do one thing
}
else
{
// do something else.
}
Task.Run(() => AfterConstructor()); // use for testing
}
private async Task AfterConstructor()
{
await Task.Delay(1000);
Console.WriteLine(allowNegatives); //returns true
}
}
Before you can assign a value to an instance property, the class should be instantiated, so first constructor will run and then you can assign property values.
That said, to have a better understanding of what is happening here, when you drop an instance of a control on your form at design time and set some of its properties, designer will generate a code like this:
private void InitializeComponent()
{
...
this.myControl1 = new MyControl();
...
//
// myControl1
//
this.myControl1.Location = new System.Drawing.Point(0, 0);
this.myControl1.Name = "myControl1";
this.myControl1.Size = new System.Drawing.Size(100, 22);
this.myControl1.MyProperty = true;
...
}
I believe it's now clear that what is happening here. You see first the constructor of your control will run, then later property values will be set.
To use property values to configure your object can put the logic inside the setter of the property:
private bool myProperty = false;
public bool MyProperty
{
get { return myProperty;}
set
{
myProperty = value;
// some logic here.
}
}
It's the most common scenario.
Another option is delaying the initializations to some time later, for example when the control handle is created by overriding OnHandleCreated or another suitable time.
// This is just an example, the event may not be a good one for your requirement
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
// some logic here
}
Another option for complex initialization scenarios which may involve multiple properties, you can implement ISupportInitialize and put the logic inside EndInit:
public class MyControl : TextBox, ISupportInitialize
{
public void BeginInit()
{
}
public void EndInit()
{
// some logic here
}
}
Then when you drop an instance of the control on the form, this code will be generated in addition to the common code that I showed at beginning of this answer:
...
((System.ComponentModel.ISupportInitialize)(this.myControl1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
...
(I expect it's obvious now, that) All above options will run after running the constructor.
Putting that code in the setter worked
public partial class ControlIntEntry : TextBox
{
private bool allowNegatives = false;
[Description("Allow negative values"), Category("Behavior")]
public bool AllowNegatives
{
get { return allowNegatives; }
set
{
allowNegatives = value;
if (allowNegatives)
this.KeyPress += KeyPress_AllowNegatives;
else
this.KeyPress += KeyPress_PositiveOnly;
}
}
public ControlIntEntry()
{
InitializeComponent();
}
private void KeyPress_PositiveOnly(object sender, KeyPressEventArgs e)
{
Char newChar = e.KeyChar;
if (!Char.IsDigit(newChar) && newChar != 8)
{
e.Handled = true;
}
}
private void KeyPress_AllowNegatives(object sender, KeyPressEventArgs e)
{
Char newChar = e.KeyChar;
int cursorIndex = this.SelectionStart;
if (cursorIndex == 0)
{
if (!Char.IsDigit(newChar) && newChar != 8 && newChar != 45)
{
e.Handled = true;
}
}
else
{
if (!Char.IsDigit(newChar) && newChar != 8)
{
e.Handled = true;
}
}
}
}

How to call multiple methods from another form C#

I have multiple forms with the same method called "UpdateTheme" which changes the back colour of the form. I want to be able to call all of these methods from another form.
I tried to make a base form with the "UpdateTheme" method then have all other forms inherit from the base form, But I didnt know how/ if it was possible to then call every instance of the derived forms methods from a separate "Settings" form.
public abstract class CustomForm : Form
{
public void UpdateTheme(string theme)
{
if (theme == "dark")
{
this.BackColor = Color.Black;
}
else if (theme == "light")
{
this.BackColor = Color.White;
}
}
}
In the settings form I would have something like
public void btnSetThemeToDark_Click(object sender, EventArgs e)
{
foreach (instance of derived form)
{
derivedForm.UpdateTheme("dark");
}
}
Whats the best way to do this?
You could create a singleton called StyleManager that contains the global style properties. This singleton has an event called style changed that can be handled by all forms, or a base form. So all of your forms get the information from one source.
StyleManager
public class StyleManager
{
#region singleton
public static StyleManager Instance { get; } = new StyleManager();
private StyleManager()
{
}
#endregion
#region events
public event EventHandler StyleChanged;
private void OnStyleChanged()
{
this.StyleChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
#region properties
public Color BackColor { get; set; }
#endregion
#region methods
public void UpdateBackColor(Color color)
{
this.BackColor = color;
this.OnStyleChanged();
}
#endregion
}
and use it in your forms like this:
public Form()
{
this.InitializeComponent();
//Attach to the event
StyleManager.Instance.StyleChanged += this.StyleChanged;
}
//Handle event
private void StyleChanged(object sender, EventArgs eventArgs)
{
this.BackColor = StyleManager.Instance.BackColor;
}
//set backcolor of all forms
StyleManager.Instance.UpdateBackColor(Color.Yellow);
Assuming this forms are MdiChildren of the form, you would do it this way:
foreach (var form in this.MdiChildren)
{
var castedForm = form as CustomForm;
if (myObjRef != null)
{
castedForm.UpdateTheme("dark");
}
}
This functionality works beyond themes to any common method of the child forms.
I don't think this is the best way to do this. But, you can archive what you want by using this code.
CustomForm mainFrm = (CustomForm)Application.OpenForms["YouCustomFormNameHere"];
mainFrm.UpdateTheme("dark");
Replace 'YouCustomFormNameHere' with your CustomForm form name.

How can I write a C# method to clear a group of checkboxes upon button click?

New C# problem...
I'm supposed to write 6 separate methods to clear 6 groups of checkboxes (all at once) when I click the clear button. I know how to code it with individual checkboxes, but the problem asks for me to create a method and then call all 6 of them upon clicking the clear button. Help?
I have nothing associated with the clearButton_Click event yet.
private void ClearOilLube
{
set {oilChangeCheckBox.Checked = false;
lubeJobCheckBox.Checked = false;}
}
private void ClearFlushes
{
set {radiatorFlushCheckBox.Checked = false;
transFlushCheckBox.Checked = false;}
}
private void ClearMisc
{
set {inspectionCheckBox.Checked = false;
replaceMufflerCheckBox.Checked = false;
tireRotationCheckBox.Checked = false;}
}
private void ClearOther
{
set {partsCostInputTextBox.Text = null;
laborInputTextBox.Text = null;}
}
private void ClearFees
{
set {servicesLaborDispLabel.Text = null;
partsDispLabel.Text = null;
partsTaxDispLabel.Text = null;
totalFeesDispLabel.Text = null;}
}
When the clear button is clicked just call the methods you created above to clear the data.
public void clearButton_Click(Object sender, EventArgs e)
{
ClearOilLube();
ClearFlushes();
ClearMisc();
ClearOther();
ClearFees();
// May be required to be called to ensure the UI is up to date.
Update();
}
Edit
The syntax on your question is a little strange. They are written kind of like properties when they should be methods. No 'set' or 'get' is needed. Just make them regular helper methods.
This is just typed in no testing on a compiler or IDE but what I can see is fixed.
private void ClearOilLube()
{
oilChangeCheckBox.Checked = false;
lubeJobCheckBox.Checked = false;
}
private void ClearFlushes()
{
radiatorFlushCheckBox.Checked = false;
transFlushCheckBox.Checked = false;
}
private void ClearMisc()
{
inspectionCheckBox.Checked = false;
replaceMufflerCheckBox.Checked = false;
tireRotationCheckBox.Checked = false;
}
private void ClearOther()
{
partsCostInputTextBox.Text = "";
laborInputTextBox.Text = "";
}
private void ClearFees()
{
servicesLaborDispLabel.Text = "";
partsDispLabel.Text = "";
partsTaxDispLabel.Text = "";
totalFeesDispLabel.Text = "";
}
Your methods (or is that properties?) are written quite badly. I will just assume that you want to write methods not properties.
Method headers take the following form: (simplified)
[access modifier] {return value type} {method name} ([parameter list])
So your methods should look like this:
private void ClearOilLube ()
{
oilChangeCheckBox.Checked = false;
lubeJobCheckBox.Checked = false;
}
private void ClearFlushes ()
{
radiatorFlushCheckBox.Checked = false;
transFlushCheckBox.Checked = false;
}
private void ClearMisc ()
{
inspectionCheckBox.Checked = false;
replaceMufflerCheckBox.Checked = false;
tireRotationCheckBox.Checked = false;
}
private void ClearOther ()
{
partsCostInputTextBox.Text = null;
laborInputTextBox.Text = null;
}
private void ClearFees ()
{
servicesLaborDispLabel.Text = null;
partsDispLabel.Text = null;
partsTaxDispLabel.Text = null;
totalFeesDispLabel.Text = null;
}
And then, you can call these methods one by one in the onClick method.
However, a better approach is to loop through (or iterate) through the Controls of the form.
private void ClearButtonClick (object sender, EventArgs e) {
foreach (Control control in this.Controls) {
if (control is CheckBox) {
((CheckBox)control).Checked = false;
}
}
}
If you still don't understand, tell me in the comments!
To give you an alternative, though it is more complex:
private void GetAllControlsOfType<TControl>(TControl collection, List<TControl> container) where TControl : Control
{
foreach(Control control in collection)
{
if(control is TControl)
container.Add(control);
if(control.HasControls())
GetAllControlsOfType<TControl>(control.Controls, container);
}
}
Then to utilize, you would simply do:
var checkboxes = new List<Checkbox>();
GetAllControlsOfType<Checkbox>(Page.Controls, checkboxes);
After that is executed it will contain a List of all the checkbox controls. Then you could simply map them to the state of the object or you can simply do the following:
foreach(var checkbox in checkboxes)
checkbox.Checked = false;
That would uncheck all, keep in mind this hasn't been tested so it may need some fine tuning.

Create an instance of a class sent by parameter / Delegates

I am trying to optimize my code a bit by either using delegates or using a class instance as parameter. I am pretty new to C# and I am not yet sure which one is the better approach assuming im on the right track in the first place. But my problem relates to sending a class instance as parameter. Let me explain. I am trying to follow this logic but im failiing....
I have created a VSTO Ribbon with a few buttons. It looks somewhat like this:
Now, I am now trying to add some functionality to the buttons, so a click on each button opens a new TaskPane.
I wrote this code for the Calendar Ribbon button which sits in the GSMRibbon.cs
note: I think that for the more experienced programmers this code will be quite easy to understand but in case you guys dont understand something please let me know in the comments i will explain ).
namespace GSM
{
public partial class GSMRibbon
{
private void GSMRibbon_Load(object sender, RibbonUIEventArgs
{
}
private CustomTaskPane taskPane;
private CustomTaskPane TaskPane
{
get
{
return this.taskPane;
}
}
private void vendors_calendar_Click(object sender, RibbonControlEventArgs e)
{
string newTitle = "PO Calendar";
if (TaskPane != null)
{
if (TaskPane.Title != newTitle)
{
Globals.ThisAddIn.CustomTaskPanes.Remove(TaskPane);
CreateTaskPane(newTitle);
}
else
{
taskPane.Visible = true;
}
}
else
{
CreateTaskPane(newTitle);
}
}
private void CreateTaskPane(string title)
{
var taskPaneView = new CalendarView();
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
}
}
Ok. What I wanted to do was to modify the CreateTaskPane function adding a class parameter (does this make sense?) so I can re-use this function multiple times for different buttons on the ribbon. I have created a separate View for each of the buttons, but I am not sure how to pass the View.
So, im after something like this: (CalendarView is the name of the View)
CreateTaskPane(new CalendarView(), newTitle);
and the function something like:
private void CreateTaskPane(object typeOfView, string title)
{
var taskPaneView = new (typeOfView)Object;
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
I really hope you understand what I am trying to but being unable to do myself. I appreciate any attempt to help. Thanks
You can use generics to do this:
private void CreateTaskPane<T>(string title) where T : UserControl, new()
{
T taskPaneView = new T();
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
You would then call it via:
CreateTaskPane<CalendarView>(newTitle);
Alternatively, you could write this as:
private void CreateTaskPane<T>(T taskPaneView, string title) where T : UserControl
{
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
Then call via:
CreateTaskPane(new CalendarView(), newTitle);
What you seem to be looking for is Generics
The function you would end up with is something like:
private void CreateTaskPane<T>(string title) where T : UserControl, new()
{
var taskPaneView = new T();
taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, title);
taskPane.Visible = true;
}
// Later on..
CreateTaskPane<CalenderTaskPane>("Calender");

how to access i statement in usercontrol

How can I go about accessing the result of an if statement in a user control?
UserControl code:
public bool SendBack(bool huh)
{
if(huh)
huh = true;
else huh = false;
return huh;
}
And in a separate project i am trying to access it like this:
private void button1_Click(object sender, EventArgs e)
{
MyControl.TextControl t = (MyControl.TextCOntrol)sender;
if(t.SendBack(true))
{
// Do something.
}
}
In this case I thing the sender will be the button1, so it will not be castable to your usercontrol...
You will need a reference form the container (form/panel/...) that contains your usercontrol.
Also, I know this might be for simplicity but you can change
public bool SendBack(bool huh)
{
if(huh)
huh = true;
else huh = false;
return huh;
}
to
public bool SendBack(bool huh)
{
return huh;
}
You might also want to take a look at Control.ControlCollection.Find Method
Searches for controls by their Name
property and builds an array of all
the controls that match.

Categories

Resources