To implement a tab-based environment in WPF we need to convert our forms to user controls, however when doing this, the Loaded event of the user control is called two times.
While searching on the internet other people also pointed this issue. How can we ensure that loaded event is called only once? Because when it is called multiple times, initialization of our controls happens multiple times.
As explained in this blog, the Loaded event is fired when ever a control is about to be rendered (i.e. added to the visual tree).
There are several controls that would cause your control to be loaded/unloaded multiple times. For example, the native WPF TabControl only renders the content of the selected tab. So when you select a new tab, the content of the previously selected tab is unloaded. If you click back to the previously selected tab, then it's content is reloaded.
One work around is to use a Boolean to flag whether you have already initialized your control, as suggested by others. Alternatively, you may be able to use the Initialized event instead.
Your routed event handler can (and should) remove itself from the Loaded hook as the first thing it does.
public class MyClass : Window
{
public MyClass()
{
Loaded += MyLoadedRoutedEventHandler;
}
void MyLoadedRoutedEventHandler(Object sender, RoutedEventArgs e)
{
Loaded -= MyLoadedRoutedEventHandler;
/// ...
}
};
Set a loaded flag in the event, and, if the flag has already been set, don't do anything.
As mentioned above,you can use bool flag for it.
bool isPageLoadingForFirstTime = true;
public void LoadedEvent()
{
if(ispageLoadingForFirstTime)
{
//do something
ispageLoadingForFirstTime = false;
}
}
Related
Is there an event for panel that is equivalent to form event Shown?
I had a few couple of panel switching within a form which will never be closed.
However i couldn't find anything close to an event like Shown which is used in form.
The closes i had is Paint event. However i only wish to update the panel once every time it is shown.
Form.Shown is not raised every time the form is shown, rather it Occurs whenever the form is first displayed. This being said, there is no Panel.Shown event, and no event which is raised "whenever a panel is first displayed".
You can simulate this behavior with the Panel.Paint event, using a flag to keep track of whether it's been "shown" once before. This will make it behave similar to Form.Shown.
private bool panel1Painted = false;
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (!panel1Painted)
{
// do your shown stuff here
panel1Painted = true;
}
}
To keep in the spirit of Form.Shown, you may want to reset the flag if the Panel is reconstructed. This is not the same as shown.
You could listen on the VisibleChanged event and only act on when visibility = true.
https://msdn.microsoft.com/en-us/library/system.windows.forms.panel_events%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
You could also experiment with the Enter and Invalidated events to see if these give you the results you want.
Or if disabling the panel when leaving it is an option, you might be able to use the EnabledChanged event in your toolbox.
E.g. instead of having a button to initiate the method, the method automatically happens without any user interaction - automatically.
private void button13_Click(object sender, EventArgs e)
{
try
{
ServiceController sc = new ServiceController();
sc.ServiceName = "Spooler";
if (sc.Status.ToString().ToLower() == "stopped")
{
serviceStatusLabel.Text = "Installed but stopped";
}
if (sc.Status.ToString().ToLower() == "running")
{
serviceStatusLabel.Text = "Installed and started";
}
}
catch
{
serviceStatusLabel.Text = "Service not installed";
}
}
I just want the Label object to show the service status when the form is loaded up, without using a button
EDIT: Given your comment, are you actually after the Form.Load event? It sounds like it. Any event handlers subscribed to that event will be executed "when the form is displayed for the first time".
(The confusing thing is that your title talks about "On-Load" of an object whereas it sounds like you really want the method to be called when the form is loaded.)
It's not really clear what you mean by "when its output on the form" but you might want to look at the TextChanged and VisibleChanged events. That's if you want something to happen when the label is altered.
If you're looking for when the service status is altered, it doesn't look like there's an event raised for that, I'm afraid. Note that it would be much cleaner to switch on the enum value rather than to convert it to a string, lower it, and then compare that with hard-coded constants.
... Do I get your question correctly?
You want a piece of code to be executed when an object or the form is loaded?
Well that's easy :p
Click on your object (or form) in the designer, in the properties dock, click the lightning bolt icon, go to the Load or Show event, and double-click the box.
A new piece of code should now be created in the code view, something like this:
private void Object_Load(blabla) handles Object.Load
{
}
Whatever code is in that event will be executed when the object is loaded or shown.
If you create a handler for the Load event, it will run when the form gets loaded.
I need to change a certain DataGridView's property (a DataSourceUpdateMode for one of its binding) only when ALL of its initial data bindings are completed.
I tried subscribing to the "DataBindingComplete" event, but it's fired too many times (one or more time for each binding associated to the control); what I need is a more global "AllDataBindingsComplete" event, fired when the control is ready to be displayed to the user.
As a temporary workaround, I'm using the MouseDown event (I've assumed that when the user is able to click the control, it means that the control is displayed... :) and the events I'm playing with - SelectionChanged - are fired after the MouseDown):
protected override void OnMouseDown(MouseEventArgs e)
{
Binding selectedItemsBinding = this.DataBindings["SelectedItems"];
if (selectedItemsBinding != null)
{
selectedItemsBinding.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
}
base.OnMouseDown(e);
}
It works, but it smells like an ugly hack A LOT (and it's called too many times, only one time is enough for my needs).
Is there a better way?
(yes, I'm trying to adopt MVVM in a Windows Forms project, and I've added a bindable "SelectedItems" property to the DataGridView...)
What I've done at the Windows Forms form level, and may be improvised down to just the control(s) you want, is to subclass the Windows Forms baseclass into my own. Then, in its constructor, attach an extra event call to the Load() event.
So when everything else is completely loaded, only THEN will it hit my custom method (of the subclass). Since it is the bottom of the call-stack chain being attached to the event queue, I know it's last and everything else is done... Here's a snippet of the concept.
public class MyForm : Form
{
public MyForm()
{
this.Load += AfterEverythingElseLoaded;
}
private void AfterEverythingElseLoaded(object sender, EventArgs e)
{
// Do my own things here...
}
}
This concept can be applied to the Init() function too if that's more appropriate for your control... Let everything else within it get initialized(), then do you the "AfterInitialized()" function.
I need to execute code before a wpf user control is unloaded and cancel the unloading if certain conditions are met and keep the control open in its current state in the ui...
Is there any way I can accomplish this? I couldnt see anything like unloading event?
Thanks,
Unloaded is fired when the control is removed from the WPF visual tree. As far as I've been able to read there is no "Unloading" event as there is, I think, in Windows Forms. But, "Unloaded" doesn't mean that the control is destroyed, just that it's removed from the visual tree.
Keep a reference to the control in a separate place in your code, along with a little bit of metadata about its parent control. You can probably collect that metadata by storing a reference to the Parent property in your Initialized event handler.
Then, when Unloaded is called, make your tests in the Unloaded event handler, and if your conditions are met, re-insert the control into the logical tree. The ContentControl class has an explicit AddChild protected method you could call.
There are probably some side effects to watch out for; According to the documentation, Unloaded is called when themes are changed at the OS level, when the WPF visual tree reconstitutes itself.
There is an Unloaded event on System.Windows.Controls.Control, but I don't know an elegant way to stop unloading the control with it.
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
if (ConditionsMet) { e.Handled = true; }
}
If ConditionsMet the Unloaded event will be set to true henceforth keeping your control in the VisualTree - your control does not Unload
I have a page and a user control — we'll call them Detail.aspx and Selector.ascx.
Let's say the page shows the details of individual records in a database. The user control basically consists of a DropDownList control and some associated HTML. The DropDownList displays a list of other records to switch to at any time.
When the DropDownList fires its SelectedIndexChanged event, I'd like the parent page, Detail.aspx in this case, to handle it. After all, he'll need to know what was selected so that he can appropriately change the URL and the details shown, etc.
To do that, I've done what I usually do, which is also what the top answer says to do in this StackOverflow question:
public event EventHandler DropDownSelectedIndexChanged
{
add
{
MyDropDownList.SelectedIndexChanged += value;
}
remove
{
MyDropDownList.SelectedIndexChanged -= value;
}
}
The above code appears in the Selector.ascx.cs codebehind file.
As a result, on Detail.aspx, I can use it like so:
<cc1:RecordSelector ID="RecordSelector1" runat="server"
OnDropDownSelectedIndexChanged="RecordSelector1_DropDownSelectedIndexChanged" />
So far nothing fancy or surprising.
Here is my problem:
This causes a NullReferenceException when the browser hits Detail.aspx.
Debugging the problem shows that when the page is first hit, the public event I've shown above tries to add the event, but MyDropDownList is null, thus throwing the exception. From what I can tell, the events are added (or attempted to be added) before the Selector user control's Load event fires and thus also before the DropDownList's Load event fires.
Curiously, if I omit the OnDropDownSelectedIndexChanged attribute from Detail.aspx and instead put the following in the Page_Load event in Detail.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
RecordSelector1.DropDownSelectedIndexChanged += new EventHandler(RecordSelector1_DropDownSelectedIndexChanged);
}
It works exactly as expected. The events are attached and handled just fine. No problems.
But this means several bad things:
I have to remember not to use the designer to add said event onto my user control
I have to remember not to add the event via attributes when working in source view
Worst of all, as the control's author I need to make sure everybody else using my control knows 1 and 2
So what am I doing wrong? Every example I've seen thus far shows similar usage of exposing child controls' events through a user control.
The reason this works:
protected void Page_Load(object sender, EventArgs e)
{
RecordSelector1.DropDownSelectedIndexChanged
+= new EventHandler(RecordSelector1_DropDownSelectedIndexChanged);
}
and this does not:
<cc1:RecordSelector ID="RecordSelector1" runat="server"
OnDropDownSelectedIndexChanged="RecordSelector1_DropDownSelectedIndexChanged" />
is because the first one adds the handler after the control has been initialized (via the page's Init). The second example gets parsed much earlier and as such the page is attempting to add the handler before the control has initialized.
Due to the nature of the page's life cycle I think you may have to live with adding the event handler in the code-behind. There will be no way to add the handler before the control is initialized because that control will always be null prior to initialization.