I am creating an application using WinForms. I have panel in which I show a user control. Inside this user control I have a button. When I click the button, I want to clear the panel and show a different user control. I am trying to do that using the following code:
private void btnCreateOffer_Click(object sender, EventArgs e)
{
var myControl = new WindowsFormsDemo.View.CreateOffer();
MockUpForm.panMain.Controls.Clear();
MockUpForm.panMain.Controls.Add(myControl);
}
This works from the buttons placed directly in the parrent form, but when I use in inside the user control, it says:
'MockUpForm.panMain' is inaccessible due to its protection level
I suppose it has something to do with private/public classes. But I would rather have the "correct" solution, as opposed to just changing everything to public.
Any suggestions on how this is usually done?
Solution 1 (ugly):
Make panMain public in the designer:
Solution 2 (somewhat better):
Provide public methods to achieve such tasks safely:
// MockUpForm code:
public void ClearPanelControls()
{
panMain.Controls.Clear();
}
public void AddControlToPanel(Control c)
{
panMain.Controls.Add(c);
}
And then call these methods instead of publishing the full panel, which makes possible for example to dispose the whole panel and such things...
To access parent form's control from UserControl You can use delegate and event
something like this....
Windows Form (Parent Form) Code....
public Form1()
{
InitializeComponent();
userControl1.CreateOffer += UserControl1_CreateOffer;
}
private void UserControl1_CreateOffer()
{
var myControl = new WindowsFormsDemo.View.CreateOffer();
this.panMain.Controls.Clear();
this.panMain.Controls.Add(myControl);
}
User Control Code...
internal delegate void CreateOfferDelegate();
internal event CreateOfferDelegate CreateOffer;
public UserControl1()
{
InitializeComponent();
}
private void btnCreateOffer_Click(object sender, EventArgs e)
{
CreateOffer();
}
Related
I have created dashboard consisting of two windows - home screen (the Form) and data screen (the User Control).
My home screen is the dashboard and upon clicking on "data" button present on the dashboard, the user is sent to the data screen (where he enters his details) . The way I'm doing this transition between the 2 windows is by making the data screen visible and bringing it to front when data button is pressed.
Q1. Is this the right way to go about switching windows? Is there a better method to do this?
Coming to the main question, the dashboard indicates whether the user has entered data or not and if yes, the user data is displayed. The code to fetch the user data is written in the user control. Now I wish to send this data to the home screen (form) so that the data can be displayed on the dashboard.
Q2. How to link the user control and form - variables wise? How to establish a Bidirectional Communication between form and user control i.e. User control should be able to read form's variables and form should be able to read user control's variables
Here are two approaches...
Tightly Coupled
In UserControlA, I've declared a property of type Form1 (the parent form):
public partial class UserControlA : UserControl
{
public UserControlA()
{
InitializeComponent();
}
private Form1 f1;
public Form1 F1
{
get { return f1; }
set { f1 = value; }
}
private void foo()
{
if (f1 != null)
{
// ... do something with "f1" ...
}
}
}
Then, in Form1, I set that property in the Load() event:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
userControlA1.F1 = this;
}
}
This is tightly coupled because now UserControlA has a hard-coded reference to Form1. This makes it work well with Form1, but is now less flexible as it won't work well with any other forms. If you wanted this approach to work with Form2, for instance, then you'd have to change the hard-coded type in the UserControl.
Use this approach if there is a very strong relationship between the form and the usercontrol and it's likely that the usercontrol will not be used with any other forms, or in any other scenarios.
Loosely Coupled
In UserControlB, I've declared an event (BroadcastName) that will communicate a string to the outside world:
public partial class UserControlB : UserControl
{
public delegate void dlgBroadcastName(UserControlB source, string name);
public event dlgBroadcastName BroadcastName;
public UserControlB()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (BroadcastName != null)
{
BroadcastName(this, textBox1.Text);
}
}
}
Now, in the Load() event of Form1 (or using the lightning bolt icon in the properties pane), we wire up that BroadcastName event:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
userControlB1.BroadcastName += UserControlB1_BroadcastName;
}
private void UserControlB1_BroadcastName(UserControlB source, string name)
{
// ... do something to Form1 with the received information in here ...
}
}
This is loosely coupled because UserControlB has no idea who it is communicating with. It simply raises its event and whoever has subscribed gets notified. Note that this UserControl can be used with any form, without change. This is generally a more flexible approach and makes sense when your usercontrol is more generic in nature and will be used in a wider variety of situations.
This may sound stupid, But I am having hard time to figure this out; any help would be appreciated:
I have two user controls called “Safety_Check” and “OEE_Track”. In my MainForm I have a panel called “pnl_main_controller” this is where I am displaying both my user controls. I have two buttons on my main form and I am dynamically switching between both without any issue.
Safety_Check User control;
public partial class Safety_Check : UserControl
{
private static Safety_Check _instance;
public static Safety_Check instance
{
get
{
if (_instance == null)
_instance = new Safety_Check();
return _instance;
}
}
public Safety_Check()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
///////------------------------
}
}
OEE_Track User control
public partial class OEE_Track : UserControl
{
private static OEE_Track _instance;
public static OEE_Track instance
{
get
{
if (_instance == null)
_instance = new OEE_Track();
return _instance;
}
}
public OEE_Track()
{
InitializeComponent();
}
}
MainForm:
public partial class MainForm : Form
{
private void btn_reg_Click(object sender, EventArgs e)
{
if (!pnl_main_controller.Contains(Safety_Check.instance))
{
pnl_main_controller.Controls.Add(Safety_Check.instance);
Safety_Check.instance.Dock = DockStyle.Fill;
Safety_Check.instance.BringToFront();
}
else
{
Safety_Check.instance.BringToFront();
} }
private void btn_OEE_Click(object sender, EventArgs e)
{
if (!pnl_main_controller.Contains(OEE_Track.instance))
{
pnl_main_controller.Controls.Add(OEE_Track.instance);
OEE_Track.instance.Dock = DockStyle.Fill;
OEE_Track.instance.BringToFront();
}
else
{
OEE_Track.instance.BringToFront();
}
}
What I am trying to do is I have a button called “Button1” on my “Safety_Check” Usercontrol, whenever I press this , I want “Safety_Check” to be disappear on “pnl_main_controller” and bring “OEE_Track” to the panel
There are several solutions for interaction between controls. Controls are classes and like any other class they can interact with each other using their public properties and methods or using some mediator.
In this case, your controls don't need to know each other and don't need to interact to each other directly:
They can ask another object which knows both controls, to do the job for them.
Or they can raise their request notification and the one who subscribed to that notification, will serve it.
To ask another object to do the job for them you have multiple solutions. As an example you can implement a specific interface in the parent form and in the child controls, cast the parent to that specific interface and call a specific method which do the job for you.
For raising the request notification, an easy solution is relying on events. You can create an event in the child control and raise it when you need the parent do something for you. Then in the parent subscribe to that event and do the job.
Example - Using event
I assume you have UserControl1 having Button1 inside and you have handled Click event of Button1. Then you can create Button1Clicked event and raise it when Button1 clicked:
public event EventHandler Button1Clicked;
private void Button1_Click(object sender, EventArgs e)
{
Button1Clicked?.Invoke(this, e);
}
Then in the parent form, subscribe for the event and do whatever you want:
private void userControl11_Button1Clicked(object sender, EventArgs e)
{
//Hide userControl11 and show userControl21
}
Example - Using interface
I assume, you have an interface having a few standard methods:
public interface IDoSomething
{
void DoSomething();
void DoSomethingElse();
}
And you have implemented the interface in your parent form:
public class Form1: Form, IDoSomething
{
// ...
public void DoSomething()
{
//Hide userControl11 and show userControl21
}
public void DoSomethingElse()
{
// ...
}
}
Then in you user control:
private void Button1_Click(object sender, EventArgs e)
{
var f = FindForm() as IDoSomething;
if(f!=null)
f.DoSomething();
}
I want to expand on Reza Aghaei's answer a bit. I think it could get even better than it is now.
First way to do this
If I were you I would have some interface ICheckedView which has at least 1 method to implement like so:
ICheckedView
{
void Continue();
}
Now, we're able to apply this interface to any class in our solution, most likely to views, though. Next, I would make your main form implement this interface and implement the required method. The in this case we want our main form to remove the control from the panel and add a new control. Frankly, our Safety check control doesn't need to (and maybe shouldn't) know about other controls or what happens next. It's just used for flow of control.
Finally, you need to add either a public property, or maybe even a parameter to the constructor for Safety_Check which includes an ICheckedView in it. When your safety check control gets clicked it can tell whoever has been passed into it (we'll say the client) that it must continue.
Second way to do this
It can be done with an action delegate.
If you add an Action delegate to your safety check, you could just pop in any method whose signature matches that delegate (void methodName()). The constructor for your Safety_Check control should include an Action and that Action would get assigned to a private field of the class. Then when it's time to invoke, that action can be invoked directly.
Notes on this method
Because we're probably invoking from the UI thread in the first place, we're probably alright, but you need to think about thread safety here. The invoke required pattern can help you around this.
I have a TabControl and a UserControl interacting in the following way:
Each time a new tab is opened, the UserControl loads onto the new tab.
In the UserControl there's a Panel, a TexBox and a Button. Each time text is entered into the TexBox and the Button is pressed, it's supposed to update the title of the current tab
How do I access the tab title from within the UserControl?
Better if the user control does not know where it is embedded into.
Consider providing a TitleChanged event in the user control instead. Then it can be the responsibility of the consumer to update itself accordingly.
public class MyUserControl : UserControl
{
// [...]
public string Title { get; private set; }
public event EventHandler TitleChanged;
// [...]
private void MyTextBox_TextChanged(object sender, EventArgs e)
{
Title = MyTextBox.Text;
TitleChanged?.Invoke(this, EventArgs.Empty);
}
}
And the necessary code of the consumer class can be sg like this:
// after subscribing the myUserControl.TitleChanged event:
private void MyUserControl_TitleChanged(object sender, EventArgs e)
{
myTab.Text = myUserControl.Title;
}
Even better if you use data binding in the user form:
myTab.DataBindings.Add(nameof(TabPage.Text), myUserControl, nameof(MyUserControl.Title));
Suppose If I have a button on a custom made user control that removes the control from the form (Lets call it formX) it is placed in.
private void btnClose_Click(object sender, EventArgs e)
{
this.ParentForm.Controls.Remove(this);
}
Now upon closing this UserControl I want a method in the formX to be called.
I tried doing something like this :
discount.ControlRemoved += new ControlEventHandler(discount_ControlRemoved);
void UserControl_ControlRemoved(object sender, ControlEventArgs e)
{
CallMethod();
}
However this does not work, when removing the userControl from formX the event is not even called in the debugger.
How do I do this?
The event you should be using is the ControlRemoved event on the parent container, likely the Form in this case. You could do this several ways, some may be better than others depending upon what all you want to do, but the following should at least do what you are requesting:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.ControlRemoved += new ControlEventHandler(Form1_ControlRemoved);
}
void Form1_ControlRemoved(object sender, ControlEventArgs e)
{
if (e.Control.Name == "NameOfUserControl") CallMethod();
}
private void CallMethod()
{
// Do stufff...
}
}
This assumes that you have named your User Control instance "NameOfUserControl". There are various ways you could check for the control being removed to be the correct one. You can also make this a bit more dynamic by doing this in the control itself while using delegates to call back to the parent form, etc... This is just a basic example.
I have a Winform dialog that contains several user controls - all of them are some sort of Datagridview. The main parent has details about a user, and the user controls each have additional details on that person. When my Dialog first loads all of the UserControls work but I am trying to figure out how to update the UserControl2 based on a position change in UserControl1.
So, I am trying to select a row in UserControl1 and have the data in UserControl2 update based on a value that I just selected.
I have tried using MouseDownEvents on the UserControl1 and BindingSourcePositionChanged but I can't figure out how to get the value selected back to my parent form and then use that value to refresh the other datagrids?
I looked at delegates and events but I guess the lack of sleep is making it incredibly hard to comprehend. I understand that I need to create my delegate and event on the UserControl1 and then somehow call it on my mainform but that's where I get stuck and have no clue where to start.
Is this the right direction? Or is there another way to get this done? Can anyone offer up any suggestions on how this works?
Yes this is the correct approach something like the following will provide an event handler that you can use to the retrieve a public property from the UserControl:
public class SomeClass : BaseControl
{
public event EventHandler PersonSelected;
public string Name{get;set;}
protected void FindUser()
{
var find = new Button {ID = (ToString() + "search"), Text = "Search"};
find.Click += delegate(object sender, EventArgs e)
{
if (PersonSelected!= null)
{
//forward this event to the page's event handler
PersonSelected(this, e);
}
};
}
}
public class SomeOtherClass : Page
{
public void Main()
{
var sp = (SomeClass)Control;
sp.PersonSelected += BtnClick;
}
public void BtnClick(object sender, EventArgs e)
{
//Get some value from the (SomeClass)Control here
}
}