miércoles, 14 de julio de 2010

[WinForms] FormBase – Control de tiempo de actividad

 

Introducción


La idea de este articulo surge con la necesidad de implementar código común para los formulario.

El requerimiento apunta a que los formularios después de cierto tiempo deben deshabilitar sus controles.

 

Definición del Form Base


todo el truco en esta implementación esta en la definición del la clase Base, la cual será usada por todos los formularios de la aplicación para controlar las actividades.

[C#]

public class FormBase : Form
    {
        private Timer _timer;

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            HandlerActitivy(this.Controls);

            _timer = new Timer();
            _timer.Interval = 5000; //se establecen 5 seg
            _timer.Tick += new EventHandler(_timer_Tick);
            _timer.Start();
        }

        void _timer_Tick(object sender, EventArgs e)
        {
            _timer.Stop();

            DisableControls();

        }

        public void Restart()
        {
            EnableControls();

            _timer.Start();

        }

        protected virtual void DisableControls()
        {
        }

        protected virtual void EnableControls()
        {
        }

        private void HandlerActitivy(Control.ControlCollection col)
        {
            foreach (Control ctrl in col)
            {
                if (ctrl is TextBox)
                {
                    TextBox txt = ctrl as TextBox;
                    txt.TextChanged += new EventHandler(txt_TextChanged);
                }
                if (ctrl is ComboBox)
                {
                    ComboBox cmb = ctrl as ComboBox;
                    cmb.SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);
                }
                if (ctrl is CheckBox)
                {
                    CheckBox chk = ctrl as CheckBox;
                    chk.CheckedChanged += new EventHandler(chk_CheckedChanged);
                }
                if (ctrl is GroupBox)
                {
                    if (ctrl.HasChildren)
                    {
                        HandlerActitivy(ctrl.Controls);
                    }
                }
            }
        }

        void chk_CheckedChanged(object sender, EventArgs e)
        {
            TimerRestart();
        }

        void cmb_SelectedIndexChanged(object sender, EventArgs e)
        {
            TimerRestart();
        }

        void txt_TextChanged(object sender, EventArgs e)
        {
           TimerRestart();
        }

        private void TimerRestart()
        { 
            _timer.Stop();
            _timer.Start();  
        }
    }

[VB.NET]

Public Class FormBase
    Inherits Form

	Private _timer As Timer

	Protected Overloads Overrides Sub OnLoad(e As EventArgs)
		MyBase.OnLoad(e)

		HandlerActitivy(Me.Controls)

		_timer = New Timer()
        _timer.Interval = 5000 'se establecen 5 seg

        AddHandler _timer.Tick, AddressOf _timer_Tick

		_timer.Start()
	End Sub

	Private Sub _timer_Tick(sender As Object, e As EventArgs)
        _timer.Stop()

		DisableControls()

	End Sub

	Public Sub Restart()
		EnableControls()

		_timer.Start()

	End Sub

	Protected Overridable Sub DisableControls()
	End Sub

	Protected Overridable Sub EnableControls()
	End Sub

    Private Sub HandlerActitivy(ByVal col As Control.ControlCollection)

        For Each ctrl As Control In col

            If TypeOf ctrl Is TextBox Then
                Dim txt As TextBox = TryCast(ctrl, TextBox)
                AddHandler txt.TextChanged, AddressOf txt_TextChanged
            End If

            If TypeOf ctrl Is ComboBox Then
                Dim cmb As ComboBox = TryCast(ctrl, ComboBox)
                AddHandler cmb.SelectedIndexChanged, AddressOf cmb_SelectedIndexChanged
            End If

            If TypeOf ctrl Is CheckBox Then
                Dim chk As CheckBox = TryCast(ctrl, CheckBox)
                AddHandler chk.CheckedChanged, AddressOf chk_CheckedChanged
            End If

            If TypeOf ctrl Is GroupBox Then
                If ctrl.HasChildren Then
                    HandlerActitivy(ctrl.Controls)
                End If
            End If

        Next
    End Sub

	Private Sub chk_CheckedChanged(sender As Object, e As EventArgs)
		TimerRestart()
	End Sub

	Private Sub cmb_SelectedIndexChanged(sender As Object, e As EventArgs)
		TimerRestart()
	End Sub

	Private Sub txt_TextChanged(sender As Object, e As EventArgs)
		TimerRestart()
	End Sub

	Private Sub TimerRestart()
        _timer.Stop()
		_timer.Start()
    End Sub

End Class

Los puntos a remarcar serian:

- Se define en esta clase un objeto timer que actuaria como controlador del tiempo transcurrido sin actividad.

- En el Load de formulario, el cual es tomado por la clase base al utilizar el OnLoad(), lo cual es posible ya que esta clase hereda de Form.

- En el Load se invoca un método de nombre HandlerActitivy(), cuya funcionalidad es tomar los controles definidos en el formulario y adjunta un evento estándar a cada control del formulario.

Este método si bien cubre el objetivo del ejemplo, puede que se necesite extender para controlar otros controles que pudieran usarse en el formulario.

 

Definición del formulario, usando la clase base


[C#]

public partial class Form1 : FormBase
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }


        protected override void DisableControls()
        {
            txtNombre.Enabled = false;
            txtDireccion.Enabled = false;
            txtMail.Enabled = false;

            btnConfirmar.Enabled = false;

            confirmarToolStripMenuItem.Enabled = false; 

        }

        protected override void EnableControls()
        {
            txtNombre.Enabled = true;
            txtDireccion.Enabled = true;
            txtMail.Enabled = true;

            btnConfirmar.Enabled = true;

            confirmarToolStripMenuItem.Enabled = true; 
        }


        private void btnSalir_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnRestart_Click(object sender, EventArgs e)
        {
            base.Restart(); 
        }

        private void restartToolStripMenuItem_Click(object sender, EventArgs e)
        {
            base.Restart(); 
        }

        private void salirToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

    }

 

[VB.NET]

Public Partial Class Form1
    Inherits FormBase

	Public Sub New()
		InitializeComponent()
	End Sub


	Private Sub Form1_Load(sender As Object, e As EventArgs)

	End Sub


	Protected Overloads Overrides Sub DisableControls()
		txtNombre.Enabled = False
		txtDireccion.Enabled = False
		txtMail.Enabled = False

		btnConfirmar.Enabled = False

		confirmarToolStripMenuItem.Enabled = False

	End Sub

	Protected Overloads Overrides Sub EnableControls()
		txtNombre.Enabled = True
		txtDireccion.Enabled = True
		txtMail.Enabled = True

		btnConfirmar.Enabled = True

		confirmarToolStripMenuItem.Enabled = True
	End Sub


	Private Sub btnSalir_Click(sender As Object, e As EventArgs)
		Me.Close()
	End Sub

	Private Sub btnRestart_Click(sender As Object, e As EventArgs)
		MyBase.Restart()
	End Sub

	Private Sub restartToolStripMenuItem_Click(sender As Object, e As EventArgs)
		MyBase.Restart()
	End Sub

	Private Sub salirToolStripMenuItem_Click(sender As Object, e As EventArgs)
		Me.Close()
	End Sub

End Class

Los puntos a remarcar aquí podría ser

- En la definición del formulario no se hereda mas de Form, ahora se utiliza FormBase

- Se implementaron los método definidos como virtual en la clase base, DisableControls() y EnableControls(), estos métodos permites especificar concretamente que controles se deshabilitan cuando se vence el tiempo, y son invocados directamente desde FormBase.

 

Acerca del ejemplo


Cuando se abra por primera vez el código, si se realiza un doble clik en alguno de los formularios puede que el diseñador muestre un error, ya que requiere de la implementación de FormBase para poder resolver el diseño del formulario

Es por esto que lo primero que se debería hacer es compilar la solución, de esta formas e evita el problema que se comenta con el diseñador de los formularios

 

[C#] 
[VB.NET] 

9 comentarios:

  1. Hola Leandro, como podría hacerse si el formulario principal ya esta heredando (Inherits) de otra clase y al mismo tiempo necesite implementar la técnica del control de tiempo por actividad?

    ResponderEliminar
  2. hola bbionicss

    Podrias hacer que el FormBase herede de la clase de actividad y luego tu form principal del formbase

    o sea armarias una cascada de herencia:

    public class FormControlActividad : Form
    {
    }

    public class FormBase : FormControlActividad
    {
    }

    public class FormPrincipal : FormBase
    {
    }

    como veras es una cascada en dodne uno va heredando de otro, pero simpre el de mas abajo hereda de la clase Form


    saludos

    ResponderEliminar
  3. Una consulta amigo Leandro tu codigo es muy bueno pero el incoveniente es cuando la inactividad parece ser independiente por cada formulario en el caso de ser forms MDI quisiera que la inactividad se reinicie tanto del form padre como del form hijo cuando si hay actividad en uno de ellos no se si me explico graciassss de antemano

    ResponderEliminar
  4. hola adruard

    asi es la validacion de inactividad es por formulario, no es gobal a la aplicacion

    el tema es que implementar algo como lo que planteras cambia completamente la situacion porque cada forma deberia informar de su inactividad y a la vez ser informado para reiniciar el conteo.

    La verdad no se plantee algo asi, pero el problema lo veo en la comunicacion de los formularios, quizas se requiera alguna clase manejadora que interceda en el proceso.


    saludos

    ResponderEliminar
  5. Hola buenas tardes,

    Me sirvió mucho el ejemplo y la explicación.

    Saludos cordiales.

    ResponderEliminar
  6. Hola Leandro,

    Si tuviera codigo dentro de un evento de de mi FormBase y quiero agregarle más desde mi FormHeredado ??

    EJ:
    ----Base---
    protected virtual void Mensaje()
    {
    MessageBox.Show("Hola");
    }
    ----Heredado---
    protected override void Mensaje()
    {
    MessageBox.Show("Sergio");
    }

    === Resultado ===
    Hola
    Sergio

    ResponderEliminar
    Respuestas
    1. hola
      Como resultado solo verias el mensaje que dice "Sergio" ya que te esta faltanto la linea

      base.Mensaje();

      para invocar el metodo del form base

      saludos

      Eliminar
    2. Muchas gracias Leandro, sos GRANDE...

      Eliminar
  7. Funciona bien, el problema es que quiero que si el usuario está inactivo durante x tiempo se le cierre la sesion o lo saque del aplicativo, si tengo mas de 2 formularios activos y estoy trabajando en uno solo, igual se me va a cerrar la sesion por que toma el tiempo para la otra ventana tambien... Hay alguna forma de controlar que no sea por formulario sino por proceso o por aplicativo?.
    Y otra cosa es que se me cierra el Visual Studio cuando heredo de la clase FormBase, solo me funciona la primera vez, es curioso.

    saludos.

    ResponderEliminar