Diálogos y ventanas
El esqueleto de diálogo de Dinaup, los diálogos predefinidos (confirmar, elegir ficha, histórico, anotaciones) y el gestor de ventanas flotantes.
Todos los diálogos de Dinaup comparten el mismo esqueleto. Se monta con DnzDialogLayout, que tiene tres huecos: cabecera, cuerpo y pie. Los casos repetidos ya están hechos: DnzConfirmDialog pide confirmación antes de borrar, DnzItemPickerDialog abre la selección de una ficha de un informe y DnzHistoricoDialog muestra el histórico de un campo. Cuando el contenido tiene que convivir con la pantalla en lugar de cubrirla, se abre en una ventana flotante con DnzWindowManagerService.
Los diálogos funcionan sobre el DialogService de Radzen: necesitas <RadzenDialog /> en el diseño principal (MainLayout.razor) y @inject DialogService DialogService. Las ventanas necesitan <DnzWindowHost /> en el mismo diseño y el servicio DnzWindowManagerService que registra AddDinaZen.
Qué componente uso
| Necesitas | Componente |
|---|---|
| Montar tu propio diálogo con cabecera, cuerpo y pie | DnzDialogLayout |
| Confirmar una acción antes de ejecutarla | DnzConfirmDialog |
| Que el usuario elija una fila de un informe | DnzItemPickerDialog |
| Ver el histórico de cambios de un campo | DnzHistoricoDialog |
| Comentarios, ficheros o galería de un registro | DnzAnnotationsDialog |
| Qué registros apuntan a este | DnzBacklinksDialog |
| Vista previa de un correo con botón de enviar | DnzEmailPreviewDialog |
| Editar una imagen en un diálogo | DnzImageEditorDialog, en Entrada de datos y formularios |
| Abrir un informe, una ficha o un documento en ventana flotante | DnzWindowManagerService, OpenAsWindow |
El patrón de diálogo Dinaup
Envuelve en DnzDialogLayout
El componente raíz de tu .razor es <DnzDialogLayout>. Aporta la rejilla de tres filas (cabecera, cuerpo con desplazamiento, pie) y el cierre.
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
Los campos. Mientras cargan los datos, IsLoading=true muestra un esqueleto; con datos, 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 Cancelar (Danger, Text). Si el guardado termina bien, cierra con DialogService.Close(true).
Para confirmar algo destructivo (borrar, dar de baja, desvincular) no montes un DnzDialogLayout. Usa DnzConfirmDialog.OpenAsync, que ya trae el icono, el color según gravedad y los dos botones.
DnzDialogLayout
Estructura fija de cualquier diálogo: cabecera, cuerpo con desplazamiento y pie. Rellenas los tres RenderFragment y el componente pone la rejilla y el botón de cerrar. Solo el cuerpo se desplaza; cabecera y pie quedan fijos.
<DnzDialogLayout AltoAuto=true IsLoading=@cargando ErrorText=@errorCarga OnRetry=@CargarAsync>
<TitleContent>
<span>@(ID.IsEmpty() ? "Nueva retención" : "Editar retención")</span>
</TitleContent>
<BodyContent>
<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 componente genera una cabecera automática (Title, Icon y botón de cerrar) y un pie con un botón Cerrar. El patrón Dinaup es darle tu propio pie con Guardar y Cancelar.
Cabecera: automática o a medida
<DnzDialogLayout Title="Detalle del pedido" Icon="receipt" AltoAuto=true>
<BodyContent>
<DetallePedidoU Pedido=@pedido />
</BodyContent>
</DnzDialogLayout><DnzDialogLayout AltoAuto=true>
<TitleContent>
<div class="d-flex align-items-center justify-content-between w-100">
<span class="fw-semibold">Detalle del pedido 1042</span>
<DnzBadgetAutoColor Value="Pagado" />
</div>
</TitleContent>
<BodyContent>
<DetallePedidoU Pedido=@pedido />
</BodyContent>
</DnzDialogLayout>Convención del pie
| Botón | Estilo | Variante |
|---|---|---|
| Cancelar | Danger | Text |
| Aceptar | Success | sólido, con IsBusy mientras procesa |
Marca el botón de aceptar con IsBusy=@variable durante la operación: el botón muestra el indicador de carga mientras dura. Para evitar un doble guardado, comprueba al inicio del método si ya hay uno en curso.
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 de la cabecera automática. |
TitleContent | RenderFragment | null | Cabecera a medida. |
BodyContent | RenderFragment | null | Cuerpo del diálogo. Es la única zona con desplazamiento. |
FooterContent | RenderFragment | null | Pie a medida con los botones. Sin él, aparece un botón Cerrar. |
ContentStyle | string | null | style en línea para el contenedor del cuerpo. |
HeaderStyle | string | null | style en línea para la cabecera. |
IsLoading | bool | false | Sustituye el cuerpo por un esqueleto de carga. |
LoadingText | string | null | Con texto, muestra un indicador de carga con ese mensaje en lugar del esqueleto. Para esperas largas. |
ErrorText | string | null | Con texto, sustituye el cuerpo por un aviso de error. Tiene prioridad sobre IsLoading. |
OnRetry | EventCallback | Con delegado, el aviso de error muestra el botón Reintentar. Un error de configuración se deja sin delegado. | |
Flat | bool | false | Diálogo blanco y plano, sin fondo gris ni degradados. |
AltoAuto | bool | false | El diálogo se ajusta a su contenido hasta un máximo de 85vh (variable CSS --dnz-dialog-alto-max), con el cuerpo desplazable y el pie visible. Ábrelo con Height = null en las DialogOptions. |
AltoAuto=true es la forma recomendada de abrir un diálogo. No lo uses cuando el cuerpo se dimensiona a partir del hueco (un TPV a pantalla completa, un lienzo, un visor): ahí el alto lo ponen las DialogOptions.
Por @ref tienes SetWidth(string width) y SetHeight(string height) para ajustar el tamaño desde código, y Cerrar(), que llama a DialogService.Close().
DnzConfirmDialog
Diálogo de confirmación para acciones que no se pueden deshacer. No va en tu marcado: se abre 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, $"Se elimina el dominio '{dominio}' y no se puede deshacer.", "Eliminar dominio", DnzConfirmSeverity.Danger, "Eliminar") == false) return;
await BorrarDominioAsync(dominio);
NotificationService.Notify(NotificationSeverity.Success, "Dominio eliminado");
}
}El patrón es siempre el mismo: if (await DnzConfirmDialog.OpenAsync(...) == false) return; y después 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")| Argumento | Tipo | Por defecto | Qué hace |
|---|---|---|---|
dialogService | DialogService | El servicio de diálogos de Radzen inyectado. | |
message | string | Texto que explica la consecuencia. | |
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, con el verbo de la acción: "Eliminar", "Dar de baja". |
cancelText | string | "Cancelar" | Texto del botón de cancelar. |
Valores de DnzConfirmSeverity
| 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 se muestra en rojo. |
Success | Confirmación positiva de algo que va a completarse. |
if (await DnzConfirmDialog.OpenAsync(DialogService, $"Se elimina el dominio '{dominio}' y no se puede deshacer.", "Eliminar dominio", DnzConfirmSeverity.Danger, "Eliminar") == false) return;if (await DnzConfirmDialog.OpenAsync(DialogService, "La app externa tendrá acceso a tus datos.", "Instalar app externa", DnzConfirmSeverity.Warning, "Instalar") == false) return;DnzItemPickerDialog
Selector de ficha sobre un informe. Abre un diálogo con un DnzReportView de 50 filas por página y devuelve la fila que elija el usuario, o null si cancela.
@code {
async Task ElegirClienteAsync()
{
var fila = await DnzItemPickerDialog.OpenAsync(DialogService, Client, SectionsD.EntidadesD._SectionID);
if (fila == null) return;
var nombre = fila.GetLegible("nombre");
}
}Firma de OpenAsync
public static async Task<DinaupDynamicRowDTO> OpenAsync(DialogService dialogService, DinaupClientC client, string reportId)| Argumento | Tipo | Qué hace |
|---|---|---|
dialogService | DialogService | El servicio de diálogos de Radzen inyectado. |
client | DinaupClientC | El cliente con el que se ejecuta el informe. |
reportId | string | GUID del informe que alimenta la tabla de selección. |
El diálogo mide min(95%, 1806px) de ancho y 600 px de alto, y es redimensionable y arrastrable. Para el selector embebido en un formulario, consulta Selectores.
DnzHistoricoDialog
Histórico de cambios de un campo de un registro (auditoría): primero el valor vigente con autor y fecha, después cada cambio como "antes y después". Lee Client.GetHistoryChanges.
@code {
async Task VerHistorialAsync()
{
await DnzHistoricoDialog.Open(DialogService, Client, sectionId, rowId, "importe", "Importe");
}
}public static async Task Open(DialogService dialogService, DinaupClientC client, Guid sectionId, Guid rowId, string field, string fieldLabel = null)fieldLabel es el nombre visible del campo; sin él se muestra el nombre interno.
DnzAnnotationsDialog
Comentarios, ficheros y galería pública de un registro, con pestañas para cambiar de tipo y un redactor con menciones. Lee Client.Annotations_GetAsync y escribe con Client.Annotation_PutAsync.
public static async Task OpenAsync(DialogService dialogService, DinaupClientC client, Guid sectionId, Guid rowId, AnnotationTypeE type)AnnotationTypeE tiene Comments, Files y PublicGallery. DnzFormView pide abrir este diálogo con OnAnnotationOpen, que recibe un DinaupAnnotationRequest con SectionId, RowId, Type y Client.
DnzBacklinksDialog
Lista los registros que apuntan al registro dado, una línea por campo de relación. Lee Client.GetBacklinksAsync al abrir.
public static async Task OpenAsync(DialogService ds, DinaupClientC client, string sectionId, string rowId)DnzEmailPreviewDialog
DnzEmailPreview envuelto en un diálogo con un botón de confirmar. Devuelve true si el usuario pulsa el botón.
public static async Task<bool> OpenAsync(DialogService dialogService, string body, string destinatario, string asunto, EventCallback onConfirmar, string confirmarTexto = "Enviar")Otros diálogos
| Diálogo | Qué hace |
|---|---|
DnzFormInspectorDialog | Inspector de los campos de una ficha (RowId, SectionId, SectionLabel, Fields: List<VirtualFormDTO.Control>). Herramienta de depuración de DnzFormView. |
DnzReportDebugDialog | Tiempos y metadatos de un informe. Detalle en Tablas e informes. |
DnzRecoveryPasswordWithDinaupDialog | Panel informativo de recuperación de contraseña con la cuenta Dinaup. Sin parámetros; se abre con DialogService.OpenAsync<DnzRecoveryPasswordWithDinaupDialog>. |
Ventanas flotantes
Una ventana flotante es un panel arrastrable, redimensionable, minimizable y maximizable que convive con la pantalla. Las gestiona DnzWindowManagerService (registrado por AddDinaZen, uno por circuito Blazor) y las muestra DnzWindowHost, que además monta la barra de tareas DnzTaskbar cuando hay alguna abierta.
Coloca DnzWindowHost en el diseño principal
Una vez, en MainLayout.razor, junto a <RadzenDialog />.
<DinaZen.Components.WindowManager.DnzWindowHost />Abre con los métodos OpenAsWindow
DnzReportView, DnzFormView y DnzDynamicDocumentView traen un OpenAsWindow que recibe el gestor y devuelve el id de la ventana.
@inject DnzWindowManagerService WindowManager
@code {
private void AbrirCliente(string clienteId)
{
var windowId = DnzFormView.OpenAsWindow(WindowManager, Client, SectionsD.EntidadesD._SectionID, clienteId, title: "Cliente");
}
}O abre tu propio contenido con Open
Open(WindowOptions options, RenderFragment content) acepta cualquier RenderFragment.
@code {
private void AbrirTarea(string tareaId, string titulo)
{
RenderFragment content = builder =>
{
builder.OpenComponent<TareaU>(0);
builder.AddAttribute(1, "TareaId", tareaId);
builder.CloseComponent();
};
WindowManager.Open(new WindowOptions { Title = titulo, Icon = "task_alt", InitialWidth = 1100, InitialHeight = 700, MinWidth = 800, MinHeight = 400 }, content);
}
}WindowOptions
| Propiedad | Tipo | Por defecto | Qué hace |
|---|---|---|---|
Title | string | "" | Título de la ventana. |
Subtitle | string | "" | Subtítulo. |
Icon | string | "" | Icono Material. |
IconUrl | string | "" | URL de icono propio. Tiene prioridad sobre Icon. |
InitialWidth | double | 1100 | Ancho inicial en px. |
InitialHeight | double | 620 | Alto inicial en px. |
MinWidth | double | 1000 | Ancho mínimo al redimensionar. Redúcelo si el contenido tiene una maqueta estrecha. |
MinHeight | double | 400 | Alto mínimo al redimensionar. |
ControlKey | string | null | Identidad de la ventana a efectos de "de esta solo puede haber una". Con valor, volver a pedirla trae al frente la que ya hay y Open devuelve su id. |
Ambito | AmbitoVentanaE | Pestana | Hasta dónde llega ese control: Pestana, Usuario o Licencia. Los dos últimos necesitan que la app conteste por ResolverOcupadaFuera. |
PresetId | string | null | Id predefinido, para pasar el WindowId al contenido antes de abrir. |
DnzWindowManagerService
| Miembro | Qué hace |
|---|---|
Windows | Las ventanas abiertas (IReadOnlyList<WindowState>). |
MaxWindows | Constante: 6 ventanas a la vez. Al superarlo, Open no abre y dispara OnMaxWindowsReached. |
Open(options, content) | Abre y devuelve el id. Devuelve null si otra sesión tiene la ventana con ese ControlKey (OnVentanaOcupada). |
Close(windowId), CloseActive() | Cierra una ventana o la activa. |
Focus, Minimize, Restore, ToggleMaximize, ToggleMinimize | Con el windowId. |
UpdateTitle, UpdateSubtitle, UpdateIcon, UpdateWindowInfo | Cambian los rótulos de una ventana abierta. |
GetWindow(windowId) | El WindowState (id, título, posición, tamaño, IsMinimized, IsMaximized, IsActive, ControlKey). |
OnChanged | Evento al cambiar cualquier ventana. |
ResolverOcupadaFuera | Func<string, AmbitoVentanaE, string> que la app rellena para los ámbitos Usuario y Licencia. |
ControlKeysAbiertas | Las ControlKey abiertas en este circuito, para publicarlas a otras sesiones. |
Las ventanas se renderizan sueltas, sin contenedor, para que cada una compita en z-index con los modales de Radzen. Cambiar de capa solo cambia un número, no el contenedor, así que lo escrito dentro no se pierde.
Relacionado
- DinaZen: instalación,
DnzHead,DnzScriptsyDnzWindowHost. - Componentes · Selectores · Tablas e informes
- Patrones de uso: el ejemplo del esqueleto de diálogo.
- Cliente Dinaup: el cliente que alimenta el selector de ficha y el histórico.