Using other classes to modify panel within Windows Forms - c#

The program i am trying to make involves one main form, which should be able to switch between 5 different menus. Programming all the functionality within the Form.cs file would make this an extremely long class, so what i want to do is call the Panel from another class to add control elements and load all the data from a MySQL database, depending on the menu chosen.
More specifically I have my ParentInterface.cs, where I want to show a ChoreUI within a dynamic Panel which will be modified in a new class called ChoreUI.cs.
I have tried making ChoreUI inherit from the ParentInterface, as well as making it the target. Though my lack of knowledge of Windows Forms is in the way.
ParentInterface.cs
namespace ChoreApplication.UI{
public partial class ParentInterface : Form
{
private ChoreUI ChoreUI;
private ParentInterface PUI;
public ParentInterface()
{
ChoreUI = new ChoreUI(PUI);
InitializeComponent();
}
private void ChoreNavButton_Click(object sender, EventArgs e)
{
var ChoreUI = new ChoreUI(PUI);
ChoreUI.DillerDaller();
}
}
ChoreUI.cs
namespace ChoreApplication.UI
{
class ChoreUI
{
public ParentInterface PUI;
public ChoreUI(ParentInterface PUI)
{
this.PUI = PUI;
}
public void DillerDaller()
{
PUI.dynamicPanel.Controls.Add(new Label { Location = new Point(10, 10), Name="textName", Text = "hello"});
}
}
I want to be able to add new control elements to the Panel, from the ChoreUI class instead of in the ParentInterface class. But as of now I am not succeeding in doing so.

What you have at the moment is not that bad, your child component has a reference to the parent and that's ok.
This, however, is the issue
public void DillerDaller()
{
PUI.dynamicPanel.Controls.Add(new Label { Location = new Point(10, 10), Name="textName", Text = "hello"});
}
At the basic level, you violate the encapsulation principle, where the dynamicPanel is protected inside the form so that it's not accessible from outside. Inheriting the child component from the main form is not the right solution.
At somewhat higher level, you violate here the so called Law of Demeter where the inner implementation details of a component should not be that misused. Changing the dynamicPanel visibility to public will not help. Rather, the rule says you should wrap such implementation details with a stable interface
public partial class ParentInterface : Form
{
...
public void AddDynamicPanelControl( Control control ) {
this.dynamicPanel.Controls.Add( control );
}
public void RemoveDynamicPanelControl( Control control ) {
this.dynamicPanel.Controls.Remove( control );
}
}
and use the newly introduced interface
public void DillerDaller()
{
var label = new Label { Location = new Point(10, 10), Name="textName", Text = "hello"};
this.PUI.AddDynamicPanelControl( label );
// if you store the reference to the label somewhere,
// you'll be able to call `Remove....` to remove this control from
// the form
}

Related

Winforms MVP Pattern w/ Multiple Views

I recently created a winforms application that followed no specific design pattern. This application has 4 different "views", each implemented using a TableLayoutPanel. One view is a "main" view that allows the user to select input files and the other 3 views contain DataGridViews that allow the user to work with the data loaded from the input file.
The problem lies in the fact that I have a single form with 4 different panels within it that are hidden and made visible when needed. But this has caused my form class to become much larger than I would like considering I have different events and methods that operate on the data for each panel all within the same class. So I did some research and came across Model-View-Presenter, but I've only came across examples that show applications with single-views.
My question is, if I use MVP and each view has its own interface and presenter, and the concrete implementation of the view is done using a Form, what is the best way to switch between views (for example, when clicking "next").
Should the concrete implementation of my view even be a Form? Am I missing something here? I'd like to follow the MVP pattern but I am open to suggestions if there is a better alternative.
First of all, you want to create a UserControl for each of the three DataGridView forms. As you are using MVP, each one should have an interface that the control inherits. For example:
public interface IDataGridView1
{
// Properties, Methods, etc...
}
public interface IDataGridView2
{
// Properties, Methods, etc...
}
public interface IDataGridView3
{
// Properties, Methods, etc...
}
Here is an example of the DataGridView1 UserControl, which inherits from its interface, and also from Control:
public class DataGridView1 : Control, IDataGridView1
{
TableLayoutPanel layoutPanel;
public DataGridView1()
{
layoutPanel = new TableLayoutPanel();
// Set up the layoutPanel.
// Rest of constructor, define your controls.
// Add your controls to layoutPanel.
// Add layoutPanel to this control.
Controls.Add(layoutPanel);
}
// Methods etc...
}
The other two DataGridViews will be similar but with their own functionality.
You could then create an interface for the MainView, which includes properties for the three DataGridViews it should contain, and methods to show one DataGridView whilst hiding the rest:
public interface IMainView
{
IDataGridView1 DataView1 { get; set; }
IDataGridView2 DataView2 { get; set; }
IDataGridView3 DataView3 { get; set; }
void ShowOnlyDataView1();
void ShowOnlyDataView2();
void ShowOnlyDataView3();
// Other methods, properties, etc...
}
The MainView class would inherit from Form and its own interface. Here I have shown the instantiated DataGridViews being passed in via the form's constructor:
public class MainView : Form, IMainView
{
public IDataGridView1 DataView1 { get; set; }
public IDataGridView2 DataView2 { get; set; }
public IDataGridView3 DataView3 { get; set; }
TableLayoutPanel layoutPanel;
public MainView(IDataGridView1 dataView1, IDataGridView2 dataView2,
IDataGridView3 dataView3)
{
this.DataView1 = dataView1;
this.DataView2 = dataView2;
this.DataView3 = dataView3;
layoutPanel = new TableLayoutPanel();
// Define your layout panel here.
// Add your controls to layoutPanel.
// Add layoutPanel to the MainView.
Controls.Add(layoutPanel);
// Rest of constructor...
}
// Hides other views and show DataView1.
public void ShowOnlyDataView1()
{
DataView2.Hide();
DataView3.Hide();
DataView1.Show();
}
// Hides other views and show DataView2.
public void ShowOnlyDataView2()
{
// Etc...
}
// Hides other views and show DataView3.
public void ShowOnlyDataView3()
{
// Etc...
}
// Other Methods etc...
}
Here is an example of the your Main method. You will want to instantiate each DataGridView and pass these into your MainView:
public static void Main(string[] args)
{
IDataModel dataModel = new DataModel();
IDataGridView1 dataView1 = new DataGridView1();
IDataGridView2 dataView2 = new DataGridView2();
IDataGridView3 dataView3 = new DataGridView3();
IMainView mainView = new MainView(dataView1, dataView2, dataView3);
DataGridPresenter1 dataPresenter1 = new DataGridPresenter1(dataView1, dataModel);
DataGridPresenter2 dataPresenter2 = new DataGridPresenter2(dataView2, dataModel);
DataGridPresenter3 dataPresenter3 = new DataGridPresenter3(dataView3, dataModel);
MainPresenter mainPresenter = new MainPresenter(mainView, dataModel);
}
Something to that effect.
So your three DataGridViews are displayed within your MainView, and all four views are accessed by their own Presenters.

Multiple exactly the same forms

I have multiple forms who look exactly the same (invoice, priceoffer, order,...) Now I created the first form. But after thinking about this, all the other forms will look exactly the same.
Is there a technique, or some other way that prevents me of creating all the forms over and over again?
Just create a single form class that you'd call OrderForm or something like that
public class OrderForm : Form {
}
Then use this to create all your controls on it, and create an instance of this class every time you need to show it to a user. Best part of this is if one of your forms changes (let's say invoice) you could just inherit from your class and only change the part that is different:
public class InvoiceForm : OrderForm {
}
And then just create an instance of InvoiceForm when you need that.
EDIT answering OPs question in the comment.
Let's say you create a bunch of controls in the constructor of OrderForm :
public class OrderForm : Form {
public OrderForm {
var button = new Button() { ... };
var label = new Label () { ... };
this.Controls.Add(button);
this.Controls.Add(label);
}
}
Now if you'd create an instance of OrderForm, these controls would be added to the form, no matter how many instances you'd create of these:
//each of these instances have the same controls
var form1 = new OrderForm();
var form2 = new OrderForm();
var form3 = new OrderForm();
Now if you'd define a subclass of OrderForm namely InvoiceForm, you'd be able to use the same form controls as OrderForm, with some changes to it:
public class InvoiceForm : OrderForm {
public InvoiceForm() : base(){ // : base() executes the constructor of the superclass
//all controls in the `OrderForm` class are added because we called base().
var invoiceControl = new Label() { ... };
this.Controls.Add(invoiceControl);
//now in total your form will count 4 controls
}
}
Now you can just create an instance of InvoiceControl to get the changed form.
//first 2 forms have 3 controls, last 2 forms have 4 controls.
var form1 = new OrderForm();
var form2 = new OrderForm();
var form3 = new InvoiceForm();
var form4 = new InvoiceForm();
Create a UserControl, and use it in multiple forms.
A good place to start with Visual inheritance.
https://msdn.microsoft.com/en-us/library/bx1155fz(v=vs.110).aspx
But as GuidoG say above you could have some strange behavior in Visual Studio and in the designer.
You could handle the different save button behavior by making the click handler calling an other overridable Sub and then override this Sub in the inherited Form (instead of coding your saving process directly in the event handler as most people do:-)).
Exemple
public class BaseForm
{
private void SaveButton_Click(System.Object sender, System.EventArgs e)
{
OnSaveButtonClick();
}
protected virtual void OnSaveButtonClick()
{
//Saving process for base form
}
}
public class InheritedForm : BaseForm
{
protected override void OnSaveButtonClick()
{
//Saving process for inherited form
}
}
Keep in mind that further modification in the "base" form would also apply to the inherited one without a word. This could be an issue if you are not well organized. Sometimes it's better to do a good old copy/paste.

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.

Correct architecture to extend WinForm UserControl base classes?

I have large number of UserControls that are very similar. They share a lot of common behavior. I been using a base class that has the common stuff and then specializing the class as needed.
class BaseControl : UserControl
{
// common
}
class RedControl : BaseControl
{
// specialized
}
class BlueControl : BaseControl
{
// specialized
}
etc ...
This works fairly well until I needed to start inserting or changing the layout of the child controls contained in BaseControl. For example RedControl needs to add a Button to a particular panel of the base control. In other case I need to change the size or layout of other base child controls.
When I tried the following code, I did not see any button at run time...
public partial class RedControl : BaseControl
{
public RedControl()
{
InitializeComponent();
addButtonToBase(); // no button shows up
this.PerformLayout();
}
void addButtonToBase()
{
Button button = new Button();
button.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
button.Location = new System.Drawing.Point(3, 3);
button.Size = new System.Drawing.Size(23, 23);
button.Text = "My Button";
baseSplitContainer.Panel1.Controls.Add(button); // protected child control in base
}
// ...
}
I can make the button display as a child of baseSplitContainer if I make addButtonToBase() virtual and manually add it to the generated code in InitalizeComponent() of BaseControl.
The layout of BaseControl was still going on and you are permitted to call virtual functions in constructors in C#.Net....
So even though it works, its not a nice solution. For one thing, when I edit BaseControl in the VS designer, the call to addBaseControl() in IntializeComponent gets removed,
For another calling virtual functions in constructors feels dangerous.
I think I need to get the layout of the base control to happen again in the derived control...
I tried that but either did it wrong or it wont work...
btw yes I know WPF is good at this. Cant use it because of constraints on other systems.
It turns out the correct way to modify the layout of a base control is to override the layout call from Control.OnLayout()
so something like
public RedControl()
{
//....
protected override void OnLayout(LayoutEventArgs e)
{
addButtonToBase(); // modify base layout
base.OnLayout(e);
}
}
I think you just miss something to call the base init logic to create the controls and your changes are then overriden. Try to call it like
public RedControl()
: base()
{ ... }
OR
public RedControl()
{
base.InitializeComponent();
InitializeComponent();
addButtonToBase(); // no button shows up
this.PerformLayout();
}

In a Windows Forms (.Net 4) application, how can I define a base form style for all forms?

For example, I'd like all of my forms to have the same Icon and StartPosition. However I also need to be able to define things in each form how you normally would, dragging and dropping controls, etc.
Is this possible?
Create a form and set the Icon and StartPosition properties the way you want them. Compile. This will be your base form. Now use Project + Add New Item, Windows Forms node and pick the Inherited Form item template. The IDE will prompt you to select the base form.
Antoher way to go, is to make an extension method where you set all the parameters:
public static class FormExtentsions
{
public static void SetDefault(this Form form)
{
form.Icon = new Icon("path");
form.StartPosition = FormStartPosition.CenterScreen;
}
}
The use it like this:
public partial class Form1 : Form
{
public Form1()
{
// Put it here if you want to be able to override everything
this.SetDefault();
InitializeComponent();
// Put it here if you want the defualt to override "local" settings
this.SetDefault();
}
}
Simply create your own base Form class:
class FormBase : Form
{
public FormBase()
{
Icon = SomeIcon;
StartPosition = StartPosition.Whatever;
}
}
You could have a static Icon and Position, initialize it with a static constructor, and then make a constructor where you initialize the instance's Icon and Position properties with the static Icon and position:
Fx.
class Foo : Form {
static Bitmap sIcon { get; private set; }
static Point sPosition { get; private set; }
static Foo() {
sIcon = /* Load from external source */
sPosition = new Point( x, y ); //Insert x and y
}
public Foo()
: base() {
Icon = Foo.sIcon;
Position = Foo.sPosition;
}
}
Then use "Foo" as your base form when creating your forms.
I didn't check references for the "Icon" and "position" so I don't know if they exists, but you get the idea :)
Yes, but bear in mind that forms inheritance is somewhat flaky in terms of designer support. Just keep in mind that any controls that need to be accessible to child forms must have their modifier changed to Protected (Internal will work for forms in the same assembly, but will also expose the control to ANY class in the same assembly; Public should be avoided). This includes things like panels or other containers, which you'll likely want to use if your base form has to define some basic presentation elements, so you'll want to contain the child form to a particular area.
To this, just create your base form as you would any other form, then when you go to create new forms, choose "Inherited Form" instead of "Form", and select your base form.

Categories