I have a User Control with with some search functionality, then I also have a control that contains a grid. In the control that has the grid I have all the functionality to retrieve some items I need to populate a report from the search control.
My question is, and I have read about this but not completely sure how to make it work, I read i need to register a event handler in control A that calls the function in control B. How does that work?
Say in control B there is a function that I need to call GetAllItemsByRegistrantID(int id). How can I make a call to this function from control A?
I would really appreciate any help or guidance on this.
Regards
Here is an example if you had to do it using events.
On your first user control (the search control) define a custom event.
public partial class UserControl1 : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
public event EventHandler<MyCustomeEventArgs> MyCustomClickEvent;
protected virtual void OnMyCustomClickEvent(MyCustomeEventArgs e)
{
if (MyCustomClickEvent != null)
MyCustomClickEvent(this, e);
}
public void button1_Click(object sender, EventArgs e)
{
OnMyCustomClickEvent(new MyCustomeEventArgs(5));
}
}
public class MyCustomeEventArgs : EventArgs
{
public MyCustomeEventArgs(int searchID)
{
SearchID = searchID;
}
public int SearchID { get; set; }
}
On your user control with the grid, define a handler.
public void HandleCustomEvent(object sender, MyCustomeEventArgs e)
{
GetAllItemsByRegistrantID(e.SearchID);
}
public void GetAllItemsByRegistrantID(int id)
{
Label1.Text = id.ToString();
}
On the page where they both live, wire them together.
protected void Page_Load(object sender, EventArgs e)
{
UserControl1Instance.MyCustomClickEvent += UserControl2Instance.HandleCustomEvent;
}
I would definitely try and avoid this sort of situation. You should be putting this sort of code in a class and sharing this between the two controls.
Related
I am working with windowsFrom in c#. I am trying to call mainfrom method in one of the from in user control.
I have mainfrom like this
namespace Project
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public void TempCommand()
{
StartTemp();
}
}
}
I have the button click in the user control. When i click that button then it will open another form. I have the code like this in the user control.
private TempCalib _tempCalib = new TempCalib();
private void calibBtn_Click(object sender, EventArgs e)
{
_tempCalib.Show();
}
it will open another from and i have one button in that from. I need to call mainfrom method when i click "Ok" button in this from.
namespace Project
{
public partial class TempCalib : Form
{
public TempCalib()
{
InitializeComponent();
}
private void OkButton_Click(object sender, EventArgs e)
{
// I need to call the mainfrom "TempCommand" method here.
this.Hide();
}
}
}
Can anyone help me how to do this.
Thanks.
Quick answer
Just add a reference to the primary form in your secondary form:
public partial class TempCalib : Form
{
private MainForm _main
public TempCalib(MainForm main) : this()
{
_main = main;
}
/// Other stuffs
}
Then assign value when you construct your secondary form:
private TempCalib _tempCalib;
private void calibBtn_Click(object sender, EventArgs e)
{
if (_tempCalib == null)
_tempCalib = new TempCalib(this);
_tempCalib.Show();
}
If calibBtn_Click isn't inside MainForm (but it's inside a UserControl on it) then you can replace _tempCalib initialization with:
_tempCalib = new TempCalib((MainWindow)FindForm());
You'll be then able to call the primary form:
private void OkButton_Click(object sender, EventArgs e)
{
_main.TempCommand();
this.Hide();
}
Notes: this is just one option, you may create a property to hold MainForm reference (so secondary form can be reused and it'll be more designer friendly) moreover TempCalib is not an UserControl but a Form (pretty raw but for an UserControl you may just check its parent Form and cast it to proper type).
Improvements
Such kind of references are often an alert. Usually UI components shouldn't not be so coupled and a public Form's method to perform something very often is the signal that you have too much logic in your Form. How to improve this?
1. DECOUPLE CONTROLS. Well a first step may be to decouple them a little bit, just add an event in TempCalib and make MainForm its receiver:
public partial class TempCalib : Form
{
public event EventHandler SomethingMustBeDone;
private void OkButton_Click(object sender, EventArgs e)
{
OnSomethingMustBeDone(EventArgs.Empty); / TO DO
this.Hide();
}
}
Then in MainForm:
private TempCalib _tempCalib;
private void calibBtn_Click(object sender, EventArgs e)
{
if (_tempCalib == null)
{
_tempCalib = new TempCalib();
_tempCalib.SomethingMustBeDone += _tempCalib_SomethingMustBeDone;
// In _tempCalib_SomethingMustBeDone you'll invoke proper member
// and possibly hide _tempCalib (remove it from OkButton_Click)
}
_tempCalib.Show();
}
2. DECOUPLE LOGIC FROM CONTROLS. UI changes pretty often, logic not (and when it changes probably isn't in parallel with UI). This is just the first step (now TempCalib isn't aware of who will use it). Next step (to be performed when too much things happen inside your form) is to remove this kind of logic from the form itself. Little example (very raw), keep TempCalib as before (with the event) and change MainForm to be passive:
public partial class MainForm : Form
{
public event EventHandler Calibrate;
protected virtual void OnCalibrate(EventArgs e)
{
// TODO
}
}
Now let's create a class to control the flow and logic:
public class MyTaskController
{
private MainForm _main;
private TempCalib _tempCalib;
public void Start()
{
_main = new MainForm();
_main.Calibrate += OnCalibrationRequested;
_main.Show(); // Or whatever else
}
private void OnCalibrationRequested(object sender, EventArgs e)
{
if (_tempCalib == null)
{
_tempCalib = new TempCalib();
_tempCalib.SomethingMustBeDone += OnSomethingMustBeDone();
}
_tempCalib.Show();
}
private OnSomethingMustBeDone(object sender, EventArgs e)
{
// Perform the task here then hide calibration window
_tempCalib.Hide();
}
}
Yes, you'll need to write much more code but this will decouple logic (what to do as response to an action, for example) from UI itself. When program grows up this will help you to change UI as needed keeping logic unaware of that (and in one well defined place). I don't even mention that this will allow you to use different resources (people) to write logic and UI (or to reuse logic for different UI, WinForms and WPF, for example). Anyway IMO the most obvious and well repaid benefit is...readability: you'll always know where logic is and where UI management is, no search, no confusion, no mistakes.
3. DECOUPLE LOGIC FROM IMPLEMENTATION. Again you have more steps to perform (when needed). Your controller is still aware of concrete types (MainForm and TempCalib). In case you need to select a different form at run-time (for example to have a complex interface and a simplified one or to use dependency injection) then you have to decouple controller using interfaces. Just an example:
public interface IUiWindow
{
void Show();
void Hide();
}
public interface IMainWindow : IUiWindow
{
event EventHandler Calibrate;
}
public interface ICalibrationWindow : IUiWindow
{
event EventHandler SomethingMustBeDone;
}
You could use a custom event that is declared in your UserControl. Then your form needs to handle this event and call the method you want to call. If you let the UserControl access your form, you are hard-linking both with each other which decreases reusability of your UserControl.
For example, in TempCalib:
public delegate void OkClickedHandler(object sender, EventArgs e);
public event OkClickedHandler OkClicked;
private void OkButton_Click(object sender, EventArgs e)
{
// Make sure someone is listening to event
if (OkClicked == null) return;
OkClicked(sender, e);
this.Hide();
}
in your mainform:
private void Mainform_Load(object sender, EventArgs e)
{
_tempCalib.OkClicked += CalibOkClicked;
}
private void CalibOkClicked(Object sender, EventArgs e)
{
StartTemp();
}
You create an event in your usercontrol and subscribe to this in the mainform.
That is the usual way.
Form1 Code:
UserControl1 myusercontrol = new UserControl1();
public void TabClose(Object sender,EventArgs e)
{
int i = 0;
i = tabControl1.SelectedIndex;
tabControl1.TabPages.RemoveAt(i);
}
private void Form1_Load(object sender, EventArgs e)
{
myusercontrol.Dock = DockStyle.Fill;
TabPage myTabPage = new TabPage();
myTabPage.Text = "Student";
myTabPage.Controls.Add(myusercontrol);
tabControl1.TabPages.Add(myTabPage);
myusercontrol.OkClick += TabClose;
}
UserControl1 Code:
public delegate void OkClickedHandler(Object sender, EventArgs e);
public partial class UserControl1 : UserControl
{
public event OkClickedHandler OkClick;
public UserControl1()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
if (OkClick == null) return;
OkClick(sender, e);
}
}
Try this:
From user control try this:
MainForm form = this.TopLevelControl as MainForm;
form.TempCommand();
I have a form with 6 buttons. These buttons serve to increase/decrease tha value of the respective textbox. Now I'm trying to "animate" the buttons. I want to get another effect on the button when the mouse is over him.
To do that, I have two diferent images in Resources and I am doing this code:
private void btnHoursDown_MouseHover(object sender, EventArgs e) {
btnHoursDown.Image = Game_Helper.Properties.Resources.DownHover;
}
private void btnHoursDown_MouseLeave(object sender, EventArgs e) {
btnHoursDown.Image = Game_Helper.Properties.Resources.Down;
}
This works fine. My question is: it wouldn't be wise to create a class (ButtonBehaviour.cs) and put this code in that class?
So I would have something like this:
ButtonBehaviour buttonBehaviour = new ButtonBehaviour();
private void btnHoursDown_MouseHover(object sender, EventArgs e) {
buttonBehaviour.buttonDownHover();
}
private void btnHoursDown_MouseLeave(object sender, EventArgs e) {
buttonBehaviour.buttonDownLeave();
}
And the class should be:
public class ButtonBehaviour {
public void buttonDownHover() {
// code
}
public void buttonDownLeave() {
// code
}
}
How can I create this Class Behaviour and make the buttons adapt this Behaviour?
if one effect should be applied for all buttons, try to add the same event handlers to them
private void btn_MouseHover(object sender, EventArgs e)
{
(sender as Button).Image = Game_Helper.Properties.Resources.DownHover;
}
private void btn_MouseLeave(object sender, EventArgs e)
{
(sender as Button).Image = Game_Helper.Properties.Resources.Down;
}
button which raised event is available via sender variable
this way you avoid code duplication for every button. creating a ButtonBehaviour or CustomButton is probably an over-engineering unless you need them in many forms
I have having some trouble with communication from a usercontrol to the main page. The order in which events are raised means that the action on the user control occurs too late in the post back to have an effect on the main page.
For example, I have a button on a user control which, when pressed, raises a custom event that is being listened for on the main page. When the button is pressed the postback order is:
page_load - on the main page
page_load - on the usercontrol (the user control is loaded programitically by the main page page_load)
The button call back on the user control
The event call back method on the main page
By this point, it seems it is too late for anything the event call back method does to have any effect on the rendered page, for example I am trying to use it to change the usercontrol that is being displayed.
What other techniques can be used for this kind of communication?
Relevant code
Main page:
public string LastLoadedControl
{
get
{
return Session["LastLoaded"] as string;
}
set
{
Session["LastLoaded"] = value;
}
}
private void LoadUserControl()
{
string controlPath = LastLoadedControl;
ContentPlaceholder.Controls.Clear();
if (string.IsNullOrEmpty(controlPath))
controlPath = Utils.Paths.USERCTRL_BASE + "Main.ascx";
Control uc = Page.LoadControl(controlPath);
ContentPlaceholder.Controls.Add(uc);
}
protected void Page_Load(object sender, EventArgs e)
{
LoadUserControl();
if (!IsPostBack)
Utils.Events.redirectPage += Events_redirectPage;
}
private void Events_redirectPage(string path)
{
if (path.Equals("Main"))
{
//Session.Clear();
//Session.Abandon();
}
else LastLoadedControl = Paths.USERCTRL_BASE + path + ".ascx"
LoadUserControl();
}
User control
protected void profileBtn_Click(object sender, EventArgs e)
{
Utils.Events.triggerRedirectPage("Login");
}
Event
public class Events
{
public delegate void redirectEvent(string path);
public static event redirectEvent redirectPage;
public static void triggerRedirectPage(String path)
{
if (Utils.Events.redirectPage != null)
Utils.Events.redirectPage(path);
}
}
There are two approaches that you can follow.
Approach 1:
public interface IEventProvider
{
void TriggerEvent();
}
public class YourPage: Page, IEventProvider
{
// Other page methods
public void TriggerEvent()
{
// Your Implementation
}
}
public class YourUserControl : WebUserControl
{
protected void profileBtn_Click(object sender, EventArgs e)
{
IEventProvider eventProvider = this.Page as IEventProvider;
if(eventProvider != null)
eventProvider.TriggerEvent();
}
}
Approach 2:
public interface IEventProvider
{
// This does not have to be a boolean. You can use a string / enum / anything that suits your implementation
bool Trigger {get; set;}
}
public class YourPage: Page, IEventProvider
{
// Other page methods
protected override void OnLoadComplete(EventArgs e)
{
// This will be raised when all the events have fired for all the controls in the page.
if(this.Trigger)
TriggerEvent();
}
protected void TriggerEvent()
{
// Your code here
}
public bool Trigger
{
get;
set;
}
}
public class YourUserControl : WebUserControl
{
protected void profileBtn_Click(object sender, EventArgs e)
{
IEventProvider eventProvider = this.Page as IEventProvider;
if(eventProvider != null)
eventProvider.Trigger = true;
}
}
i have a really simple counter application, I made in C#.
Now what i want to know is it possible to clone the form design and code, so there are 2 counter's instead of one. with a button.
they both have to be working.
i'm an beginner.. so that's why i ask if this is possible.
So from this (this is what i currently have, without clone button):
http://i.stack.imgur.com/ASMY4.jpg
to this:
http://i.stack.imgur.com/acluZ.jpg
this is my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void counteradd()
{
int current1 = Convert.ToInt32(totaltb.Text);
current1++;
totaltb.Text = Convert.ToString(current1);
}
public void counterreduce()
{
int current2 = Convert.ToInt32(totaltb.Text);
current2--;
totaltb.Text = Convert.ToString(current2);
}
public void counterreset()
{
totaltb.Text = ("0");
}
private void reducebttn_Click(object sender, EventArgs e)
{
counterreduce();
}
private void resetbttn_Click(object sender, EventArgs e)
{
counterreset();
}
private void addbttn_Click(object sender, EventArgs e)
{
counteradd();
}
}
Simply duplicating the controls and laying them out on the form will result in messy code. The "clone' that you are referring to would be to build the functional piece as a user-control, and then add that as a control to your form.
I have searched extensively on this, but cannot find the solution to my problem. I am trying to call a function in the code behind of a page from a user control on that page.
I have a web application that uses a master page. I am adding a user control that I wrote to one of the content pages. I added the user control to the aspx page by dragging and dropping it from the toolbox. I am able to see the user control from the code behind, but I cannot access the public functions. To fix that problem, I created an object of the user control in the code behind and used the LoadControl function. All of that seems to work fine.
The problem I am having is when I am trying to hook the into the EventHandler from the aspx page to the user control. Everything compiles and runs just fine, but I am not seeing anything happen on the page. I think the issue is that the EventHandler is always null.
User Control Code
public partial class ucBuyerList : System.Web.UI.UserControl
{
public delegate void BuyerSelectedEventHandler(object sender, EventArgs e);
public event BuyerSelectedEventHandler BuyerSelected;
private string name = "";
public string Name
{
get { return name; }
set { name = value; }
}
private string auid = "";
public string AUID
{
get { return auid; }
set { auid = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
private void OnBuyerSelected(EventArgs e)
{
if (BuyerSelected != null)
{
BuyerSelected(this, new EventArgs());
}
}
protected void lbBuyerList_SelectedIndexChanged(object sender, EventArgs e)
{
SetNameAndAUID();
OnBuyerSelected(e);
}
private void SetNameAndAUID()
{
name = lbBuyerList.SelectedItem.Text;
auid = lbBuyerList.SelectedItem.Value;
}
}
Parent Page Code
public partial class frmBuyerInformation : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Master.changePageTitle("Buyer Information");
buyerList.BuyerSelected += new ucBuyerList.BuyerSelectedEventHandler(buyerListControl_BuyerSelected);
}
void buyerListControl_BuyerSelected(object sender, EventArgs e)
{
DisplayBuyerInformation();
}
public void DisplayBuyerInformation()
{
tbName.Text = buyerList.Name;
tbAUID.Text = buyerList.AUID;
}
}
Can anyone see what I am doing wrong?
EDIT: This issue has been resolved. The code snippits above are now functional. If anyone runs into the issue I had, you can model the code above. Make sure that AutoEventWireup="true" in both the aspx and ascx pages. Thank you June Paik for your solution. Thank you Diego De Vita for your input as well.
I've been struggling with events for quite a while as well. Nowadays I always create them this way 'cause it's the only way I know it works. Haven't tested it with your code but here it goes anyway:
public partial class ucBuyerList : System.Web.UI.UserControl
{
public delegate void BuyerSelectedEventHandler(object sender, EventArgs e);
public event BuyerSelectedEventHandler BuyerSelected;
public string Name;
public string AUID;
protected void Page_Load(object sender, EventArgs e)
{
//Select the first buyer in the list when the user control loads
if (!IsPostBack)
{
lbBuyerList.SelectedIndex = 0;
}
}
private void OnBuyerSelected(EventArgs e)
{
BuyerSelectedEventHandler handler = BuyerSelected;
if (handler != null)
{
handler(this, new EventArgs());
}
}
protected void lbBuyerList_SelectedIndexChanged(object sender, EventArgs e)
{
Name = lbBuyerList.SelectedItem.Text;
AUID = lbBuyerList.SelectedItem.Value;
OnBuyerSelected(e);
}
}
In the parent page you can just call your function the same way you're doing it already.
It could be that Page_Load is too late in the page lifecycle to be using LoadControl and subscribing to the event. What happens if you move that code to the Page_Init method?