comment
IRC Chat
play_arrow
Este sitio utiliza cookies propias y de terceros. Si continúa navegando consideramos que acepta el uso de cookies. OK Más Información.

Cerrando programa WinForms tras 15 minutos de inactividad

  • 3 Respuestas
  • 899 Vistas

0 Usuarios y 1 Visitante están viendo este tema.

Conectado solid water

  • *
  • Underc0der
  • Mensajes: 55
  • Actividad:
    23.33%
  • Reputación 1
    • Ver Perfil
« en: Octubre 11, 2018, 03:53:38 pm »
Cerrar programa winforms tras 15 minutos de inactividad por parte del usuario.
No parece tan difícil pero veamos como después se complica un poco.
Supongamos que tenemos un formulario padre con 2 botones que llama a los formularios hijos:



Código: C#
  1.    private void button1_Click(object sender, EventArgs e)
  2.         {
  3.             Form form2 = new Form2();
  4.             form2.Show();
  5.         }
  6.  
  7.         private void button2_Click(object sender, EventArgs e)
  8.         {
  9.             Form form3 = new Form3();
  10.             form3.Show();
  11.         }



Bueno para controlar los 15 minutos utilizaremos un timmer que agregaremos al formulario 1 y setearemos que cada 1 minuto suba 1 punto el contador al llegar a 15 minutos sin actividad cerraríamos el programa.

En el form1 load declaramos que el timmer llamara a la función TimerEventProcessor cada 1 minuto:
Código: C#
  1.             timer1.Interval = 60000;
  2.             timer1.Tick += new EventHandler(TimerEventProcessor);
  3.             timer1.Start();

Y en la función nuestro contador aumentará de 1 en 1 y si llega a 15 cerrará el programa:

Código: C#
  1. private void TimerEventProcessor(object sender, EventArgs e)
  2.         {
  3.             _counter++;
  4.            
  5.             if (_counter == 15)
  6.             {
  7.                
  8.                 Environment.Exit(1);
  9.                 this.Close();
  10.             }
  11.         }

Cabe destacar que la variable counter es un static int declarado como dato miembro de form1.

Código: C#
  1. static int _counter;

Bueno todo muy lindo, pero ahora faltaría que cada vez que se presione una tecla o se haga click en algun control de cualquiera de los formularios la variable counter vuelva a 0 así vuelven a contarse los 15 minutos desde la última vez que hubo actividad del usuario.

Para eso debemos delegar a los controles la función que vuelve el contador a 0.

Código: C#
  1. public static void mdiPrincipal_Click(object sender, EventArgs e)
  2. {
  3.             _counter = 0;
  4.             MessageBox.Show("Click or keypressed");
  5. }

Como estamos testeando le puse a la función un messagebox que me avisa que hubo un click o key pressed.
Pero todavía nos falta delegarle dicha función, a los eventos click y keypress de todos los controles de los formularios.
Sin lugar a dudas hacerlo 1 por 1 sería una solución horrible.
Para delegar la función a los eventos keypressed y click podemos usar un código como el siguiente:

Código: C#
  1. foreach (Control control in Controls)
  2. {
  3.  
  4.         control.Click += new System.EventHandler(mdiPrincipal_Click);
  5.         control.KeyDown += new KeyEventHandler(mdiPrincipal_Click);
  6. }

Pero el código tiene un problema, cuando el control con el que se encuentra el foreach es un groupBox, este tiene
anidado los controles dentro de el, por lo que se saltiaría todos los controles dentro de groupboxs.
Para solucionarlo podemos decir que si es un group box, recorra los elementos que hay dentro de este último 1 por 1 como lo hace con los de los formularios. El siguiente código funcionaría en caso de que haya como máximo 2 group box (anidados) o cualquier cantidad sin anidar en el formulario:

Código: C#
  1. foreach (Control control in Controls)
  2.             {
  3.                 if (control is GroupBox)
  4.                 {
  5.                     foreach (Control controlinside in control.Controls)
  6.                     {
  7.                         if (controlinside is GroupBox)
  8.                         {
  9.                             foreach (Control controlinside2 in controlinside.Controls)
  10.                             {
  11.                                 controlinside2.Click += new System.EventHandler(mdiPrincipal_Click);
  12.                                 controlinside2.KeyDown += new KeyEventHandler(mdiPrincipal_Click);
  13.                             }
  14.                         }
  15.                         else
  16.                         {
  17.  
  18.                             controlinside.Click += new System.EventHandler(mdiPrincipal.mdiPrincipal_Click);
  19.                             controlinside.KeyDown += new KeyEventHandler(mdiPrincipal.mdiPrincipal_Click);
  20.                         }
  21.                     }
  22.                 }
  23.                 else
  24.                 {
  25.  
  26.                     control.Click += new System.EventHandler(mdiPrincipal.mdiPrincipal_Click);
  27.                     control.KeyDown += new KeyEventHandler(mdiPrincipal.mdiPrincipal_Click);
  28.                 }
  29.             }

Pero que pasaría si hubiera más de 2 group box no podemos hardcodear todo así. Debemos hacerlo de un modo más prolijo.
Para eso creé una función que utiliza recursividad con los group box volviendo a llamarse una y otra vez siempre y cuando encuentre otro group box anidado.
En conclusión creé 2 funciones la principal (AddEvents) que "si es un group box llama a la recursiva" y si no agrega los eventos a cada control, y la recursiva que se encarga de los group box y group box anidados en otros group box.

Código: C#
  1. public static void AddEvents(Form form){
  2.              
  3.             foreach (Control control in form.Controls)
  4.             {
  5.                
  6.                 if(recursiva(control)){
  7.  
  8.                 }
  9.                 else
  10.                 {
  11.  
  12.                     control.Click += new System.EventHandler(mdiPrincipal_Click);
  13.                     control.KeyDown += new KeyEventHandler(mdiPrincipal_Click);
  14.                 }
  15.             }
  16.        }
  17.  
  18.  
  19.         public static bool recursiva(Control control){
  20.  
  21.              if (control is GroupBox)
  22.              {  
  23.                    foreach (Control controlinside in control.Controls)
  24.                    {
  25.                        if (recursiva(controlinside))
  26.                         {
  27.  
  28.  
  29.                          }else{
  30.  
  31.                            controlinside.Click += new System.EventHandler(mdiPrincipal_Click);
  32.                            controlinside.KeyDown += new KeyEventHandler(mdiPrincipal_Click);
  33.                         }
  34.                    }
  35.  
  36.                     return true;
  37.  
  38.               }else{
  39.  
  40.                         return false;
  41.  
  42.                   }
  43.  
  44.         }

Por último, nos queda en todos los form loads, llamar a:
Código: C#
  1. AddEvents(this);

Como podrán ver para testearlo en un formulario anidé muchos groupbox con controles dentro:



Más tarde les dejo el proyecto para descargar que ahora no estoy en casa.

Autor: Solid Water (Matías).

PD: Seguramente podría hacerse de algún modo con hooking o capturar algún evento más, pero bueno todo depende de para que queramos usarlo, podría ser mejor o peor.

Saludos,
« Última modificación: Octubre 11, 2018, 05:01:58 pm por solid water »

Desconectado 79137913

  • *
  • Co Admin
  • Mensajes: 636
  • Actividad:
    6.67%
  • Reputación 11
  • 4 Esquinas
    • Ver Perfil
    • Doors.Party
    • Email
  • Skype: fg_mdq@hotmail.com
« Respuesta #1 en: Noviembre 16, 2018, 08:56:10 am »
HOLA!!!

Admiro tu creatividad, pero con un 10% de ese codigo lo haces XD

Tenes que usar un invoke sobre "getlastuserinput" y listo te dara la ultima interaccion del usuario.

Te dejo documentacion con ejemplos y todo:
http://www.pinvoke.net/default.aspx/user32.getlastinputinfo

GRACIAS POR LEER!!!
"Algunos creen que soy un bot, puede que tengan razon"
"Como no se puede igualar a Dios, ya he decidido que hacer, ¡SUPERARLO!"
"La peor de las ignorancias es no saber corregirlas"

*Shadow Scout Team*                                                   DOORS.PARTY

Conectado solid water

  • *
  • Underc0der
  • Mensajes: 55
  • Actividad:
    23.33%
  • Reputación 1
    • Ver Perfil
« Respuesta #2 en: Noviembre 16, 2018, 09:20:21 am »
Gracias por la información, cuando busqué una solución a eso no la encontré por eso hice todo eso.

Sin embargo al parecer esa función que mencionas devuelve la última acción sobre el sistema y no sobre la aplicación, y hay que agregar más código para comparar con la aplicación incluso un timmer:

https://stackoverflow.com/questions/3289438/wpf-application-idle-time

Saludos,
« Última modificación: Noviembre 16, 2018, 11:21:35 am por solid water »

Conectado solid water

  • *
  • Underc0der
  • Mensajes: 55
  • Actividad:
    23.33%
  • Reputación 1
    • Ver Perfil
« Respuesta #3 en: Noviembre 16, 2018, 11:20:34 am »
Mira Lo hice utilizando esa función:

User32Interop.cs
Código: C#
  1. using System;
  2. using System.Text;
  3. //For use DLL import:
  4. using System.Runtime.InteropServices;
  5.  
  6. namespace WindowsFormsApplication2
  7. {
  8.  /*This class only tells me when the system has been idle, not the application.
  9.  If the user clicks into Word and works there for an hour,
  10.  I still want a timeout. To handle this case, I simply will remember when my application
  11.  loses focus by overriding the OnDeactivated and OnActivated methods
  12.  on the application object.
  13.   */
  14.     public static class User32Interop
  15.     {
  16.         public static TimeSpan GetLastInput()
  17.         {
  18.             var plii = new LASTINPUTINFO();
  19.             plii.cbSize = (uint)Marshal.SizeOf(plii);
  20.             if (GetLastInputInfo(ref plii))
  21.                 return TimeSpan.FromMilliseconds(Environment.TickCount - plii.dwTime);
  22.             else
  23.                 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
  24.         }
  25.  
  26.         [DllImport("user32.dll", SetLastError = true)]
  27.         static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
  28.         struct LASTINPUTINFO
  29.         {
  30.             public uint cbSize;
  31.             public uint dwTime;
  32.         }
  33.     }
  34. }
  35.  

Form1.cs
Código: C#
  1. using System;
  2. using System.ComponentModel;
  3. using System.Drawing;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Windows.Forms;
  7.  
  8. namespace WindowsFormsApplication2
  9. {
  10.     public partial class Form1 : Form
  11.     {
  12.         //Variables miembro que utilizaremos:
  13.         public static DateTime? _lostFocusTime;
  14.         int _counter = 0;
  15.  
  16.         public Form1()
  17.         {
  18.             InitializeComponent();
  19.         }
  20.  
  21.  
  22.         //Si respuesta llega a 15 (minutos) cerraremos el programa
  23.         public void Timmer_Function(object sender, EventArgs e)
  24.         {
  25.             bool respuesta = IsIdle;
  26.             //MessageBox.Show(IsIdle.ToString());
  27.             if (respuesta)
  28.             {
  29.                 _counter++;
  30.  
  31.             }
  32.             else
  33.             {
  34.                 _counter = 0;
  35.             }
  36.  
  37.             if (_counter == 15)
  38.             {
  39.                 Environment.Exit(1);
  40.                 this.Close();
  41.             }
  42.  
  43.         }
  44.  
  45.  
  46.         //LLamaremos al timmer cada 1 minuto:
  47.         private void Form1_Load(object sender, EventArgs e)
  48.         {
  49.             timer1.Interval = 60000;
  50.             timer1.Tick += new EventHandler(Timmer_Function);
  51.             timer1.Start();
  52.         }
  53.  
  54.      
  55.  
  56.         //En todos los forms debemos sobreescribir estas 2 funciones:
  57.         //Para saber cuando nuestra aplicación pierde el foco.
  58.         override protected void OnDeactivate(EventArgs e)
  59.         {
  60.             _lostFocusTime = DateTime.Now;
  61.             base.OnDeactivate(e);
  62.         }
  63.  
  64.         protected override void OnActivated(EventArgs e)
  65.         {
  66.             _lostFocusTime = null;
  67.             base.OnActivated(e);
  68.         }
  69.  
  70.  
  71.         /*The IsIdle routine was added to the application object.
  72.          * It handles the global case where the app has focus but nothing happened
  73.          * (IsMachineIdle) and the specific case where the application lost focus while
  74.          * the user is doing other stuff (isAppIdle ):*/
  75.         public bool IsIdle
  76.         {
  77.             get
  78.             {
  79.                 TimeSpan activityThreshold = TimeSpan.FromMinutes(1);
  80.                 TimeSpan machineIdle = User32Interop.GetLastInput();
  81.              
  82.                 TimeSpan? appIdle = _lostFocusTime == null ? null :
  83.                                    (TimeSpan?)DateTime.Now.Subtract
  84.                                              (_lostFocusTime.Value);
  85.  
  86.                 bool isMachineIdle = machineIdle > activityThreshold;
  87.                 bool isAppIdle = appIdle != null && appIdle > activityThreshold;
  88.                 return isMachineIdle || isAppIdle;
  89.             }
  90.         }
  91.  
  92.         private void button1_Click(object sender, EventArgs e)
  93.         {
  94.             Form2 form2 = new Form2();
  95.             form2.Show();
  96.         }
  97.  
  98.         private void button2_Click(object sender, EventArgs e)
  99.         {
  100.             Form3 form3 = new Form3();
  101.             form3.Show();
  102.         }
  103.     }
  104. }
  105.  

Y en todos los otros forms sobreescribir las funciones así:

Código: C#
  1. override protected void OnDeactivate(EventArgs e)
  2.         {
  3.             Form1._lostFocusTime = DateTime.Now;
  4.             base.OnDeactivate(e);
  5.         }
  6.  
  7.         protected override void OnActivated(EventArgs e)
  8.         {
  9.             Form1._lostFocusTime = null;
  10.             base.OnActivated(e);
  11.         }

No sé si sean menos líneas, de hecho tampoco que sea menor trabajo a nivel procesador.
Pero me agrada tener otra forma de hacerlo.
Luego lo escribo de una forma más detallada.

Saludos,
« Última modificación: Noviembre 16, 2018, 04:47:04 pm por solid water »

 

¿Te gustó el post? COMPARTILO!



[C#] Source de un programa para completar ofertas de offerwalls.

Iniciado por ragaza

Respuestas: 0
Vistas: 645
Último mensaje Mayo 19, 2018, 03:24:39 pm
por ragaza