让WinForm打开一次的3个办法

来源:互联网 发布:2016年大数据市场规模 编辑:程序博客网 时间:2024/05/11 02:05

在开发实践中,我们有时会限制对某些窗体的操作行为,比如让用户只对某个窗体打开一次。对于这种行为,我们不能能对用户说“这个菜单项你只能点击一次,当窗口打开后就不能再点击它了”。作为有责任心的程序员们,都会想办法解决这个问题。

在处理这个问题上,我前后想出了两个办法,第一个办法实实在在地解决了问题;第二个办法将实现封装到了简单的接口中。

办法一:

 创建一个共享变量,将窗体当前的引用保存到里面,例子代码:

Public Class MyForm
             inherits System.Windows.Forms.Form
........
'声明为窗体的成员变量mRef
public shared mRef as System.Windows.Forms.Form

'在窗体的Load事件中,将窗体实例的引用保存到mRef中
mRef = Me

'在窗体的Closed事件中,将mRef置为Nothing
mRef = Nothing
...........

在打开这个窗体以前,做一些判断:
dim frm as MyForm
if IsNothing(MyForm.mRef) then
      frm = new MyForm
      frm.show()
      frm.BringToFront()
else
      frm.BringToFront()
end if
这样,就可以保证这个窗体只能被打开一次了。
在这里,起关键作用的是Shared类型的成员变量,它的生命周期同应用程序的生命周期一样,如果将其声明为private或者是protected,那它只能被该类的成员访问,如果把它声明为public,那它的作用就相当于全局变量了。在以上代码中,我们在窗体加载时窗体的实例引用保存到mRef中,在窗体关闭时被清空,那么很自然我们就可以在创建这个窗体的实例之前,通过mRef来判断窗体是否已经打开。

这种方法很简单,也很实用,就几行代码的事情,但是在现实的开发中,将这几行代码放到每个窗体中,和窗体中的那些复杂的业务逻辑处理代码放在一起,想起来心里有些不舒服,多一行代码就多了一点出错的可能性,所以,当我在我的第二个工程中遇到同样的问题时,我觉得我必须将这个功能封装起来,以后可以直接调用。

办法二:

我首先想到的是用一个函数来直接创建窗体,当这个窗体被创建后,将它的实例引用保存到一个静态列表中。
public Module Fm1
      public function Create() as Object
            static refList as ArrayList
            if isNothing(refList) then refList = New ArrayList
            ..........
      end function
End Module
但是这个想法很快就被证明是错误的,1:根本不知道要创建的窗体实例的类型;2:当创建的窗体被关闭后,如何才能将refList中保存的实例引用remove呢。

 后来我想,只让窗体创建一次,是对窗体进行全局管理的观念,那么是要建立一个全局变量来管理窗体吗?这无疑会带来麻烦,因为调用这会为了使用这个组件而引入新的全局变量。这时,我又一次想到了shared类型的变量,我只需要建立一个对这个窗体来说的全局变量,将这个变量声明为private shared即可以保证每个窗体都能够访问到它,又能够保证这个功能本身的封装。

我的第二个办法是创建一个窗体基类,将功能封装到这个窗体中,需要使用这个功能的窗体都从它继承。先考虑使用private shared 类型的成员变量来保存已经打开的窗体实例引用,然后考虑在创建窗体时,比较该窗体实例的引用是否已经存在,如果存在,那么将新创建的实例注销,返回先前已经打开的那个窗口实例。
具体代码:
Public Class FrmOpenOnce
             inherits System.Windows.Forms.Form

private shared mRefList as ArrayList = New ArrayList

'通过该方法来创建窗体
Public Shared Function Create(frm as FrmOpenOnce) as FrmOpenOnce
      If IsNothing(frm) then Throw New Exception("窗体没有被实例化")
     
      dim item as FrmOpenOnce
      For Each item in mRefList
             if item.gettype.tostring = frm.gettype.tostring then
                    frm.dispose()
                    return item
             end if
      End For

      '添加实例引用
      item = frm
      mRefList.Add(item)
      return item
End Function

Protected Overrides Sub OnClosed(ByVal e As System.EventArgs)
            Dim item As FrmOpenOnce
            Dim pos As Int32 = -1

            For Each item In mRefList
                pos += 1
                If Me.GetType.ToString = item.GetType.ToString Then
                    '发现了对相同窗体的引用
                    Exit For
                End If
            Next
            '删除该对象
            If pos > -1 Then mRefList.RemoveAt(pos)
end sub

现在,可以通过继承该窗体,来实现保证一个窗体只被打开一次的功能,例如:
Public class MyForm
      inherits FrmOpenOnce
     ....................

end sub

创建该窗体的方法:
dim frm as MyForm = MyForm.Create(New MyForm)
不管上面那句代码执行多少次,都只会打开一个窗体。

办法三:

上面那段代码有个地方让我越想越不爽,就是在判断之前,必须先创建这个窗体的实例,有没有直接通过窗体类型来判断的方法呢?我想起我在进行.net remoting编程的时候,使用过Activator来创建远程对象的实例,我想我应该也可以用这个类来创建本地对象的实例吧。看了一下.Net SDK 中有关Activator的帮助信息,才发现.Net框架真是做得很强大。下面我把我的最终版本贡献出来:
Public Class FrmOpenOnce
        Inherits System.Windows.Forms.Form

 Private Shared mRefList As ArrayList = New ArrayList

'通过类型的名称来创建,如果名称不正确,那么抛出异常
        'fileName:文件名称,frmType:对象类型,被创建的对象必须是该类的继承类的对象
        Public Shared Function Create(ByVal fileName As String, ByVal frmTypeName As String) As FrmOpenOnce
            Dim item As FrmOpenOnce

            For Each item In mRefList
                If item.GetType.ToString = frmTypeName Then
                    '发现了相同的窗体
                    Return item
                End If
            Next

            item = DirectCast(Activator.CreateInstanceFrom(fileName, frmTypeName).Unwrap, FrmOpenOnce)
            mRefList.Add(item)

            Return item

        End Function


        Protected Overrides Sub OnClosed(ByVal e As System.EventArgs)
            Dim item As FrmOpenOnce
            Dim pos As Int32 = -1

            For Each item In mRefList
                pos += 1
                If Me.GetType.ToString = item.GetType.ToString Then
                    '发现了对相同窗体的引用
                    Exit For
                End If
            Next
            '删除该对象
            If pos > -1 Then mRefList.RemoveAt(pos)
        End Sub
    End Class

 以上的方法通过类型名称来判断该窗体的实例是否已经创建,所以比前一方法要好。
其它朋友还有什么好的方法,还请赐教!

 

原创粉丝点击