Etiqueta: Visual Studio

Posts relacionados con el entorno de desarrollo (IDE) de Visual Studio

  • El código de VB y C# de: Mostrar todos los formularios de una aplicación

    Pues eso… lo prometido es deuda y aquí tienes el código completo de cómo mostrar todos los formularios de una aplicación (ensamblado) y cómo mostrar un formulario en el segundo monitor.

    Al final de todo te pongo el enlace para descargar los proyectos tanto de Visual Basic como de C#.

    ¡Que te aproveche! Smile

    Estas son las capturas (en funcionamiento) de Visual Basic y C#:

    mostrar_formularios_01
    Figura
    1, la aplicación de Visual Basic

    mostrar_formularios_02
    Figura 2: La aplicación de C#

    El código completo de Visual Basic:

    '------------------------------------------------------------------------------
    ' Mostrar todos los formularios de una aplicación (ensamblado)      (16/Dic/18)
    ' Estén abiertos o no
    ' Usando reflection
    '
    ' Este código está basado en el artículo que publiqué el 13/Sep/2004 en:
    ' http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
    ' 
    '
    ' (c) Guillermo (elGuille) Som, 2018
    '------------------------------------------------------------------------------
    Option Strict On
    Option Infer On
    
    Imports Microsoft.VisualBasic
    Imports vb = Microsoft.VisualBasic
    Imports System
    Imports System.Text
    Imports System.Collections.Generic
    Imports System.Windows.Forms
    Imports System.Drawing
    Imports System.Diagnostics
    Imports System.Linq
    
    Public Class Form1
        ''' <summary>
        ''' Para recorrer todos los formularios de un ensamblado
        ''' estén abiertos o no
        ''' Basado en el código del elGuille.info:
        ''' http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
        ''' </summary>
        Private ass As System.Reflection.Assembly
    
        ''' <summary>
        ''' Mostrar todos los formularios de la aplicación actual,
        ''' estén o no en memoria, usando reflection.
        ''' Además de los formularios, mostrará:
        ''' My.Application (solo en VB)
        ''' Y todos los nombres esmpiezan con el espacio de nombres
        ''' ESPACIO_DE_NOMBRES.NombreForm
        ''' </summary>
        Private Sub mostrarForms()
            ' llena una colección con los formularios de esta aplicación
            ' estén o no en memoria.
            ' Muestra el resultado en un listbox
    
            lbForms.Items.Clear()
    
            For Each t As Type In ass.GetTypes()
                Dim nombreTipo = t.BaseType.Name
                ' También tendrá My.Application: (solo en VB)
                ' <espacio de nombres>.My.MyApplication
                If nombreTipo.ToLower().Contains("form") Then
                    lbForms.Items.Add(t.FullName)
                End If
            Next
        End Sub
    
        ''' <summary>
        ''' Muestra el formulario indicado en el argumento,
        ''' este debe ser con el espacio de nombres completo
        ''' 
        ''' Si es el actual no lo muestra.
        ''' Si da error o no es un formulario se avisa.
        ''' </summary>
        Private Sub mostrarFormulario(s As String)
            ' creamos un tipo a partir del nombre
            Dim t As Type = ass.GetType(s)
            ' instanciamos un nuevo objeto en la memoria
            Dim o As Object
    
            ' por si hemos seleccionado algo que no es una clase
            Try
                o = Activator.CreateInstance(t)
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Mostrar formularios")
                Exit Sub
            End Try
    
            ' si no es un formulario, mostramos un aviso y salimos
            If Not (TypeOf o Is Form) Then
                MessageBox.Show(s & ", no es un formulario", "Mostrar formularios")
                Exit Sub
            End If
    
            ' convertimos el objeto en un formulario
            ' como sabemos que si llega aquí es un formulario,
            ' usamos TryCast que hace menos trabajo que CType o DirectCast.
            Dim f As Form = TryCast(o, Form)
            If f Is Nothing Then
                MessageBox.Show(s & ", parece que no es un formulario",
                                "Mostrar formularios")
                Exit Sub
            End If
    
            ' si el nombre es el de este formulario,
            ' lo cerramos y salimos.
            If f.Name = Me.Name Then
                ' no volver a crear este formulario
                'f.Close()
                Me.BringToFront()
    
                MessageBox.Show(s & " es el formulario de inicio.",
                                "Mostrar formularios")
    
                Return
            End If
    
            ' mostramos el formulario.
            f.Show()
    
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ' Mostrar esta ventana en el monitor secundario
            MostrarEnVentana2(Me)
    
            ' mostrar los formularios de esta aplicación
            ass = System.Reflection.Assembly.GetExecutingAssembly()
            mostrarForms()
        End Sub
    
        Private Sub btnAbrir_Click(sender As Object, e As EventArgs) Handles btnAbrir.Click
            ' Por si no hay un nombre indicado
            If String.IsNullOrWhiteSpace(txtForm.Text) Then Return
    
            ' Abrir el formulario indicado en el textbox
            mostrarFormulario(txtForm.Text)
        End Sub
    
        Private Sub btnSalir_Click(sender As Object, e As EventArgs) Handles btnSalir.Click
            Me.Close()
        End Sub
    
        Private Sub lbForms_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lbForms.SelectedIndexChanged
            txtForm.Text = lbForms.SelectedItem.ToString
        End Sub
    
        ''' <summary>
        ''' Mostrar el formulario indicado en la segunda pantalla
        ''' Código adaptado de la respuesta 42 de:
        ''' https://stackoverflow.com/questions/2561104/how-do-i-ensure-a-form-displays-on-the-additional-monitor-in-a-dual-monitor-sc
        ''' </summary>
        Public Shared Sub MostrarEnVentana2(frm As Form)
            Dim myScreen = Screen.PrimaryScreen
            Dim otherScreen = If(Screen.AllScreens.FirstOrDefault(
                                    Function(s) Not s.Equals(myScreen)), myScreen)
    
            ' Si queremos indicar dónde mostrarlo
            ' podemos cambiar los valores de Left y Top
            'frm.Left = otherScreen.WorkingArea.Left + 12
            'frm.Top = otherScreen.WorkingArea.Top + 12
    
        End Sub
    
        Private Sub btnForm2_Click(sender As Object, e As EventArgs) Handles btnForm2.Click
            Dim f As New Form2
            f.Show()
        End Sub
    
        Private Sub btnForm3_Click(sender As Object, e As EventArgs) Handles btnForm3.Click
            Dim f As New Form3
            f.Show()
        End Sub
    End Class
    

    El código completo de C#:

    // ----------------------------------------------------------------------------
    // Mostrar todos los formularios de una aplicación (ensamblado)     (16/Dic/18)
    // Estén abiertos o no
    // Usando reflection
    // 
    // Este código está basado en el artículo que publiqué el 13/Sep/2004 en:
    // http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
    // 
    // 
    // (c) Guillermo (elGuille) Som, 2018
    // ----------------------------------------------------------------------------
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Security;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Drawing;
    
    namespace Mostrar_Nombres_Formularios_cs
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// Para recorrer todos los formularios de un ensamblado
            /// estén abiertos o no
            /// Basado en el código del elGuille.info:
            /// http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
            /// </summary>
            private System.Reflection.Assembly ass;
    
            /// <summary>
            /// Mostrar todos los formularios de la aplicación actual,
            /// estén o no en memoria, usando reflection.
            /// Además de los formularios, mostrará:
            /// My.Application (solo en VB)
            /// Y todos los nombres esmpiezan con el espacio de nombres
            /// ESPACIO_DE_NOMBRES.NombreForm
            /// </summary>
            private void mostrarForms()
            {
                // llena una colección con los formularios de esta aplicación
                // estén o no en memoria.
                // Muestra el resultado en un listbox
    
                lbForms.Items.Clear();
    
                foreach (Type t in ass.GetTypes())
                {
                    var nombreTipo = t.BaseType.Name;
                    // También tendrá My.Application: (solo en VB)
                    // <espacio de nombres>.My.MyApplication
                    if (nombreTipo.ToLower().Contains("form"))
                        lbForms.Items.Add(t.FullName);
                }
            }
    
            /// <summary>
            /// Muestra el formulario indicado en el argumento,
            /// este debe ser con el espacio de nombres completo
            /// 
            /// Si es el actual no lo muestra.
            /// Si da error o no es un formulario se avisa.
            /// </summary>
            private void mostrarFormulario(string s)
            {
                // creamos un tipo a partir del nombre
                Type t = ass.GetType(s);
                // instanciamos un nuevo objeto en la memoria
                object o;
    
                // por si hemos seleccionado algo que no es una clase
                try
                {
                    o = Activator.CreateInstance(t);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, 
                        "Mostrar formularios");
                    return;
                }
    
                // si no es un formulario, mostramos un aviso y salimos
                if (!(o is Form))
                {
                    MessageBox.Show(s + ", no es un formulario", 
                        "Mostrar formularios");
                    return;
                }
    
                // convertimos el objeto en un formulario
                Form f = o as Form;
                if (f == null)
                {
                    MessageBox.Show(s + ", parece que no es un formulario", 
                        "Mostrar formularios");
                    return;
                }
    
                // si el nombre es el de este formulario,
                // lo cerramos y salimos.
                if (f.Name == this.Name)
                {
                    // no volver a crear este formulario
                    // f.Close()
                    this.BringToFront();
    
                    MessageBox.Show(s + " es el formulario de inicio.", 
                        "Mostrar formularios");
    
                    return;
                }
    
                // mostramos el formulario.
                f.Show();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                // Mostrar esta ventana en el monitor secundario
                MostrarEnVentana2(this);
    
                // mostrar los formularios de esta aplicación
                ass = System.Reflection.Assembly.GetExecutingAssembly();
                mostrarForms();
            }
    
            private void btnAbrir_Click(object sender, EventArgs e)
            {
                //' Por si no hay un nombre indicado
                if (String.IsNullOrWhiteSpace(txtForm.Text))
                    return;
    
                //' Abrir el formulario indicado en el textbox
                mostrarFormulario(txtForm.Text);
    
            }
    
            private void btnSalir_Click(object sender, EventArgs e)
            {
                this.Close();
            }
    
            private void lbForms_SelectedIndexChanged(object sender, EventArgs e)
            {
                txtForm.Text = lbForms.SelectedItem.ToString();
            }
    
            /// <summary>
            /// Mostrar el formulario indicado en la segunda pantalla
            /// Código adaptado de la respuesta 42 de:
            /// https://stackoverflow.com/questions/2561104/how-do-i-ensure-a-form-displays-on-the-additional-monitor-in-a-dual-monitor-sc
            /// </summary>
            public static void MostrarEnVentana2(Form frm)
            {
                var myScreen = Screen.PrimaryScreen;
                var otherScreen = Screen.AllScreens.FirstOrDefault(s => !s.Equals(myScreen)) 
                                        ?? myScreen;
    
                //' Si queremos indicar dónde mostrarlo
                //' podemos cambiar los valores de Left y Top
                //'frm.Left = otherScreen.WorkingArea.Left + 12
                //'frm.Top = otherScreen.WorkingArea.Top + 12
            }
        }
    }
    
    

    El enlace para descargar el código completo (tanto de Visual Basic como de C#):

    Archivo: Mostar_Nombres_Formularios_20181216_2346.zip (204 KB)
    MD5 Checksum: DA004D15933BD82355684AC0F20680B1

    Nos vemos.
    Guillermo

  • Mostrar los formularios de nuestra aplicación (aunque no estén abiertos)

    Pues eso… que estoy haciendo una aplicación para convertir los Windows Forms en WPF (Xaml) y tengo que recorrer todos los formularios de la aplicación para ir convirtiéndolos y he buscado en Internet cómo hacer eso y… ¡no lo he encontrado!

    Mostrar todos los formularios de un ensamblado (aplicación) estén o no abiertos

    Así que… me dije que yo tenía algún código haciendo Reflection para mostrar una clase y va y resulta que en ese ejemplo tenía la respuesta que buscaba… ¡lo que no esté en el sitio del Guille! Winking smile

    Concretamente en esta página:
    Las clases de un ensamblado usando Reflection (por ejemplo para saber los formularios de nuestra aplicación)

    Y para que no me vuelva a ocurrir lo mismo en un futuro (ese artículo lo escribí el 13 de septiembre de 2004… ¡casi ná! Smile with tongue out) me he decidido a escribir este nuevo…

    No te voy a explicar mucho (eso ya lo hice en ese artículo) solo te mostraré el código de ejemplo (y te pondré el proyecto para que lo puedas descargar).
    Y como siempre el código tanto para Visual Basic como para C#.

    Primero te muestro el código que recorre los formularios del ensamblado o aplicación (que es lo que realmente te puede interesar) y después te mostraré el resto.

    El código para Visual Basic:

        ''' <summary>
        ''' Para recorrer todos los formularios de un ensamblado
        ''' estén abiertos o no
        ''' Basado en el código del elGuille.info:
        ''' http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
        ''' </summary>
        Private ass As System.Reflection.Assembly
    
        ''' <summary>
        ''' Mostrar todos los formularios de la aplicación actual,
        ''' estén o no en memoria, usando reflection.
        ''' Además de los formularios, mostrará:
        ''' My.Application (solo en VB)
        ''' Y todos los nombres esmpiezan con el espacio de nombres
        ''' ESPACIO_DE_NOMBRES.NombreForm
        ''' </summary>
        Private Sub mostrarForms()
            ' llena una colección con los formularios de esta aplicación
            ' estén o no en memoria.
            ' Muestra el resultado en un listbox
    
            lbForms.Items.Clear()
    
            For Each t As Type In ass.GetTypes()
                Dim nombreTipo = t.BaseType.Name
                ' También tendrá My.Application: (solo en VB)
                ' <espacio de nombres>.My.MyApplication
                If nombreTipo.ToLower().Contains("form") Then
                    lbForms.Items.Add(t.FullName)
                End If
            Next
        End Sub
    

    El código para C#:

            /// <summary>
            /// Para recorrer todos los formularios de un ensamblado
            /// estén abiertos o no
            /// Basado en el código del elGuille.info:
            /// http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
            /// </summary>
            private System.Reflection.Assembly ass;
    
            /// <summary>
            /// Mostrar todos los formularios de la aplicación actual,
            /// estén o no en memoria, usando reflection.
            /// Además de los formularios, mostrará:
            /// My.Application (solo en VB)
            /// Y todos los nombres esmpiezan con el espacio de nombres
            /// ESPACIO_DE_NOMBRES.NombreForm
            /// </summary>
            private void mostrarForms()
            {
                // llena una colección con los formularios de esta aplicación
                // estén o no en memoria.
                // Muestra el resultado en un listbox
                lbForms.Items.Clear();
    
                foreach (Type t in ass.GetTypes())
                {
                    var nombreTipo = t.BaseType.Name;
                    // También tendrá My.Application: (solo en VB)
                    // <espacio de nombres>.My.MyApplication
                    if (nombreTipo.ToLower().Contains("form"))
                        lbForms.Items.Add(t.FullName);
                }
            }
    

    Solo comentarte que en el código de Visual Basic he añadido 2 botones extras para mostrar los formularios Form2 y Form3 respectivamente. Esto solo es para probar que se pueden mostrar de las dos formas Smile
    Pero en cualquier caso, siempre se muestra un nuevo formulario, esté o no abierto.

    En la figura 1 puedes ver la aplicación en funcionamiento, mostrando una lista de los formularios de la aplicación.

    mostrar_formularios_01.pg
    Figura 1. La aplicación en ejecución mostrando los formularios del ensamblado (aplicación)

    Si te fijas en la figura 1, se muestra también «My.MyApplication» que no es un formulario, pero el tipo «base» es Form, y por eso se muestra. Esto no ocurre en C#, ya que C# no tiene la clase My.

    Nota:
    El código de Visual Basic lo he convertido con una utilidad que hay en la web:
    Telerik Code Converter

    Un extra: Mostrar un formulario en el segundo monitor

    Esto ya lo publicaré otro día (realmente era lo que iba a publicar hoy junto con una explicación de los problemas de nuestro código de Windows Forms y Visual Studio y los monitores HDPI), y es que en el código incluyo cómo hacer que un formulario se muestre en el segundo monitor.

    Actualmente estoy usando un portátil con la resolución 1920×1080 y usando el tamaño del texto (etc.) al 100% y un segundo monitor con 3840×2160 y el tamaño del texto, etc. al 150%.

    Como te explicaré en otra ocasión, yo uso el Visual Studio abriéndolo desde la pantalla principal (la del portátil al 100%), pero después muevo el IDE de Visual Studio al segundo monitor, por tanto cuando ejecuto el código, lo que se muestra lo hace siempre en la pantalla del portátil, por eso busqué cómo hacer que se muestre un formulario en el segundo monitor, y ¡lo encontré!, eso sí, en código para C#, concretamente en una respuesta en el sitio de StackOverflow en un hilo con la siguiente pregunta: How do I ensure a form displays on the “additional” monitor in a dual monitor scenario?
    La respuesta que me sirvió fue la 42, con este código:

    var myScreen = Screen.FromControl(originalForm);
    var otherScreen = Screen.AllScreens.FirstOrDefault(s => !s.Equals(myScreen)) 
                   ?? myScreen;
    otherForm.Left = otherScreen.WorkingArea.Left + 120;
    otherForm.Top = otherScreen.WorkingArea.Top + 120;

    Que traducido a Visual Basic sería algo así:

    Dim myScreen = Screen.PrimaryScreen
    Dim otherScreen = If(Screen.AllScreens.FirstOrDefault(
                            Function(s) Not s.Equals(myScreen)), myScreen)
    

    Y ¡esto es todo amigo!
    Espero , como siempre, que te sea de utilidad y si no… al menos a mí dentro de 14 años me puede ser útil… Winking smile

    Nos vemos.
    Guillermo

    P.S.
    El código de ejemplo completo y el enlace para la descarga de los proyectos (hechos con Visual Studio 2017 y .NET 4.7.2, ninguno de estos requisitos son necesarios para el código) te los muestro en otra página.

    El código de VB y C# de: Mostrar todos los formularios de una aplicación

    Winking smile

  • Error al guardar datos decimales – El código para C#

    Aquí tienes el código para C# de la aplicación de ejemplo del artículo Error al guardar datos decimales: El valor del parámetro ‘xxx’ está fuera del intervalo.

    Nota:
    Si buscabas el código para Visual Basic está en este otro enlace:
    Error al guardar datos decimales – El código para Visual Basic.

    Cuando lo tenga publicado, te pondré el enlace para descargar la solución de Visual Studio 2017 tanto para VB como para C#.

    El código de C#

    //-----------------------------------------------------------------------------
    //  Ejemplo para el error de asignar a decimal(4,4)                 (07/Dic/18)
    //
    // (c) Guillermo (elGuille) Som, 2018
    //-----------------------------------------------------------------------------
    
    using System;
    using System.Data.SqlClient;
    using System.Text;
    using System.Windows.Forms;
    
    namespace SQL_Error_decimal_cs
    {
        public partial class Form1 : Form
        {
    
    
            //--------------------------------------------------------------------------
            // Los campos para acceder a la base de datos
            //--------------------------------------------------------------------------
    
            /// <summary>
            /// El usuario para acceder a la base de datos de SQL Server.<br />
            /// Si es una cadena vacía se usará la seguridad integrada de Windows.
            /// </summary>
            private string userDb = "UsuarioErrDec";
    
            /// <summary>
            /// El password del usuario que accede a la base de datos de SQL Server
            /// </summary>
            private string passwordDB = "123456";
    
            /// <summary>
            /// El servidor donde está la base de datos.<br />
            /// Normalmente será .\SQLEXPRESS o (local)
            /// </summary>
            private string serverName = @".\SQLEXPRESS";
            /// <summary>
            /// El nombre de la base de datos de SQL Server
            /// </summary>
            private string databaseName = "ErrorDecimal";
    
            /// <summary>
            /// Devuelve la cadena de conexión a la base de datos de SQL Server<br />
            /// Si el usuario es una cadena vacía, se usará la seguridad integrada de Windows
            /// </summary>
            private string ConnectionString {
                get {
                    var sb = new SqlConnectionStringBuilder();
                    sb.DataSource = serverName;
                    sb.InitialCatalog = databaseName;
                    if (String.IsNullOrWhiteSpace(userDb))
                    {
                        sb.IntegratedSecurity = true;
                    } else
                    {
                        sb.UserID = userDb;
                        sb.Password = passwordDB;
                    }
                    return sb.ConnectionString;
                }
            }
    
    
    
            //--------------------------------------------------------------------------
            // Añadir un valor a las tablas
            //--------------------------------------------------------------------------
            private (bool hayError, string msg) AñadirMiTabla1(decimal valor)
            {
                var sel = "INSERT INTO MiTabla1 (Decimal_4_4) " +
                          "VALUES (@Decimal_4_4)";
    
                var retVal = (hayError: false, msg: "");
    
                var sCon = ConnectionString;
    
                using (SqlConnection con = new SqlConnection(sCon))
                {
                    var cmd = new SqlCommand(sel, con);
                    cmd.Parameters.AddWithValue("@Decimal_4_4", valor);
    
                    con.Open();
    
                    try
                    {
                        var ret = Convert.ToInt32(cmd.ExecuteNonQuery());
    
                        retVal.hayError = (ret < 1);
                        retVal.msg = "Todo OK. cmd.ExecuteNonQuery() = " + ret.ToString();
                    }
                    catch (Exception ex)
                    {
                        retVal.msg = ex.Message;
                        retVal.hayError = true;
                    }
                    con.Close();
                }
                return retVal;
            }
    
            private (bool hayError, string msg) AñadirMiTabla2(decimal[] valores)
            {
                var sel = "INSERT INTO MiTabla2 (Decimal_6_4, Decimal_18_6) " +
                          "VALUES (@Decimal_6_4, @Decimal_18_6)";
    
                var retVal = (hayError:false, msg:"");
                
                var sCon = ConnectionString;
    
                using (SqlConnection con = new SqlConnection(sCon))
                {
                    SqlCommand cmd = new SqlCommand(sel, con);
                    cmd.Parameters.AddWithValue("@Decimal_6_4", valores[0]);
                    cmd.Parameters.AddWithValue("@Decimal_18_6", valores[1]);
    
                    con.Open();
    
                    try
                    {
                        var ret = Convert.ToInt32(cmd.ExecuteNonQuery());
    
                        retVal.hayError = (ret < 1);
                        retVal.msg = "Todo OK. cmd.ExecuteNonQuery() = " + ret.ToString();
                    }
                    catch (Exception ex)
                    {
                        retVal.msg = ex.Message;
                        retVal.hayError = true;
                    }
                    con.Close();
                }
                return retVal;
            }
    
    
    
            private string leerMiTabla(string tabla)
            {
                var sel = "SELECT * FROM " + tabla;
    
                var retVal = "";
    
                var sCon = ConnectionString;
    
                using (SqlConnection con = new SqlConnection(sCon))
                {
                    var cmd = new SqlCommand(sel, con);
    
                    con.Open();
    
                    try
                    {
                        var ret = cmd.ExecuteReader();
    
                        StringBuilder sb = new StringBuilder();
    
                        while (ret.Read())
                        {
                            sb.AppendLine(String.Format("{0} = {1}", ret.GetName(0), ret[0]));
    
                            if (ret.FieldCount > 1)
                                sb.AppendLine(String.Format("{0} = {1}", ret.GetName(1), ret[1]));
                        };
                        retVal = sb.ToString();
                    }
                    catch (Exception ex)
                    {
                        retVal = "ERROR: " + ex.Message;
                    }
                    con.Close();
                };
                return retVal;
            }
    
            //--------------------------------------------------------------------------
            // Para aceptar la coma como decimal en las cajas numéricas
            //--------------------------------------------------------------------------
            /// <summary>
            /// El separador de decimales para los campos numéricos
            /// </summary>
            const string SeparadorDecimal = ",";
    
            /// <summary>
            /// Para indicar qué tecla "decimal" no se debe admitir
            /// </summary>
            const string NoSeparadorDecimal = ".";
    
            /// <summary>
            /// Comprobar si se aceptan las teclas en una caja de texto.
            /// En la pulsación de los controles numéricos
            /// aceptar solo los caracteres numéricos,
            /// el valor negativo, el separador de decimales
            /// y las teclas Intro, Delete, Back (borrar hacia atrás)
            ///
            /// Es raro, si teclasAceptadas es: ",-1234567890" también acepta el punto
            /// </summary>
            private char AceptarTeclas(KeyPressEventArgs e, string teclasAceptadas)
            {
                char c = e.KeyChar;
    
                if (c == Convert.ToChar(Keys.Return))
                {
                    // con esto hacemos que se ignore la pulsación
                    e.Handled = true;
                    // se manda al siguiente control
                    SendKeys.Send("{TAB}");
                }
                else if (c == Convert.ToChar(NoSeparadorDecimal))
                {
                    e.KeyChar = Convert.ToChar(SeparadorDecimal);
                }
                else if (teclasAceptadas.Contains(c.ToString()))
                {
                    // no hacer nada, se aceptan
                }
                else if (c == Convert.ToChar(Keys.Delete) || c == Convert.ToChar(Keys.Back))
                {
                    // no hacer nada, se aceptan
                }
                else
                {
                    e.Handled = true;
                }
                return c;
            }
    
    
    
            //--------------------------------------------------------------------------
            // Los métodos de evento del formulario
            //--------------------------------------------------------------------------
    
            private void btnCerrar_Click(object sender, EventArgs e)
            {
                this.Close();
            }
    
            private void btnAsignarTabla1_Click(object sender, EventArgs e)
            {
                var d = 0M;
                Decimal.TryParse(txtTabla1_campo1.Text, out d);
                var ret = AñadirMiTabla1(d);
    
                txtMensaje1.Text = "";
                if (ret.hayError)
                    txtMensaje1.Text = "ERROR\r\n";
                txtMensaje1.Text += ret.msg;
            }
    
            private void btnAsignarTabla2_Click(object sender, EventArgs e)
            {
                var valores = new decimal[12];
                var d = 0M;
    
                Decimal.TryParse(txtTabla2_campo1.Text, out d);
                valores[0] = d;
    
                d = 0M;
                Decimal.TryParse(txtTabla2_campo2.Text, out d);
                valores[1] = d;
    
                var ret = AñadirMiTabla2(valores);
    
                txtMensaje2.Text = "";
                if (ret.hayError)
                    txtMensaje2.Text = "ERROR\r\n";
                txtMensaje2.Text += ret.msg;
            }
    
            private void btnMostrar1_Click(object sender, EventArgs e)
            {
                // mostrar los datos de MiTabla1
                txtMensaje1.Text = leerMiTabla("MiTabla1");
            }
    
            private void btnMostrar2_Click(object sender, EventArgs e)
            {
                // mostrar los datos de MiTabla2
                txtMensaje2.Text = leerMiTabla("MiTabla2");
            }
    
            private void txt_KeyPress(object sender, KeyPressEventArgs e)
            {
                AceptarTeclas(e, SeparadorDecimal + "-1234567890");
            }
    
            public Form1()
            {
                InitializeComponent();
            }
    
    <
    
            private void Form1_KeyDown(object sender, KeyEventArgs e)
            {
                // detecta la pulsación de las teclas en el formulario
                // antes de mandarla a los controles
                // En el diseñador de formularios tienes que
                // asignar un valor True a la propiedad KeyPreview
    
                if (e.Modifiers == Keys.Control)
                {
    
    
                    if (e.KeyCode == Keys.C)
                    {
                        // copiar el texto
                        if (ActiveControl is TextBox)
                        {
                            //Dim texto = ActiveControl.Text
                            //Clipboard.SetText(texto)
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            txt.Copy();
    
                            e.Handled = true;
                        }
                    }
                    else if (e.KeyCode == Keys.V)
                    {
                        // pegar el texto
                        if (ActiveControl is TextBox)
                        {
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            txt.Paste();
    
                            e.Handled = true;
                        }
                    }
                    else if (e.KeyCode == Keys.X)
                    {
                        // cortar el texto
                        if (ActiveControl is TextBox)
                        {
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            txt.Cut();
    
                            e.Handled = true;
                        }
                    }
                    else if (e.KeyCode == Keys.Z)
                    {
                        //deshacer
                        if (ActiveControl is TextBox)
                        {
                            var txt = ActiveControl as TextBox;
                            if (txt == null) return;
                            if (txt.CanUndo)
                            {
                                txt.Undo();
                            }
                            e.Handled = true;
                        }
                    }
                }
            }
        }
    }
    

    Espero que te sea de utilidad.

    Nos vemos
    Guillermo