Skip to main content
Every management module in AppAdministrativa follows the same six-step pattern: a data model, a Page with a DataGrid, a modal dialog for add/edit operations, and registration in the main menu. Follow these steps to add a new module. This guide uses Equipos (equipment) as a concrete example.
1

Create the data model class

Declare a plain C# class for your entity inside the page’s code-behind file. All properties are strings unless the field is inherently boolean (like a connection state).
// Equipos.xaml.cs
public class EquipoItem
{
    public string ID          { get; set; } = string.Empty;
    public string Nombre      { get; set; } = string.Empty;
    public string Tipo        { get; set; } = string.Empty;
    public string Ubicacion   { get; set; } = string.Empty;
    public string Estado      { get; set; } = string.Empty;
}
Declare the model class in the same .cs file as the page that owns it. This keeps each module self-contained and mirrors the convention used by all existing modules.
2

Create the Page XAML

Add a new WPF Page to the project: right-click the project in Solution Explorer → Add → New Item → Page (WPF) → name it Equipos.xaml.Structure the page with a search filter TextBox at the top, a DataGrid bound to your collection, and Add / Edit / Delete buttons:
<!-- Equipos.xaml -->
<Page x:Class="AppAdministrativa.Equipos"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Equipos">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <!-- Search filter -->
    <TextBox x:Name="TxtBuscar"
             Grid.Row="0"
             PlaceholderText="Buscar equipo..."
             TextChanged="TxtBuscar_TextChanged" />

    <!-- Data grid -->
    <DataGrid x:Name="DataGridEquipos"
              Grid.Row="1"
              AutoGenerateColumns="False"
              IsReadOnly="True">
      <DataGrid.Columns>
        <DataGridTextColumn Header="ID"        Binding="{Binding ID}" />
        <DataGridTextColumn Header="Nombre"    Binding="{Binding Nombre}" />
        <DataGridTextColumn Header="Tipo"      Binding="{Binding Tipo}" />
        <DataGridTextColumn Header="Ubicación" Binding="{Binding Ubicacion}" />
        <DataGridTextColumn Header="Estado"    Binding="{Binding Estado}" />
      </DataGrid.Columns>
    </DataGrid>

    <!-- Action buttons -->
    <StackPanel Grid.Row="2" Orientation="Horizontal">
      <Button x:Name="BtnAgregar"  Content="Agregar"  Click="BtnAgregar_Click" />
      <Button x:Name="BtnEditar"   Content="Editar"   Click="BtnEditar_Click" />
      <Button x:Name="BtnEliminar" Content="Eliminar" Click="BtnEliminar_Click" />
    </StackPanel>
  </Grid>
</Page>
3

Create the Page code-behind

Implement the page class with:
  • An ObservableCollection<EquipoItem> field.
  • A CargarDatosDePrueba() method called from the constructor.
  • Click handlers for Add, Edit, and Delete.
  • A TextChanged handler for the search filter.
// Equipos.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace AppAdministrativa
{
    public partial class Equipos : Page
    {
        public ObservableCollection<EquipoItem> ListaEquipos = new();

        public Equipos()
        {
            InitializeComponent();
            CargarDatosDePrueba();
            DataGridEquipos.ItemsSource = ListaEquipos;
        }

        private void CargarDatosDePrueba()
        {
            ListaEquipos.Add(new EquipoItem
            {
                ID        = "E001",
                Nombre    = "Proyector Epson",
                Tipo      = "Audiovisual",
                Ubicacion = "Aula A-101",
                Estado    = "Disponible"
            });
        }

        private void BtnAgregar_Click(object sender, RoutedEventArgs e)
        {
            var ventana = new AgregarEquipoWindow();
            if (ventana.ShowDialog() == true)
                ListaEquipos.Add(ventana.NuevoEquipo);
        }

        private void BtnEditar_Click(object sender, RoutedEventArgs e)
        {
            if (DataGridEquipos.SelectedItem is EquipoItem seleccionado)
            {
                var ventana = new AgregarEquipoWindow(seleccionado);
                if (ventana.ShowDialog() == true)
                    DataGridEquipos.Items.Refresh();
            }
            else
            {
                MessageBox.Show("Selecciona un equipo para editar.");
            }
        }

        private void BtnEliminar_Click(object sender, RoutedEventArgs e)
        {
            if (DataGridEquipos.SelectedItem is EquipoItem seleccionado)
            {
                if (MessageBox.Show($"¿Eliminar {seleccionado.Nombre}?", "Confirmar",
                    MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
                    ListaEquipos.Remove(seleccionado);
            }
        }

        private void TxtBuscar_TextChanged(object sender, TextChangedEventArgs e)
        {
            string filtro = TxtBuscar.Text.ToLower();
            DataGridEquipos.Items.Filter = item =>
                item is EquipoItem eq &&
                (eq.Nombre.ToLower().Contains(filtro) ||
                 eq.Tipo.ToLower().Contains(filtro));
        }
    }
}
ObservableCollection<T> automatically notifies the DataGrid when items are added or removed, so you do not need to manually refresh ItemsSource after each operation.
4

Create the modal dialog

Add a new WPF Window: Add → New Item → Window (WPF) → name it AgregarEquipoWindow.xaml.The dialog exposes a NuevoEquipo property that the page reads after ShowDialog() returns:
<!-- AgregarEquipoWindow.xaml -->
<Window x:Class="AppAdministrativa.AgregarEquipoWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Agregar / Editar Equipo"
        Width="400" Height="320"
        WindowStartupLocation="CenterOwner">
  <StackPanel Margin="20">
    <Label Content="ID" />
    <TextBox x:Name="TxtID" />
    <Label Content="Nombre" />
    <TextBox x:Name="TxtNombre" />
    <Label Content="Tipo" />
    <TextBox x:Name="TxtTipo" />
    <Label Content="Ubicación" />
    <TextBox x:Name="TxtUbicacion" />
    <Label Content="Estado" />
    <TextBox x:Name="TxtEstado" />
    <Button Content="Guardar" Click="BtnGuardar_Click" Margin="0,12,0,0" />
  </StackPanel>
</Window>
// AgregarEquipoWindow.xaml.cs
using System.Windows;

namespace AppAdministrativa
{
    public partial class AgregarEquipoWindow : Window
    {
        public EquipoItem? NuevoEquipo { get; private set; }

        // Constructor for adding a new item
        public AgregarEquipoWindow()
        {
            InitializeComponent();
        }

        // Constructor for editing an existing item
        public AgregarEquipoWindow(EquipoItem existente) : this()
        {
            TxtID.Text        = existente.ID;
            TxtNombre.Text    = existente.Nombre;
            TxtTipo.Text      = existente.Tipo;
            TxtUbicacion.Text = existente.Ubicacion;
            TxtEstado.Text    = existente.Estado;
        }

        private void BtnGuardar_Click(object sender, RoutedEventArgs e)
        {
            NuevoEquipo = new EquipoItem
            {
                ID        = TxtID.Text.Trim(),
                Nombre    = TxtNombre.Text.Trim(),
                Tipo      = TxtTipo.Text.Trim(),
                Ubicacion = TxtUbicacion.Text.Trim(),
                Estado    = TxtEstado.Text.Trim()
            };
            this.DialogResult = true;
            this.Close();
        }
    }
}
The page checks ventana.NuevoEquipo != null after ShowDialog(). If the user closes the window without clicking Guardar, NuevoEquipo remains null and no record is added or updated.
5

Register the menu item in MenuPrincipal.xaml

Open MenuPrincipal.xaml and add a Border menu item inside the sidebar StackPanel, following the same structure as existing items. Each item contains an icon Path and a TextBlock label:
<!-- MenuPrincipal.xaml — inside the sidebar StackPanel -->
<Border x:Name="MenuEquipos"
        MouseLeftButtonDown="Menu_Equipos_Click"
        Cursor="Hand">
  <StackPanel Orientation="Horizontal">
    <!-- Replace the Data value with your chosen icon path geometry -->
    <Path Data="M12 2 L2 7 L12 12 L22 7 Z"
          Fill="White" Width="20" Height="20"
          Margin="20,0,10,0" />
    <TextBlock Text="Equipos"
               Foreground="White"
               VerticalAlignment="Center" />
  </StackPanel>
</Border>
The TextBlock label is hidden when the sidebar collapses to 60 px because its parent StackPanel clips to the sidebar width. Only the Path icon remains visible in collapsed state.
6

Register the click handler in MenuPrincipal.xaml.cs

Open MenuPrincipal.xaml.cs and add a handler for the new menu item. Call FrameSecundario.Navigate() with a new page instance and pass the clicked Border to ActualizarSeleccion():
// MenuPrincipal.xaml.cs (add this method)
private void Menu_Equipos_Click(object sender, MouseButtonEventArgs e)
{
    FrameSecundario.Navigate(new Equipos());
    ActualizarSeleccion(MenuEquipos);
}
Menu click handlers use MouseButtonEventArgs (not RoutedEventArgs) because sidebar items use MouseLeftButtonUp events, not WPF Button.Click events. Pass the named border element directly to ActualizarSeleccion — do not use sender.
ActualizarSeleccion resets all menu item backgrounds to their default color, applies the #678EC2 highlight to the selected item, and collapses the sidebar to 60 px:
private void ActualizarSeleccion(Border botonSeleccionado)
{
    ResetearColoresMenu();
    botonSeleccionado.Background =
        new SolidColorBrush((Color)ColorConverter.ConvertFromString("#678EC2"));
    BarraLateral.Width = 60;
}
After completing this step, build and run the application. Your new Equipos menu item should appear in the sidebar, navigate to the page when clicked, and support adding, editing, deleting, and filtering records.

Summary of files created

FileTypePurpose
Equipos.xamlWPF PageDataGrid view with search filter and action buttons
Equipos.xaml.csCode-behindEquipoItem model, collection, and CRUD handlers
AgregarEquipoWindow.xamlWPF WindowAdd / Edit modal dialog form
AgregarEquipoWindow.xaml.csCode-behindNuevoEquipo property and save handler
Two existing files are modified:
FileChange
MenuPrincipal.xamlAdd a Border sidebar item
MenuPrincipal.xaml.csAdd Menu_Equipos_Click handler

Build docs developers (and LLMs) love