Diálogos
El esqueleto de diálogo de Dinaup y los diálogos listos para usar: confirmación destructiva y selector de ficha contra un informe.
Todos los diálogos de Dinaup comparten el mismo esqueleto. Lo montas con DnzDialogLayout y rellenas tres huecos: cabecera, cuerpo y pie. Para los dos casos más repetidos no montas nada: DnzConfirmDialog te resuelve el "¿seguro?" antes de borrar, y DnzItemPickerDialog deja al usuario elegir una ficha de un informe.
Los diálogos de DinaZen funcionan sobre el DialogService de Radzen. Necesitas el <RadzenDialog /> colocado en tu layout raíz y el servicio inyectado: @inject DialogService DialogService.
El patrón de diálogo Dinaup
Antes de mirar props, quédate con la receta. Un diálogo de edición estándar se monta así:
Envuelve en DnzDialogLayout
El componente raíz de tu .razor es <DnzDialogLayout>. Te da el grid de tres filas (cabecera / cuerpo con scroll / pie) y se encarga de cerrar.
Título en TitleContent
Un <span> con el título, normalmente condicional según si creas o editas: @(ID.IsEmpty() ? "Nueva Retención" : "Editar Retención").
Formulario en BodyContent
Aquí van los campos. Mientras cargan los datos, pinta un <DnzSkeleton>; cuando están, el formulario dentro de un <div class="d-flex flex-column gap-3 p-3">.
Botones en FooterContent
A la derecha: Guardar (Success, IsBusy=@isBusy) y Cerrar (Danger, Text). Al guardar bien, cierras con DialogService.Close(...).
Para confirmar algo destructivo (borrar, dar de baja, desvincular) NO montes un DnzDialogLayout. Eso es trabajo de DnzConfirmDialog.OpenAsync, que ya trae el icono de alerta, el color según gravedad y los dos botones. Te ahorra el diálogo entero.
DnzDialogLayout
Estructura fija de cualquier diálogo: cabecera arriba, cuerpo con scroll en medio y pie abajo. Tú rellenas los tres RenderFragment y el componente pone el grid y el botón de cerrar. Solo el cuerpo hace scroll; cabecera y pie quedan anclados.
<DnzDialogLayout>
<TitleContent>
<span>@(ID.IsEmpty() ? "Nueva Retención" : "Editar Retención")</span>
</TitleContent>
<BodyContent>
@if (cargando)
{
<DnzSkeleton Lines=4 MaxWidth="400px" />
}
else
{
<div class="d-flex flex-column gap-3 p-3">
<RadzenFormField class="w-100" Text="Nombre" Variant="Variant.Flat">
<RadzenTextBox @bind-Value=@editandoNombre Style="width:100%" />
</RadzenFormField>
<RadzenFormField class="w-100" Text="Retención %" Variant="Variant.Flat">
<RadzenNumeric @bind-Value=@editandoRetencion Style="width:100%" Format="N2" />
</RadzenFormField>
</div>
}
</BodyContent>
<FooterContent>
<div class="d-flex justify-content-end gap-2 align-items-center px-3">
<RadzenButton Click=@Cerrar ButtonStyle="ButtonStyle.Danger" Text="Cancelar" Variant="Variant.Text" />
<RadzenButton Click=@GuardarAsync ButtonStyle="ButtonStyle.Success" Text="Guardar" Icon="check" IsBusy=@isBusy />
</div>
</FooterContent>
</DnzDialogLayout>Si omites TitleContent o FooterContent, el layout genera una cabecera automática (con Title + Icon + botón de cerrar) y un pie con un botón "Cerrar" por defecto. Pero el patrón Dinaup es darle tu propio pie con Guardar + Cancelar.
Cabecera: automática o a medida
Si solo pasas Title (y opcionalmente Icon), el componente pinta la cabecera por ti con el título, el icono y el botón de cerrar. Si necesitas algo más, usa TitleContent y mandas tú.
<DnzDialogLayout Title="Detalle del pedido" Icon="receipt">
<BodyContent>
@* ... *@
</BodyContent>
</DnzDialogLayout><DnzDialogLayout>
<TitleContent>
<div class="d-flex align-items-center justify-content-between w-100">
<span class="fw-semibold">Detalle del pedido #1042</span>
<RadzenBadge Text="Pagado" BadgeStyle="BadgeStyle.Success" />
</div>
</TitleContent>
<BodyContent>
@* ... *@
</BodyContent>
</DnzDialogLayout>Convención del pie
El pie de página de Dinaup siempre sigue el mismo orden: Cancelar a la izquierda en estilo discreto, Aceptar a la derecha y destacado.
| Botón | Estilo | Variante |
|---|---|---|
| Cancelar | Danger | Text (discreto) |
| Aceptar | Success | sólido + IsBusy mientras procesa |
Marca el botón de Aceptar con IsBusy=@variable durante la operación: el propio botón muestra el spinner y bloquea el doble clic.
Parámetros
| Parámetro | Tipo | Por defecto | Qué hace |
|---|---|---|---|
Title | string | null | Texto del título cuando NO pasas TitleContent. |
Icon | string | null | Icono Material Design de la cabecera automática cuando NO pasas TitleContent. |
TitleContent | RenderFragment | null | Cabecera a medida. Si la omites, se genera con Title + Icon + botón de cerrar. |
BodyContent | RenderFragment | null | Cuerpo del diálogo (formulario, contenido). Es la única zona con scroll. |
FooterContent | RenderFragment | null | Pie a medida con los botones. Si lo omites, sale un "Cerrar" por defecto. |
ContentStyle | string | null | style inline para el contenedor del cuerpo (usar con cabeza). |
HeaderStyle | string | null | style inline para la cabecera. |
Flat | bool | false | true deja el diálogo blanco y plano, sin el fondo gris ni los degradados. |
DnzDialogLayout expone también SetWidth(width) y SetHeight(height) por referencia (@ref) para ajustar el tamaño del diálogo desde código, y Cerrar(), que llama a DialogService.Close(). En la mayoría de casos basta con definir el tamaño en las DialogOptions al abrir el diálogo.
DnzConfirmDialog
Diálogo de "¿seguro?" para acciones que no se pueden deshacer. No lo pintas en tu markup: lo abres con el método estático OpenAsync, que devuelve true si el usuario confirma y false si cancela o cierra.
@code {
async Task EliminarDominioAsync(string dominio)
{
if (await DnzConfirmDialog.OpenAsync(DialogService, $"¿Eliminar el dominio '{dominio}'?", "Eliminar dominio", DnzConfirmSeverity.Danger, "Eliminar") == false) return;
// ... borrar el dominio
NotificationService.Notify(NotificationSeverity.Success, "Dominio eliminado");
}
}El patrón es siempre el mismo: if (await ...OpenAsync(...) == false) return; y, si pasa, ejecutas la acción.
Firma de OpenAsync
public static async Task<bool> OpenAsync(DialogService dialogService, string message, string title = "Confirmar", DnzConfirmSeverity severity = DnzConfirmSeverity.Warning, string okText = "Confirmar", string cancelText = "Cancelar")Devuelve Task<bool>: true si el usuario pulsa aceptar, false si cancela, cierra o pulsa fuera. Solo dialogService y message son obligatorios; el resto tiene valor por defecto.
| Argumento | Tipo | Por defecto | Qué hace |
|---|---|---|---|
dialogService | DialogService | — | El servicio de diálogos de Radzen inyectado. |
message | string | — | Texto que explica la consecuencia. Aquí describes qué va a pasar. |
title | string | "Confirmar" | Título de la cabecera. |
severity | DnzConfirmSeverity | Warning | Color, icono y estilo del botón de aceptar según gravedad. |
okText | string | "Confirmar" | Texto del botón de aceptar. Mejor en imperativo: "Eliminar", "Sí, dar de baja". |
cancelText | string | "Cancelar" | Texto del botón de cancelar. |
Valores de DnzConfirmSeverity
El severity cambia el color de acento, el icono y el estilo del botón de aceptar. Elige el que case con la gravedad de la acción.
| Valor | Cuándo usarlo |
|---|---|
Info | Aviso informativo, sin riesgo. |
Warning | Acción que conviene revisar (valor por defecto). |
Danger | Acción destructiva o irreversible (borrar, anular). El botón sale en rojo. |
Success | Confirmación positiva de algo que va a completarse. |
El mensaje es el único parámetro obligatorio de verdad; el resto tiene valores por defecto, pero un title y un okText claros marcan la diferencia entre un diálogo entendible y un "¿Confirmar? [Confirmar]" que no dice nada. Para borrados usa siempre DnzConfirmSeverity.Danger.
if (await DnzConfirmDialog.OpenAsync(DialogService, $"¿Eliminar el dominio '{dominio}'?", "Eliminar dominio", DnzConfirmSeverity.Danger, "Eliminar") == false) return;if (await DnzConfirmDialog.OpenAsync(DialogService, "¿Instalar esta app externa? Tendrá acceso a tus datos.", "Instalar app externa", DnzConfirmSeverity.Warning, "Instalar") == false) return;DnzItemPickerDialog
Selector de ficha sobre un informe de Dinaup. Abre un diálogo con una tabla buscable y devuelve la fila que elija el usuario, o null si cancela. Igual que el de confirmación, se abre con OpenAsync.
@code {
async Task ElegirClienteAsync()
{
var fila = await DnzItemPickerDialog.OpenAsync(DialogService, Client, "clientes");
if (fila == null) return;
// fila es la DinaupDynamicRowDTO seleccionada
var nombre = fila.GetLegible("nombre");
}
}Firma de OpenAsync
public static async Task<DinaupDynamicRowDTO> OpenAsync(DialogService dialogService, DinaupClientC client, string reportId)Devuelve Task<DinaupDynamicRowDTO> con la fila seleccionada, o null si el usuario cancela o cierra el diálogo. Por dentro monta un DnzReportView sobre el informe que le pases y cierra devolviendo la fila al pulsar sobre ella.
| Argumento | Tipo | Por defecto | Qué hace |
|---|---|---|---|
dialogService | DialogService | — | El servicio de diálogos de Radzen inyectado. |
client | DinaupClientC | — | El cliente Dinaup con el que se ejecuta el informe. |
reportId | string | — | Id del informe que alimenta la tabla de selección. |
El diálogo es redimensionable y arrastrable, y muestra hasta 50 filas. El informe (reportId) debe existir y devolver las columnas que quieras enseñar; la fila completa vuelve como DinaupDynamicRowDTO. Si necesitas el selector embebido en un formulario en vez de un diálogo aparte, mira los selectores.
Relacionado
- Intro a DinaZen — la librería de componentes al completo.
- Índice de componentes · Selectores · Tablas e informes
- Cookbook de patrones — recetas de diálogos y otros patrones de UI.
- Cliente Dinaup — el cliente que alimenta el selector de ficha.
- Informes de Dinaup Flex — los informes que usa
DnzItemPickerDialog.
Vistas avanzadas
Componentes especializados fuera del CRUD: línea de tiempo Gantt, código resaltado para documentar dentro de la app y selectores de rango de fechas con presets.
C# Code Style Guidelines
Guía de estilo C# para proyectos Dinaup, con configuración del proyecto, cultura invariante y buenas prácticas.