How to detect in VB6 if an event handler has been assigned? - events

If I have a class in VB6, with some events
Public Event SomethingHappened
and I later want to fire that event
RaiseEvent SomethingHappened
This works fine, in my form which is hosting the class
Public WithEvents TheObject as MyClass
...
Public Sub TheObject_SomethingHappened
...
BUT, is there any way to tell in the code which Raises the event, whether the event has been assigned a handler?
Because I would like to do some default behaviour if not.
I see that in VB.NET there is a automatic "SomethingHappenedEvent" variable declared, but that doesn't seem to work in VB6.
I can't find any mention of this on Google, so I suspect it's not possible, but...

As I mentioned in a comment, Microsoft has often dealt with this in its controls and classes by passing a ByRef Boolean "cancel default action" argument to the event handler.
If the handler exist without setting Cancel = True before returning then a default action is taken by the component.
That could be taken as a viable pattern based on established use. There may be alternatives but this seems pretty simple and clean to implement when you have events where you want to offer default actions.

Related

Xamarin Async Constructor

For my application I need to fetch some data asynchronously and do some initialization for each page. Unfortunately, a constructor does not allow me to make asynchronous calls. I followed this article and put all of my code into the OnAppearing method. However, since then I ran into multiple issues since each platform handles the event a little bit differently. For example, I have pages where you can take pictures, on iOS the OnAppearing is called again every time after the camera is closed while Android doesn't. It doesn't seem like a reliable method for my needs, which is also described here:
Calls to the OnDisappearing and OnAppearing overrides cannot be treated as guaranteed indications of page navigation. For example, on iOS, the OnDisappearing override is called on the active page when the application terminates.
I am searching for a method/way where I can perform my own initialization. The constructor would be perfect for that but I cannot perform anything asynchronously in there. Please do not provide me with any work arounds, I am searching for a solution that is the "recommended" way or maybe someone with a lot of experience can tell me what they are doing. (I also don't want to .Wait() or .Result as it will lock my app)
You can use Stephen Cleary's excellent NotifyTaskCompletion class.
You can read more how it works and what to do/don't in these cases in Microsoft's excellent Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding. The highlights of this topics are:
Let’s walk through the core method
NotifyTaskCompletion.WatchTaskAsync. This method takes a task
representing the asynchronous operation, and (asynchronously) waits
for it to complete. Note that the await does not use
ConfigureAwait(false); I want to return to the UI context before
raising the PropertyChanged notifications. This method violates a
common coding guideline here: It has an empty general catch clause. In
this case, though, that’s exactly what I want. I don’t want to
propagate exceptions directly back to the main UI loop; I want to
capture any exceptions and set properties so that the error handling
is done via data binding. When the task completes, the type raises
PropertyChanged notifications for all the appropriate properties.
A sample usage of it:
public class MainViewModel
{
public MainViewModel()
{
UrlByteCount = new NotifyTaskCompletion<int>(
MyStaticService.CountBytesInUrlAsync("http://www.example.com"));
}
public NotifyTaskCompletion<int> UrlByteCount { get; private set; }
}
Here, the demo is about binding the returned asynchronous value to some bindable property, but of course you can you is without any return value (for simple data loading).
This may be too simple to say, but you CAN run asynchronous tasks in the constructor. Just wrap it in an anonymous Task.
public MyConstructor() {
Task.Run(async () => {
<Your code>
}
}
Be careful when doing this though as you can get into resource conflict issues if you accidentally open the page twice.
Another thing I like to do is use an _isInit flag, which indicates a first time use, and then never again.

Why must I use UserControl.MousePointer instead of Me.MousePointer?

In VB6 on a UserControl, I must use UserControl.MousePointer = vbDefault instead of Me.MousePointer = vbDefault. I can use Me.MousePointer on a Form (and Form.MousePointer doesn't work).
Why must I use UserControl.MousePointer instead of Me.MousePointer?
I mean literally the text "UserControl", not UserControl as the placeholder for another control name.
Me isn't what you seem to think it is. It is a reference to the current instance of the module you use it in, not "magic."
To get what you want you must add this property to the default interface of your UserControl, e.g.:
Option Explicit
Public Property Get MousePointer() As MousePointerConstants
MousePointer = UserControl.MousePointer
End Property
Public Sub Test()
MsgBox Me.MousePointer
End Sub
In VB6 Forms are a little different, probably as a holdover from 16-bit VB to make porting old code easier. These always seem to inherit from a hidden interface. This is defined in a type library you don't have access to since Microsoft did not release it as part of VB6. Attempting to query it typically comes up with an error like:
Cannot jump to 'MousePointer' because it is in the library 'Unknown10' which is not currently referenced
From this alone it seems likely that using Me always carries a small performance penalty. Instead of going directly to the module's procedures it appears to me that you are going through its default COM interface.
You'd have to inspect the compiled code to determine whether there is a performance penalty, and if so how much. I don't see this documented so otherwise we are just guessing about it.
In any case there is little reason to ever use Me unless you must in order to qualify something.
Crummy example but:
Option Explicit
Private mCharm As Long
Public Property Get Charm() As Long
Charm = mCharm
End Property
Public Property Let Charm(ByVal RHS As Long)
mCharm = RHS
'Maybe we do more here such as update the user interface or some
'other things.
End Property
Public Sub GetLucky(ByVal Charm As Long)
'Do some stuff.
Charm = Charm + Int(Rnd() * 50000)
'etc.
Me.Charm = Charm 'Here we use Me so we can assign to the property Charm.
End Sub
That's really about the only legitimate use for Me anyway: scoping to the desired namespace. Relying on it because in typing it brings up IntelliSense is just lazy.
If anything Forms are "broken" not UserControls.
Figured it out. It turns out that since a UserControl is an ActiveX control, VB6 does some magic for you. With a Form control, it's not an ActiveX control which is why the MousePointer property is accessible via Me, like you'd expect.
For UserControl's, the UserControl control you create in VB6 sits on another control - the ActiveX control. That ActiveX control is accessible via UserControl. Something like this:
class ActiveXControl
{
int MousePointer;
VBUserControl control;
}
class VBUserControl
{
}
class YourUserControl : VBUserControl
{
ActiveXControl UserControl;
// we must use UserControl.MousePointer
}
but for a Form, it's more like this:
class Form
{
int MousePointer;
}
class YourForm : Form
{
// we actually inherit from Form so we use Me.MousePointer
}

How to get parent form of nested VB6 UserControl

There is a similar question here, which unfortunately does not help me. When I make a call to UserControl.Parent, either a Form or another UserControl can be returned. If a Form is returned, I have what I want. But if a UserControl is returned, I have no way of iterating up the chain, since UserControl is the base class name, and I do not have access to the base class name outside of the control's implementation.
Technically, I could probably get around this by exposing the Parent property on every single UserControl in the application, but I would really like to avoid doing this (we have thousands of them).
My ultimate goal is to get a reference to the parent form which is hosting the control, so that the control can subscribe to the Form_Unload event. Here the control will remove and clean up a hosted .NET interopped control which is preventing the VB6 UserControl from raising its UserControl_Terminated event, thus leaking GDI objects and memory.
So far I have tried to make calls to GetParent(), GetWindow() and GetAncestor() functions in USER32.dll in the UserControl_Initialize and UserControl_Resize events, and then cross referencing with the hWnds on the forms in the Forms collection, but both of these events seem to be raised before the UserControl has been sited on its host form.
I did some fiddling around, and this should give you something to go on. I just created two UserControls, called Internal and External. Internal has a command button, External has a frame. I put an instance of Internal on External, inside the frame. Then I put an instance of External on a form, whose name I just left at Form1.
So, I have a control within a control on a form. The problem is to find a reference to Form from the context of the internal control.
First, I have a method called Test on the Form, so:
Public Sub Test
MsgBox "Test Succeeded"
End Sub
Now, in the internal control's command button's Click event handler:
Private Sub cmdTest_Click()
UserControl.ParentControls(0).Parent.Test
End Sub
Running the Form project and clicking the command button successfully calls the Form's Test method. (Yee ha.)
So, to distinguish between a nested control and a control directly on the form, you can do something along these lines:
Private Sub cmdTest_Click()
If TypeOf UserControl.Parent is Form Then
UserControl.Parent.Test
Else
UserControl.ParentControls(0).Parent.Test
End If
End Sub
I then tried nesting the external control in another control (External2) and putting an instance of External2 on the form. In the debug window, I did this:
? typename(usercontrol.ParentControls(0))
External
? typename(usercontrol.ParentControls(0).Parent)
External2
And that's as far as I got. Trying things like this:
? typename(usercontrol.ParentControls(0).Parent.ParentControls(0))
or anything similar, got an "Object doesn't support this property or method" error.
If you need to evaluate controls nested more than one deep (you haven't said for sure, but "iterate up the chain" suggests that you might), that might be beyond the capabilities of this technique. You can mess with the Controls and Name properties as well, and maybe figure something out. But it looks like the ParentControls property doesn't extend to parents of controls that are themselves controls, unless of course you go to the trouble of exposing them with a different property name. At least you can iterate one more link up the chain with this.
In general, it doesn't look like the Parent property or its derivatives contain actual references to the parent objects, but some sort of subset of their properties and methods. For example, I can get the hWnd property of the UserControl, but not of UserControl.Parent. Even specifically referencing the parent by name (the name of the actual control instance is External1, so ? External1.hWnd) fails to get the hWnd property, throwing an "Object Required" error. That would appear to complicate the possibility of using the API for a solution.
Anyway, I leave it to you to play around with. If you get further than I have, I would be interested to see your results.
I was able to find the parent form by traversing parent/child relationships using HWNDs and the win32 API. My code is roughly as follows:
Private Declare Function GetParent Lib "USER32" (ByVal Hwnd As Long) As Long
Private Declare Function GetClassName Lib "USER32" Alias "GetClassNameA" _
(ByVal Hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Public Function FindParentForm(Hwnd As Long) As Form
Dim ParentHwnd As Long
Dim CandidateForm As Form
Dim strTmp As String * 255
Dim lngLngth As Long
Dim strTitle As String
ParentHwnd = Hwnd
Do While (Not ParentHwnd = 0)
ParentHwnd = GetParent(ParentHwnd)
lngLngth = GetClassName(ParentHwnd, strTmp, 255)
strTitle = Left(strTmp, lngLngth)
'ThunderFormDC is the Debug version of a VB6 form, and ThunderRT6FormDC is the Release/Runtime version of a VB6 form
If strTitle = "ThunderFormDC" Or strTitle = "ThunderRT6FormDC" Then
Exit Do
End If
Loop
For Each CandidateForm In Forms
If CandidateForm.Hwnd = ParentHwnd Then
Set FindParentForm2 = CandidateForm
Exit Function
End If
Next
'Didn't find the parent form somehow
Set FindParentForm2 = Nothing
End Function
As I mentioned in the question, the problem with this solution is that during the UserControl_Initialize and UserControl_Resize events, the parent/child relationships have not been setup between HWNDs yet. If you try to traverse the parent/child relationships, you will find that the parent of the usercontrol is a class named "Static".
I was able to work around this issue by searching for the parent form in a manual Init() procedure for my user control. However, many forms do not call the Init() procedure unless the tab it is on is clicked on (in an attempt to get some sort of lazy loading implemented in VB6). I was able to work around this by refactoring the control to not dynamically create/add the .NET interopped control until the Init() procedure was called. By this time, the parent/child relationships seem to be set up.
An alternative solution to the lazy loading problem is to hook a WndProc procedure, and listen for the WM_CHILDACTIVATED message. However, this message only gets sent when the child control changes parents. It does not propogate to grandchildren. It should, however, be possible to hook another WndProc to the new parent control and listen for it's own WM_CHILDACTIVATED message and so on until a child control gets parented to a ThunderForm. However, since WndProc can only be implemented in a static module, I didn't feel like keeping track of parent/child relationships.
I'm rusty, but isn't the Container property what you're after?
Be aware, when interopping with dot net, that dot net has very different garbage collection. Just because the VB6 objects have been released doesn't mean that the dot net objects have been. They could still be lurking in memory and - in rare cases - could still be firing events on timers.
So make sure that your dot net control is cleaning itself up properly, and has a method which VB6 can fire in order to force this termination to occur.
See: https://msdn.microsoft.com/en-us/library/aa445702(v=vs.60).aspx
I solved my referencing problem using UserControl.Parent in ReadProperty.
I tried GetParent() with no success. Also tried UserControl.Parent in Initialize.
The problem is that during Initialize the object doesn't exist. It is being builted before link to form (Parent).
In ReadProperty (or Resize) it already exists and you will be able to use UserControl.Parent.
For example, I use UserControl.Parent.name to save some information in window register.
This is asociated with this topic so I post it here for use as necessary in special limited cases. It can't get too deep into the nesting, but was is a quick way to get information in my case and others may find it of use.
I have a a Control (cmdPageDown1) placed on a User Control, that is placed on a User Control (xMCGridNew1), which is placed on a Form (frmMC).
As others have pointed out, I can't get to the Form Name with any quick single line of code, BUT with these simple lines, I can get the names of the other 2 nested user controls and the lower level Control.
For Example:
'Debug.Print '[Roadblock Here] 'Top Level frm MC (Parent)
Debug.Print Screen.ActiveControl.Name 'SubLevel 1 xMCGridNew1 (Child)
Debug.Print UserControl.Name 'SubLevel 2 xpage (GrandChild)
Debug.Print ActiveControl.Name 'SubLevel 3 cmdPageDown1 (Great Grandchild)
This will yield in the immediate window corresponding to the three nest levels.
xMCGridNew1
xPage
cmdPageDown1
As mentioned by another poster (Orignal Poster) in this thread, regarding attempted use of the Container property:
The Container property has two issues. First, UserControl's do not have a >Container property. You must get it off of the UserControl.Extender >property. Second, the Container may be another UserControl. Since the >UserControl.Extender property is a base class property, it is not accessible >outside of that UserControl's specific implementation. I.e. UserControl1 >cannot access UserControl2.UserControl.Extender.Container. Thus the >Container property has the same problems as the Parent property - I would >need to modify every user control in our application (too many). – Taedrin >Jan 15 '16 at 15:08

Which event should I use just before a page is shown on browser on Plone to trigger a subscriber?

I want to create a subscriber that gets triggered when the user tries to access the resource (which is a custom content-type). So, the object is not being added, modified, nothing, is just being traversed. Something like a Zope View Event.
So, basically, suppose a custom content type has a custom workflow (two states: private and viewed). The initial state is private. This content type is only going to be created programatically, using _createObjectByType by anonymous users. Suppose an object called myobjet was added, programatically, to the root folder of my Plone site.
What I want is: when the user access
http://localhost:8080/Plone/myobject
...it automatically changes the state of the workflow of this object to viewed. The url http://localhost:8080/Plone/myobject is going to be a custom view, not the default base_edit.
Which event should I use? I tried IEndRequestEvent and IBeforeTraverseEvent from this list and none of them work: the handler is not being called for my custom object interface.
I've tried other events with my custom object interface (like IObjectEditedEvent), and, for this event, my handler is called when I edit an object that implements the interface. But using IEndRequestEvent and IBeforeTraverseEvent doesn't call the handler.
IEndRequestEvent and IBeforeTraverseEvent only work when I set the subscriber to all interfaces:
<subscriber
for="*
zope.app.publication.interfaces.IBeforeTraverseEvent"
handler=".subscriber.myhandler"
/>
And when I make myhandler print the object and the event in this situation, it shows:
<PloneSite at Plone>
<zope.app.publication.interfaces.BeforeTraverseEvent object at 0xd52618c>
If the solution is to write an event myself, is there an easy tutorial for this?
You might want to have a look at http://pypi.python.org/pypi/plone.validatehook.
Make sure you bind the event to the right interface. If you bind it to "Interface" (as described on the plone.validatehook pypi page) the event will get called for every single request. In order to restrict the event to contentish objects you can do the following:
from Products.CMFCore.interfaces import IContentish
#adapter(IContentish, IPostValidationEvent)
def RedirectMember(object, event):
...
(Edit: I removed my first answer because it didn't work)
Not sure what this subscriber is supposed to do, but if the object is not being modified, added or whatsoever than I must suspect it will just be viewed...so why not just use the __call__ method of the items view (or the __update__ method if you are using five.grok/dexterity)?

Is it a good design to put event handling code into an own method?

Image a Button on your windows form that does something when being clicked.
The click events thats raised is typically bound to a method such as
protected void Button1_Click(object
sender, EventArgs e) {
}
What I see sometimes in other peoples' code is that the implementation of the buttons' behaviour is not put into the Button1_Click method but into an own method that is called from here like so:
private DoStuff() { }
protected void Button1_Click(object
sender, EventArgs e) {
this.DoStuff();
}
Although I see the advantage here (for instance if this piece of code is needed internally somewhere else, it can be easily used), I am wondering, if this is a general good design decision?
So the question is:
Is it a generally good idea to put event handling code into an own method and if so what naming convention for those methods are proven to be best practice?
I put the event handling code into a separate method if:
The code is to be called by multiple events or from anywhere else or
The code does not actually have to do with the GUI and is more like back-end work.
Everything small and only GUI-related goes always into the handler, sometimes even if it is being called from the same event (as long as the signature is the same). So it's more like, use a separate method if it is a general action, and don't if the method is closely related to the actual event.
A good rule of thumb is to see whether the method is doing sometihng UI specific, or actually implementing a generic action. For example a buttock click method could either handle a click or submit a form. If its the former kind, its ok to leave it in the button_click handler, but the latter deserves a method of its own. All I'm saying is, keep the single responsibility principle in mind.
In this case, I'd keep the DoStuff() method but subscribe to it with:
button1.Click += delegate { DoStuff(); }
Admittedly that won't please those who do all their event wiring in the designer... but I find it simpler to inspect, personally. (I also hate the autogenerated event handler names...)
The straight answer is yes. It's best to encapsulate the task into it's own method not just for reuse elsewhere but from an OOP perspective it makes sense to do so. In this particular case clicking the button starts a process call DoStuff. The button is just triggering the process. Button1_Click is a completely separate task than DoStuff. However DoStuff is not only more descriptive, but it decouples your button from the actual work being done.
A good rule of thumb is to see whether
the method is doing sometihng UI
specific, or actually implementing a
generic action. For example a buttock
click method could either handle a
click or submit a form. If its the
former kind, its ok to leave it in the
button_click handler, but the latter
deserves a method of its own. All I'm
saying is, keep the single
responsibility principle in mind.
I put the event handling code into a
separate method if:
The code is to be called by multiple events or from anywhere else
or
The code does not actually have to do with the GUI and is more like
back-end work.
Everything small and
only
GUI-related goes always into the
handler, sometimes even if it is being
called from the same event (as long as
the signature is the same). So it's
more like, use a separate method if it
is a general action, and don't if the
method is closely related to the
actual event.

Resources