I wrote a small control that creates a popup for my Win8 Phone application which does all the nasty things for me like rotation, proper placement etc.
The popup is opened in a Popup control but not on a new phone page.
To close the popup, my control hooks up to the "backKeyPressed" event of the underlying page.
This works like charm until the underlying page has its own implementation of BackKeyPressed event. In this case, the page event is triggered but not the popup control event.
If I would own the event, I could create my own stack to call the last added event first, but I do not own the event of the pages.
As far as I know, I am unable to unregister any previously attached event handler and reassign it once my control unsubscribes from the event.
I could have only one implementation for the BackKeyPressed event which then informs the popup control to close itself (if open), if nothing was open, do the Page specific implementation. But this would require code changes on all pages where I might want to use the popup. Even worse, if I have 5 possible popups, I would have to check all of them :-(
So I am looking for an option to handle this centrally.
What other options do I have to overcome this situation?
Normally you cannot change the order of fired events - they are executed in registered order, but it's not required by specifications - source.
But as Jon Skeet says here:
Summary: For all sane events, you can rely on the ordering. In theory, events can do what they like, but I've never seen an event which doesn't maintain the appropriate ordering.
it is fired in registered order and should be.
BUT for your purpose (I think) you can set an event to invoke your method where you would control the order. I think simple example can show this behaviour:
public partial class MainPage : PhoneApplicationPage
{
private List<EventHandler<CancelEventArgs>> listOfHandlers = new List<EventHandler<CancelEventArgs>>();
private void InvokingMethod(object sender, CancelEventArgs e)
{
for (int i = 0; i < listOfHandlers.Count; i++)
listOfHandlers[i](sender, e);
}
public event EventHandler<CancelEventArgs> myBackKeyEvent
{
add { listOfHandlers.Add(value); }
remove { listOfHandlers.Remove(value); }
}
public void AddToTop(EventHandler<CancelEventArgs> eventToAdd)
{
listOfHandlers.Insert(0, eventToAdd);
}
public MainPage()
{
InitializeComponent();
this.BackKeyPress += InvokingMethod;
myBackKeyEvent += (s, e) => { MessageBox.Show("Added first"); e.Cancel = true; };
AddToTop((s, e) => { MessageBox.Show("Added later"); });
}
}
Related
I have just a little problem with reading text from label, but...
I have two forms. Form_1, and Form_2.
Form_1 is sending (on demand) text from "label_one" directly to "label_two" in Form_2.
But in Form_2 i have another label called "label_reader" that need to show any changes done in "label_two" text.
I must (dynamically?) read any changes from "label_two" and show it in label_reader.
Never had a similar problem, and have no idea how to do that. It can't be done with the use of a button.
Any help will be greatly appreciated!
You can use the TextChanged event of label_two. Subscribe to that event (most likely in the constructor of your Form_2) and set the text of label_reader when the event is raised:
public partial class Form_2 : Form
{
//...
public Form_2()
{
InitializeComponent();
// your other code
label_two.TextChanged += label_two_TextChanged;
}
// the event handler
private void label_two_TextChanged(object sender, EventArgs e)
{
label_reader.Text = label_two.Text; // or what ever you want to do
}
}
I have many custom controls on my main form that utilize an event to signify they have done processing. They all share this same event (~100 controls or so).
The main form consumes this event but I do not have a clue how to find an efficient way at getting to the one that raised the event without having really inefficient code.
My controls are contained within a List<T> called controlList and are hosted on their own project.
My event looks like so:
public void OnTaskComplete(object sender, custom_control_project.TaskCompleteEventArgs e)
{
foreach (var control in controlList)
{
if (control.Visible) // <--- THIS IS WRONG! WHAT COULD THIS BE???
{
try
{
...// LOTS OF PROCESSING!
}
catch
{
...
}
finally
{
...
}
}
}
}
If I want to use less controls, I make them invisible and disabled, hence the control.Visible.
How can I make it so I only do work on the one control that raised the event without having to process so much unneeded iterations?
The sender parameter is the object that raised the event. You can cast this to a control.
Assuming the all of the controls are wired to the same event (which you indicate):
protected void Button1_Click(object sender, EventArgs e)
{
((Button)sender).Visible = true;
// or more generally:
((WebControl)sender).Visible = true;
}
You will need to cast the sender to a common, base type. If you go with a base type, WebControl will allow you to access the Enabled property while Control will not.
I created a new Event in my user control (SearchControl) like this:
//Event which is triggered on double click or Enter
public event EditRecordEventHandler EditRecord;
public delegate void EditRecordEventHandler(object sender, EventArgs e);
//Supressing the events
private bool _raiseEvents = true;
private void OnEditRecord(System.EventArgs e)
{
if (_raiseEvents)
{
if (this.SearchResultGridView.FocusedRowHandle > -1)
{
if (EditRecord != null)
{
EditRecord(this, e);
}
}
}
}
Now this Event is called when user double click a row in a grid. So from the properties window I selected the MouseDoubleClick Event of the grid view and called the above created EditRecord event.
private void SearchResultListGridControl_MouseDoubleClick(object sender, MouseEventArgs e)
{
// Check whether the user clicked on a real and not a header row or group row
DevExpress.XtraGrid.Views.Grid.ViewInfo.GridHitInfo info = SearchResultGridView.CalcHitInfo(e.Location);
if (info.InRow && !SearchResultGridView.IsGroupRow(info.RowHandle))
{
OnEditRecord(e);
}
}
Now the issue I am facing is every time I double click a row in grid view it calls the SearchResultListGridControl_MouseDoubleClick() which then calls OnEditRecord(), however the value of EditRecord is everytime null.
To solve this I checked the designer file of the Main Control which has SearchControl and could not find the EditRecord Event entry in this. So I manually created it like this:
this.MySearchControl.EditRecord += new performis.BA.Merkmalsleisten.Search.SearchControl.EditRecordEventHandle(this.MySearchControl_EditRecord);
Now the things are working fine, but my question is why it did not create it automatically at the first place? And as far I know it is not recommendable to add anything manually to the designer file..is there any other way I can do it?
Thanks
When you create event it has to be used in the form designer similar to how you are using MouseDoubleClick for the Grid (so you need to find event in the Misc category, because you didn't define CategoryAttribute, double clicked there, etc).
If I understand it right you want to subscribe to event automatically, when form is created. You can do this in the control constructor (find parent form control.Parent or control.FindForm()) or perhaps in the special method, which you have to call from the form constructor, which in turn is basically similar to wiring event manually (which you did in the designer created file, but, instead, you can do in the form file, which is totally ok to edit) Up to you.
Sure.
A better practice would be to add your binding line:
this.MySearchControl.EditRecord += new performis.BA.Merkmalsleisten.Search.SearchControl.EditRecordEventHandle(this.MySearchControl_EditRecord);
To the form's constructor. something like:
public MyForm()
{
this.MySearchControl.EditRecord += new performis.BA.Merkmalsleisten.Search.SearchControl.EditRecordEventHandle(this.MySearchControl_EditRecord);
//The rest of your constructor.
}
I have a custom control "BedGrid" that contains a collection of custom controls, each of which has a click handler on them. In the Page_Load event of my Parent page, I generate a collection of BedGrids, and wire them up to an event handler. This all works fine, when I generate the BedGrids on each Page_Load... The grandchild is clicked, fires the event up to the BedGrid, which alerts my Parent Page and everything goes as planned.
The problem is, it's slow.. Generating all those custom controls on each Page_Load doesn't make sense (especially with trips to the backend). So, I want to cache the collection of BedGrids like so:
protected void Page_Load(object sender, EventArgs e)
{
DrawBedGrids();
}
protected void DrawBedGrids()
{
if (CachedBedgrids == null)
{
CachedBedgrids = new List<BedGrid>();
//Hit DB here and generate list of buildings....
foreach (Building b in buildings)
{
BedGrid bg = new BedGrid(b);
bg.RaiseAlertParentPage += new EventHandler(BedGrid_Clicked);
CachedBedgrids.Add(bg);
}
}
else
{
foreach (BedGrid bg in CachedBedgrids)
{
somePanel.Controls.Add(bg);
}
}
}
protected List<BedGrid> CachedBedgrids
{
get
{
try { return (List<BedGrid>)Session["CachedBedgrids"]; }
catch { return null; }
}
set { Session["CachedBedgrids"] = value; }
}
And it all breaks.. The events never fire... Even if I add
bg.RaiseAlertParentPage += new EventHandler(BedGrid_Clicked);
to the "else" right before I add the BedGrid to the panel..
What am I missing? All of this is happening in Page_Load, so why is the event not firing? Everything else is fine, meaning that the controls and their children draw properly..
The reason this doesn't work is because the controls must be recreated on the post back, and events wired up, for them to fire.
See, since the server is stateless, to fire an event on an object, that object needs recreated AND readded to the form.
How about cache the database result instead and continue the loop otherwise to build new, add, and hookup the controls?
I have a WinForms app that contains many NumericUpDown controls. In a nutshell, if my users enter a value into the control and then delete the text, I want to restore it (the text) when the control loses focus. So I decided that I'd check .Text when the control loses focus and if it's empty, I set .Text = .Value.ToString().
I'm doing this in the Leave event handler and it works just fine. But as I said, I have many of these controls (18, to be exact). I don't like creating 18 Leave event handlers that all do the same thing so I created a generic one like this:
private void numericUpDown_GenericLeave(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(((NumericUpDown)sender).Text))
((NumericUpDown)sender).Text = ((NumericUpDown)sender).Value.ToString();
}
I started to hook up all of the controls to this generic event handler but I quickly got tired of doing this:
numericUpDown1.Leave += numericUpDown_GenericLeave;
numericUpDown2.Leave += numericUpDown_GenericLeave;
numericUpDown3.Leave += numericUpDown_GenericLeave;
...
numericUpDown18.Leave += numericUpDown_GenericLeave;
So I thought I'd create a function that would return a list of all the controls of a specified type and then loop through that list and hookup the event handlers. That function looks like this:
public static List<Control> GetControlsOfSpecificType(Control container, Type type)
{
var controls = new List<Control>();
foreach (Control ctrl in container.Controls)
{
if (ctrl.GetType() == type)
controls.Add(ctrl);
controls.AddRange(GetControlsOfSpecificType(ctrl, type));
}
return controls;
}
I call the function like this:
var listOfControls = GetControlsOfSpecificType(this, typeof(NumericUpDown));
foreach (var numericUpDownControl in listOfControls)
{
numericUpDownControl.Leave += numericUpDown_GenericLeave;
}
When I run my app, however, I don't see the expected behavior that occurs when I manually hookup each control to the generic event handler. This code is currently in the constructor of my form and I've tried calling it before as well as after the call to InitializeComponent() but neither one seems to be working. I get no error of any kind, I just don't see the behavior that I was expecting. I have a breakpoint set inside the generic event handler but the debugger never breaks so it seems like the event handler isn't being hooked up correctly. Does anyone know why this might be or how I can troubleshoot it further? Thanks!
EDIT
I just realized that the call to:
var listOfControls = GetControlsOfSpecificType(this, typeof(NumericUpDown));
was happening before the call to InitializeComponent() so of course the list of controls being returned was empty. DOH! Thanks for all the replys. I apologize for wasting everyones time. :-(
You're passing this to your method, which is presumably a reference to your form. Your method will only catch the controls that are placed directly on your form. Any NumericUpDown controls that are not directly on the form (i.e. they're sitting on a panel or something) will be missed.
Why not create a user control that has a NumericUpDown control in it.
Then handle this is in the user control events.
This worked for me:
private decimal _previous = 0;
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (((NumericUpDown)sender).Text.Length > 0)
{
_previous = this.numericUpDown1.Value;
}
}
private void UserControl1_Leave(object sender, EventArgs e)
{
if (this.numericUpDown1.Text == "")
{
this.numericUpDown1.Value = _previous;
this.numericUpDown1.Text = System.Convert.ToString(_previous);
}
}
Just note that the Leave event is on the user control not on the updown control itself.
Question answered. See Edit above. Thanks to bsegraves for pointing me in the right direction.