Prevent Duplicate Window Instance

Hi All,

Since Implicit Instance = Off is all the rage now I thought this would be a good time to ask this question:

Assuming Implicit Instance = Off:
What is the best way to generically create an instance of a window if it is not already in existance and if it is then move said window to the forefront?

Var t As Introspection.TypeInfo
Var strWindowName As String
Var bWindowFound As Boolean

bWindowFound = False

For each w as DesktopWindow in app.Windows
  
  t = Introspection.GetType(w)
  strWindowName = t.Name
  If strWindowName = NewWindowName Then
    bWindowFound = True
  End If
  
Next

I figure that the next bit of code is something like:

If bWindowFound = False Then
// Struggling to figure out what could should be put here so that the requested window is created
// I know that: Var w as New winWindow  will do this, but I think you have to explicitly use the name.  I am trying to figure out how to pass the name and it create the new instance
Else
NewWindownName.Show
End If

Am I making any sort of sense?

Thanks.

I don’t this can be done generically in Xojo, any more so than you can do with any abstract class. Why do you need it to be generic?

I was just trying to create a generic method to handle the checking and the creation of a window.
If it can’t be done that is fine.

So Implicit Instance is just an additional module, function, and property. Something like this pseudo code:

Module Window1
  Global Class Window1
    ...
  End Class

  Global Function Window1() As Window1
    If mInstance Is Nil Then
      mInstance = New Window1
    End If
    Return mInstance
  End Function

  Private Property mInstance As Window1
End Module

You can setup much of the same logic with shared methods and properties, while giving you a little more control:

Class Window1
  Shared Function SharedInstance(Create As Boolean = True) As Window1
    If mInstance Is Nil And Create = True Then
      mInstance = New Window1
    End If
    Return mInstance
  End Function

  Event Closing()
    If Self = mInstance Then
      mInstance = Nil
    End If
  End Event

  Private Property mInstance As Window1
End Class

Now you would call Window1.SharedInstance.DoWhatever instead of just Window1.DoWhatever. This makes it hard to accidentally call the wrong instance, and allows you to pass False into Window1.SharedInstance if you want to NOT create an instance if one doesn’t already exist.

This is called a Singleton model and if it’s what you need, implicit instance isn’t necessarily a bad thing. In the rare cases I need a singleton window - such as a preferences window - I use a model like this, but I wouldn’t call somebody wrong for using implicit instance instead. It has its uses.

As for doing it generically… don’t. I’ve had too many problems with introspection of the years. It works great until it doesn’t, and then your code behaves in the most bizarre ways.

1 Like

Perfectly. I do it like this. We will assume the “”About” window here.

Var myAbout as AboutWindow

‘We will store the loop bound in a variable first
‘Better than re-evaluating each time through the loop

Var limit as integer = app.WindowCount-1

For x as integer = 0 to limit

If app.WindowAt(x) isa AboutWindow then
     myAbout=AboutWindow(app.WindowAt(x))
     ‘Break the loop since we found it
     Exit
End if

Next

‘Create the instance if it wasn’t there

if myAbout=nil then myAbout=New AboutWindow

MyAbout.Show

Hope this helps.

Thanks @Thom_McGrath and @Jerry_Fritschle , I appreciate the code and your time very much.

This gives me plenty to mull over. :grinning:

1 Like

You’re very welcome—and I just now fixed a typo in my code (doing this on my phone.) :slight_smile:

1 Like

Future Jerry: Re-reading your original post, I more fully understand your desire for a “generic” method, whereas my code is not that. So now you’ve given ME something to play with :slight_smile:

LOL!
If you get it sorted then please, by all means, repost it here! Many would benefit from such a thing!

Thank you Sir.

He won’t. The language just doesn’t support this kind of functionality. I suggest you implement the Singleton pattern detailed above and move on. :wink:

1 Like

Doesn’t this all just re-create the concept of implicit instance, just with a lot more code?

3 Likes

Mostly, yes. As I said, I personally prefer controlling the singleton myself, using an accessor that is clear. But it’s so similar to implicit instance, that I wouldn’t fault somebody or even recommend against using implicit instance.

When you structure your code to remove all possible globals and treat every project item as a mini program, controlling the accessor like this makes a lot of sense. Window1 is always the class name, and Window1.WhateverINamedTheAccessor is always the accessor. There’s no ambiguity. I took away the global Window1 function without taking away the functionality it provides.

What’s wrong with having a property, e.g. in the App class (SingleWindowInstance as MyWindow) and having a function like the following one?

Function GetSingleWindow as MyWindow
if SingleWindowInstance=nil or SingleWindowInstance.SomeControl=nil then
'Window either doesn't exist or was closed
SingleWindowInstance=new MyWindow
end if
return SingleWindowInstance
end function

Or maybe I’ve not understood the question correctly :man_shrugging:.

I mean… it’s an option. Not exactly well organized, but it’s an option. It’s usually better practice to keep factory methods like this with the class itself.

1 Like

I do this with a Shared Method and a static variable:

Public Shared Function GetSharedInstance() As SomeClass
  static sharedInstance as SomeClass
  
  if sharedInstance=nil then
    sharedInstance=new SomeClass
  end
  
  return sharedInstance
End Function

The Constructor for SomeClass is Private so the class can’t be created without going through the shared method.

It is considered bad design. It will retain the object indefinitely in a hidden global scope, no accessors. That means that the obj reference only can be released at App.Close() time, not before.

And yet, there are times when this is perfectly acceptable, which is why I use it.

But you bring up a good point, so here is a revision that addresses those concerns. Once the instance is no longer in use by the external code, it is destroyed and the WeakRef’s value is nil.

Public Shared Function GetSharedInstance() As SomeClass
  static sharedInstance as WeakRef
  
  if sharedInstance.Value=nil then
    dim newInstance as SomeClass
    
    newInstance =new SomeClass
    
    sharedInstance=new WeakRef(newInstance)
  end
  
  return sharedInstance.Value
End Function

Class MyWindow Inherits DesktopWindow

  Private Shared Property SelfInstance As MyWindow


  Public Shared Sub Instantiate()

    If SelfInstance = Nil Or SelfInstance.Handle = Ptr(0) Then
      SelfInstance = New MyWindow
    End
      
  End Sub


  Public Shared Function GetInstance() As MyWindow

    Instantiate

    Return SelfInstance
    
  End Function


  Public Shared Function PeekInstance() As MyWindow

    // Return current ref without a new instantiation, even a nulled one
    If SelfInstance <> Nil And SelfInstance.Handle = Ptr(0) Then SelfInstance = Nil   // if closed, free it
    Return SelfInstance
    
  End Function


  Public Shared Sub ReleaseInstance()

    If SelfInstance = Nil Then Return
    
    If SelfInstance.Handle <> Ptr(0) Then SelfInstance.Close  // if existing, try to close it
    
    If SelfInstance.Handle = Ptr(0) Then SelfInstance = Nil   // if closed, free it
    
  End Sub

End Class

I dislike that you’re relying on a special value (Ptr(0)) to determine that the window has been closed (?).

Fast immediate approach without needing events, in Xojo, after closed, before released, the handle is nulled, you potentially can find a not Nil Window but invalid, I avoid invalid ones to avoid exceptions.