Condividi tramite


Panoramica della personalizzazione e della creazione di moduli con lo strumento di creazione di Service Manager

Un modulo è una finestra che consente agli utenti di interagire con gli oggetti del database. Gli utenti possono utilizzare un modulo per visualizzare e modificare le proprietà degli oggetti. Ciascun modulo è associato a una classe specifica e visualizza informazioni per le sole istanze della classe di destinazione. Un modulo contiene alcuni campi. In genere, ogni campo è associato a una proprietà specifica della classe di destinazione del modulo. Ad esempio, il modulo degli eventi imprevisti è associato all'oggetto evento imprevisto. Di conseguenza, il modulo degli eventi imprevisti visualizza informazioni sugli oggetti degli eventi imprevisti nel database.

Un modulo di Service Manager è costituito dall'implementazione del modulo Windows Presentation Foundation (WPF) in un assembly di Microsoft .NET Framework e da una definizione di modulo in un Management Pack di Service Manager. La definizione del modulo indica la classe rappresentata dal modulo, insieme ad altre proprietà dello stesso.

Concetti chiave sui moduli

Prima di personalizzare i moduli, è necessario studiare i seguenti concetti.

Utilizzo dei moduli

Quando il Management Pack che contiene le definizioni di modulo viene importato in Service Manager, le definizioni dei moduli vengono archiviate nel database. Successivamente, quando l'utente avvia un'attività console di Service Manager che richiede la visualizzazione di un oggetto, Service Manager deve trovare un modulo per visualizzare l'oggetto richiesto. Service Manager accede al database e cerca un modulo definito per tale oggetto. Se per l'oggetto non è definito alcun modulo, Service Manager cerca un modulo definito per l'oggetto padre dell'oggetto. Service Manager continua a cercare l'intera gerarchia di ereditarietà dell'oggetto finché non trova un modulo definito.

Moduli generici

Se Service Manager non riesce a trovare alcun modulo per l'oggetto o per uno dei relativi oggetti padre, Service Manager compila in modo dinamico un modulo generico predefinito per tale oggetto. Il modulo generico viene generato dal sistema ed è sufficiente per l'utilizzo di un modulo semplice. Il modulo generico è un modo semplice e rapido per creare un modulo per gli oggetti senza alcuna definizione di modulo.

Per impostazione predefinita, il modulo generico visualizza tutte le proprietà del modulo in un layout semplice che non è possibile modificare. Il modulo generico visualizza le proprietà di tutti gli oggetti padre nella gerarchia di ereditarietà del modulo e non è possibile modificare tale comportamento. Le possibilità di personalizzazione del modulo generico sono limitate. Ad esempio, è possibile specificare le proprietà che si desidera che il modulo generico visualizzi; Tuttavia, il modulo generico non può essere usato come base per la personalizzazione. Se successivamente si definisce un modulo personalizzato per tale oggetto, il modulo personalizzato sovrascrive il modulo generico dell'oggetto.

Per ulteriori informazioni utili a nascondere le proprietà in un modulo generico e sugli altri modi per personalizzare questo tipo di modulo, consultare il post del blog Overview of the Forms Infrastructure and the Generic Form (Panoramica delle infrastrutture dei moduli e del modulo generico).

Classi combinate nei moduli

In alcuni casi, un modulo deve mostrare informazioni derivate da più di una classe. Per ottenere ciò, viene creata una classe combinata da associare a un campo del modulo. Per altre informazioni sulle classi combinate, vedere Modifiche allo schema comune di System Center.

Aspetti funzionali di un modulo

Un modulo utilizza i seguenti aspetti funzionali:

  1. Inizializzazione

  2. Dimensione e posizione

  3. Refresh

  4. Invia modifiche

Questi aspetti vengono descritti nelle sezioni seguenti.

Inizializzazione

Durante l'inizializzazione, viene analizzato il linguaggio XAML (Extensible Application Markup Language) di un modulo e tutti i controlli del modulo vengono create e caricate. L'evento Loaded del modulo indica quando la maschera e tutti gli elementi contenuti sono stati caricati. Le operazioni di caricamento dati sono asincrone. Pertanto, l'istanza di destinazione potrebbe non essere disponibile quando viene generato l'evento Loaded . Al contrario, l'evento DataContextChanged deve essere utilizzato per avvertire quando l'istanza di destinazione viene impostata nel modulo. L'evento PropertyChanged della proprietà DataContext può essere utilizzato al posto dell'evento DataContextChanged .

Si consiglia di utilizzare l'evento Loaded per l'inizializzazione personalizzata relativa ai controlli e utilizzare gli eventi DataContextChanged o PropertyChanged della proprietà DataContext per l'inizializzazione personalizzata relativa all'istanza di destinazione.

Dimensione e posizione

Quando un modulo viene visualizzato in una finestra popup, le dimensioni iniziali vengono determinate in base alle proprietà Width, Height, MinWidth e MinHeight del form. Se queste proprietà non sono impostate per il modulo, le dimensioni iniziali del modulo vengono calcolate in base al relativo contenuto.

Si consiglia di impostare tali proprietà come indicato di seguito:

  • Impostare le proprietà Width e Height del modulo per specificarne in modo esplicito la dimensione ideale. Si consiglia di impostare queste proprietà con il valore Auto . In questo modo è possibile impostare la larghezza e l'altezza del modulo in base alla dimensione del contenuto.

  • Impostare le proprietà MinWidth e MinHeight del modulo per specificare la dimensione minima accettabile della finestra per il modulo. Se un utente riduce la finestra a una dimensione inferiore a quella specificata, vengono visualizzate barre di scorrimento per scorrere il contenuto nascosto del modulo.

Quando il modulo è ospitato all'interno dell'host dei moduli di Service Manager, le dimensioni e la posizione dell'ultimo utilizzo vengono mantenute per la visualizzazione successiva di tale modulo da parte dello stesso utente all'interno della stessa sessione di esecuzione.

Refresh

L'istanza di destinazione di un modulo può cambiare in seguito all'esecuzione di un comando Refresh comando sul modulo. Il gestore di questo comando recupera i nuovi dati dal database. Quando arrivano i dati, il valore della proprietà DataContext del modulo viene impostato sulla nuova istanza di destinazione e viene generato l'evento DataContextChanged.

Per fare distinzione tra l'evento DataContextChanged , generato al primo caricamento del modulo, e l'evento generato per la gestione di un comando di Refresh , verificare la proprietà OldValue degli argomenti dell'evento a esso inviati. Il valore di questa proprietà è nullo se il modulo è appena stato inizializzato.

Invia modifiche

La finestra popup dell'host del modulo in Service Manager fornisce pulsanti per l'invio di modifiche apportate nel modulo e per la chiusura della finestra popup.

Quando un utente seleziona il pulsante Applica per un modulo, l'istanza di destinazione del modulo viene inviata per l'archiviazione. Questa operazione è sincrona; pertanto, l'utente non può modificare il modulo fino al completamento dell'operazione di invio. In caso di errore durante l'invio del modulo, viene visualizzato un messaggio di errore. Il modulo resta aperto per apportare ulteriori modifiche. Si consiglia agli utenti di applicare spesso le modifiche per evitare conflitti nel caso in cui un'altra istanza del modulo viene modificata allo stesso tempo.

Se l'utente seleziona il pulsante OK , il comportamento è simile a Applica, ad eccezione del fatto che, se l'operazione di invio del modulo ha esito positivo, il modulo e la relativa finestra host vengono chiusi.

Se l'utente seleziona il pulsante Annulla , viene visualizzata una finestra di dialogo che chiede all'utente di confermare l'operazione. L'utente può selezionare e perdere le modifiche oppure selezionare No e tornare al modulo.

Linee guida generali e procedure consigliate per i moduli

È possibile estendere le funzionalità di Service Manager aggiungendo o modificando moduli. In questa sezione vengono descritte alcune procedure consigliate per la creazione e l'uso di moduli di Service Manager, usando direttamente vari strumenti e definizioni dei moduli di scripting.

Questa sezione è destinata principalmente a partner e clienti esperti nella creazione di moduli personalizzati tramite Windows Presentation Foundation (WPF) e Microsoft Visual Studio Team System o Microsoft Expression Blend.

Di seguito sono riportate le linee guida generali per la creazione e la modifica di un nuovo modulo.

  • Utilizzare i controlli standard.
  • Seguire le indicazioni generali per la progettazione del modulo.
  • Evitare i code-behind.
  • Includere la gestione delle eccezioni.
  • Considerare la personalizzazione e gli aggiornamenti dei moduli.
  • Nominare tutti i controlli personalizzabili.
  • Associare il modulo a origini dati.
  • Usare le regole di convalida dell'infrastruttura di Service Manager, i convertitori di valori e i modelli di errore.
  • Utilizzare i comandi ed eventi dell'infrastruttura dei moduli.

Per informazioni su queste linee guida, vedere le sezioni riportate di seguito.

Usare i controlli standard

I controlli utilizzati in un modulo possono essere come segue:

  • Controlli standard. Sono inclusi i controlli di libreria .NET, come caselle combinate e caselle di riepilogo.
  • Controlli personalizzati. Sono inclusi i controlli aggiuntivi che vengono creati dall'autore del modulo o da terzi.

Suggerimento

Quando si utilizzano controlli standard, evitare se possibile la creazione di controlli personalizzati, al fine di promuovere la coerenza nell'esperienza utente con i moduli. Qualora fosse necessario creare un controllo personalizzato, separare l'aspetto visivo, il comportamento e il comportamento logico utilizzando i modelli di controllo per definire l'aspetto del controllo. Preferibilmente, dovrebbe esserci un modello di controllo separato per ogni tema di Windows.

Seguire le linee guida generali per la progettazione dei moduli

Quando si progetta un modulo, utilizzare le linee guida pubbliche di progettazione al fine di creare un modulo semplice e intuitivo e che aderisca ai comuni paradigmi di interazione dell'utente.

Per ulteriori informazioni sulla progettazione generale in Windows, vedere Windows User Experience Interaction Guidelines (Linee guide sull'interazione per l'esperienza utente in Windows).

In aggiunta:

  • Suddividere le informazioni in più schede per rendere il modulo più semplice e più facile da leggere. Includere le informazioni più usate nella prima scheda e le informazioni di minore importanza nelle schede successive.
  • Utilizzare i pannelli di layout per disporre controlli nel modulo. In questo modo, il modulo si comporta correttamente quando viene ridimensionato e localizzato.
  • Evitare di impostare singole proprietà visive del controllo e utilizzare invece gli stili. In questo modo è possibile modificare l'aspetto di tutti i controlli in una serie di moduli modificando lo stile e promuove un aspetto coerente tra i moduli correlati.

Evitare code-behind

Il termineCode-behind descrive il codice collegato agli oggetti definiti da commenti quando una pagina XAML viene compilata con i commenti. Limitare il più possibile l'utilizzo di code-behind in un modulo. È preferibile incorporare il codice per un modulo nel controllo stesso, perché in seguito è più semplice modificare il codice. Usare invece le funzionalità dichiarative supportate dall'infrastruttura basata su moduli di Service Manager per definire le conversioni dei valori e le regole di convalida nel modulo.

Come linea guida generale, è consigliabile limitare l'uso del code-behind alle situazioni in cui non è possibile fornire le funzionalità necessarie usando le funzionalità dichiarative di XAML, con le classi definite in WPF e nella libreria dell'infrastruttura dei moduli. Anche in questo caso, si consiglia di spostare in una libreria helper la funzionalità implementata nel code-behind, quindi di farvi riferimento da XAML.

Includere la gestione delle eccezioni

Assicurarsi che il codice nel modulo contenga la gestione delle eccezioni in modo che il modulo possa essere caricato sia durante la fase di progettazione nello strumento di creazione che nella console di Service Manager in fase di esecuzione.

Prendere in considerazione la personalizzazione e gli aggiornamenti dei moduli

Quando si progetta un nuovo modulo, è consigliabile prendere in considerazione le personalizzazioni e gli aggiornamenti futuri a tale modulo. Per assicurarsi che sia possibile personalizzare e aggiornare un modulo mantenendo le personalizzazioni, seguire le linee guida e i suggerimenti forniti in precedenza in questa sezione, insieme alle linee guida seguenti:

  • Prendere in considerazione le personalizzazioni e gli aggiornamenti futuri durante la progettazione del modulo. È probabile che i moduli si evolvono nelle versioni future ed è importante considerare come gli utenti potranno eseguire l'aggiornamento alle nuove versioni del modulo mantenendo al tempo stesso le personalizzazioni al modulo originale. Ad esempio, è possibile fornire un modulo aggiornato dopo che gli utenti hanno già investito molto tempo nella personalizzazione del modulo originale. Gli utenti si aspettano che le personalizzazioni vengano conservate con l'aggiornamento della versione.

  • Specificare un nome univoco per ogni controllo del modulo in modo da consentire di applicare le personalizzazioni ai controlli. Le personalizzazioni dei moduli vengono archiviate come una serie di azioni destinate a un controllo specifico o a un insieme di controlli. Viene fatto riferimento al controllo di destinazione in base al nome, motivo per cui è importante mantenere i nomi dei controlli tra le versioni del modulo. Se un controllo non ha un nome, l'Editor personalizzazione modulo genera un nome, ma il nome generato non viene mantenuto in versioni diverse del modulo.

  • Assicurarsi che i nomi dei controlli rimangano non modificabili tra versioni diverse del modulo. In questo modo si garantisce che le personalizzazioni di un determinato controllo in una precedente versione possano essere applicate allo stesso controllo in una nuova versione del modulo.

  • Se possibile, quando si aggiorna un modulo, evitare di spostare i controlli in una posizione diversa nella stessa scheda. Una personalizzazione frequente applicata dagli utenti è lo spostamento in una posizione diversa dei controlli nel modulo. Se si modifica la posizione di un controllo in una nuova versione del modulo, è possibile che la nuova posizione del controllo si sovrapponga a un controllo che l'utente ha spostato.

  • Se possibile, evitare di spostare i controlli tra schede durante la progettazione di un aggiornamento a un modulo esistente. I controlli vengono identificati sia per nome che per scheda in cui si trovano. Lo spostamento di un controllo da una scheda a un'altra in una nuova versione del modulo può interrompere le personalizzazioni applicate dall'utente a tale controllo in quanto le personalizzazioni non saranno in grado di identificare il controllo di destinazione.

  • Quando l'aggiornamento a un modulo include nuovi controlli, è consigliabile aggiungere i nuovi controlli a una nuova scheda. Questo è il modo più sicuro per evitare di interferire con le personalizzazioni degli utenti alle schede e ai controlli esistenti.

  • Tenere presente le associazioni dei controlli. I controlli di sola lettura devono utilizzare solo associazioni unidirezionali.

Assegnare un nome a tutti i controlli personalizzabili

Assicurarsi che i nomi dei controlli descrivano i dati ai quali è associato il controllo o l'azione eseguita da tale controllo.

Associare il modulo alle origini dati

Lo scopo principale di un modulo è visualizzare un singolo oggetto dal database di Service Manager. L'oggetto viene detto target instanceed è sempre specificato dalla proprietà DataContext di un modulo (che viene ereditata dalla classe FrameworkElement ).

Importante

Non modificare la proprietà DataContext del modulo. L'ambiente che ospita i moduli utilizza questa proprietà per identificare l'istanza di destinazione del modulo.

Nel modello di dati di Service Manager un'istanza di destinazione viene rappresentata come oggetto BindableDataItem . Questa classe aggrega l'oggetto SDK sottostante e ne espone le proprietà tramite un indicizzatore, che assume un nome di proprietà come parametro.

La classe BindableDataItem implementa anche ICustomTypeDescriptor, che consente di utilizzare la classe BindableDataItem come origine dati per l'associazione WPF. Di seguito è riportato un esempio di associazione di una proprietà dell'istanza di destinazione alla proprietà Text di un controllo TextBox :


<TextBox Name="textBoxDescription" Text="{Binding Path=Summary}"/>

Non è necessario specificare l'origine dell'associazione perché le istanze di destinazione vengono impostate come DataContext del modulo, che funge da origine predefinita per tutti i controlli del modulo.

I controlli nel modulo possono essere associati a origini dati diverse dall'istanza di destinazione e la libreria dell'infrastruttura dei moduli contiene molti controlli che eseguono l'associazione in modo implicito. Ad esempio, il controllo di selezione istanza è associato all'origine dati, che fornisce l'insieme di istanze da scegliere. È anche possibile definire origini dati aggiuntive in modo dichiarativo usando le classi ObjectDataProvider e XmlDataProvider .

L'infrastruttura dei moduli considera l'istanza di destinazione come l'unica origine dati di lettura/scrittura nel modulo. Pertanto, con l'implementazione del comando Submit verranno archiviate solo le modifiche effettuate all'istanza di destinazione. Le altre origini dati del modulo vengono considerate come di sola lettura.

Usare regole di convalida dell'infrastruttura basata su moduli di Service Manager, convertitori di valori e modelli di errore

È consigliabile usare le regole di convalida dell'infrastruttura basata su moduli per designare l'input di dati non valido. L'infrastruttura di associazione WPF supporta la convalida per le proprietà del controllo associate a un'origine dati con associazioni unidirezionali o bidirezionali. L'oggetto di associazione ha una raccolta di ValidationRules che può contenere un numero qualsiasi di oggetti ValidationRule . Ogni volta che i dati vengono inseriti dal controllo nell'origine dati, gli oggetti ValidationRule vengono richiamati per convalidare il valore.

La libreria dell'infrastruttura dei moduli contiene molte regole di convalida che gestiscono i casi più comuni. L'infrastruttura dei moduli sfrutta le regole di convalida per determinare se sia possibile inviare il contenuto del modulo per l'archiviazione. Ad esempio, il pulsante Invia di un modulo può essere disabilitato se è presente un controllo con un errore di convalida nel modulo.

Si consiglia di utilizzare il modello di errore personalizzato che viene fornito con la libreria dell'infrastruttura dei moduli. Se un controllo presenta un errore di convalida, esso viene visualizzato con un bordo rosso per impostazione predefinita. WPF permette di definire un indicatore di errore personalizzato tramite la proprietà Validation.ErrorTemplate , la quale può essere impostata per qualsiasi controllo. La libreria dell'infrastruttura basata su moduli di Service Manager contiene un modello di errore personalizzato, che visualizza un'icona di errore anziché il bordo rosso WPF. Inoltre, quando si posiziona il mouse sull'icona di errore, viene visualizzata una descrizione del comando con un messaggio di errore. Il messaggio di errore dovrebbe indicare il motivo per cui non è riuscita la convalida dei dati nel controllo.

Nell'esempio riportato di seguito viene illustrato come fare riferimento al modello di errore nel codice XAML:


<TextBox Text="{Binding SomeProperty}"
         scwpf:Validation.ValueRequired="True"
         Validation.ErrorTemplate="{DynamicResource {ComponentResourceKey {x:Type scwpf:Validation}, InvalidDataErrorTemplate}}"/>

Se le regole di convalida predefinite non forniscono la logica di convalida necessaria, è consigliabile creare regole di convalida personalizzate per rappresentare tale logica. Ciò consentirà alla logica di convalida standard e personalizzata di coesistere all'interno del meccanismo comune di gestione della convalida.

Se il meccanismo delle regole di convalida non è adeguato per uno scenario specifico, è invece necessario gestire FormEvents.PreviewSubmitEvent ed eseguire la convalida da questa posizione.

Nell'esempio di codice riportato di seguito viene fornito un esempio di modello che è possibile utilizzare per eseguire la convalida personalizzata:


void MyForm_Loaded(object sender, RoutedEventArgs e)
{
    // hook to handle form events
    this.AddHandler(
        FormEvents.PreviewSubmitEvent,
        new EventHandler<PreviewFormCommandEventArgs>(this.OnPreviewSubmit));
}
private void OnPreviewSubmit(object sender, PreviewFormCommandEventArgs e)
{
    string errorMessage;
    bool result = this.DoVerify(out errorMessage);
    if (!result)
    {
        // cancel Submit operation
        e.Cancel = true;
        // display error message
        MessageBox.Show(errorMessage);
    }
}
internal bool DoVerify(out string errorMessage)
{
    // Do custom verification and return true to indicate that
    // validation check has passed; otherwise return false and
    // populate errorMessage argument
}

Usare i comandi e gli eventi dell'infrastruttura dei moduli

L'infrastruttura del modulo espone molti comandi che possono essere eseguiti in un modulo. Tra questi comandi vi sono:

  • FormsCommand.Submit, che consente di salvare l'istanza di destinazione del modulo.

  • FormsCommand.SubmitAndClose, che consente di salvare l'istanza di destinazione del modulo e chiude il modulo.

  • FormsCommand.Refresh, che ripete la query per l'istanza di destinazione del modulo.

  • FormCommands.Cancel, che rimuove tutte le modifiche e chiude il modulo.

Ciascuno di questi comandi è racchiuso tra eventi che vengono generati prima e dopo l'esecuzione del comando.

Prima del comando, vengono generati i seguenti eventi:

  • L'evento FormEvents.PreviewSubmit viene generato prima del comando FormCommand.Submit , mentre l'evento FormEvents.Submitted viene generato dopo il comando FormCommand.Submit .

  • L'evento FormEvents.PreviewRefresh viene generato prima del comando FormCommands.Refresh , mentre il comando FormCommand.Refreshed viene generato dopo il comando FormCommand.Submit .

  • L'evento FormEvents.PreviewCancel viene generato prima del comando FormCommands.Cancel , mentre l'evento FormCommand.Canceled viene generato dopo il comando FormCommand.Cancel .

Gli eventi di anteprima consentono di passare un oggetto PreviewFormCommandEventArgs . L'oggetto contiene una proprietà mutevole Cancel che impedisce l'esecuzione del comando corrispondente quando la proprietà è impostata su true.

Gli eventi post-comando passano un oggetto FormCommandExecutedEventArgs . Questo oggetto contiene una proprietà Result che indica se l'esecuzione del comando è avvenuta, è stata annullata o ha causato un errore. In caso di errore, la proprietà Error dell'oggetto FormCommandExecutedEventArgs farà riferimento all'eccezione che fornisce informazioni sull'errore.

È possibile abilitare, disabilitare ed eseguire comandi del modulo sia a livello di codice che dichiarativi.

Per attivare i comandi di un modulo a livello di codice, stabilire una CommandBinding tra il modulo e il comando correlato.

Nell'esempio riportato di seguito viene instaurata un'associazione del comando tra il modulo e un comando Refresh e vengono definiti due gestori per questo comando. Il primo gestore restituisce l'indicazione se il comando Refresh possa essere o meno eseguito, mentre il secondo gestore contiene effettivamente l'implementazione del comando Refresh :


    public class MyForm : UserControl
    {
        public MyForm()
        {
            // do standard initialization
            // establish CommandBinding for Refresh command
            this.CommandBindings.Add(
                new CommandBinding(FormCommands.Refresh, this.ExecuteRefresh, this.CanExecuteRefresh));
        }
        private void CanExecuteRefresh(
              object sender,
              CanExecuteRoutedEventArgs e)
        {
            // put your logic that determines whether Refresh
// can be executed here
            bool canExecute = true;
            BindableDataItem dataItem = this.DataContext as BindableDataItem;
            if (dataItem)
            {
                canExecute = dataItem["Status"] != "New";
            }
            e.CanExecute = canExecute;
        }
        private void ExecuteRefresh(
            object sender,
            ExecutedRoutedEventArgs e)
        {
            // here is placeholder for the code that has do be
// executed upon running Refresh command
        }
    }

È inoltre possibile definire in modo dichiarativo i gestori per i comandi del modulo. A questo scopo, è necessario distribuire un oggetto Regola che utilizzi un RoutedCommandTrigger. Nell'esempio di codice riportato di seguito viene illustrato come definire i gestori in modo dichiarativo:


    <scwpf:BusinessLogic.Rules>
        <scwpf:RuleCollection>
            <scwpf:Rule>
                <scwpf:Rule.Triggers>
                    <scwpf:RoutedCommandTrigger
RoutedCommand="{x:Static scwpf:FormCommands.Refresh}"/>
                </scwpf:Rule.Triggers>
                <scwpf:Rule.Conditions>
                    <scwpf:PropertyMatchCondition
                        Binding="{Binding Status}"
                        Value="New"
                        Operation="NotEquals" />
                </scwpf:Rule.Conditions>
                <!-- Use RuleAction objects to define the logic that executed
                upon running Refresh command; this can be left empty -->
            </scwpf:Rule>
        </scwpf:RuleCollection>
    </scwpf:BusinessLogic.Rules>

Passaggi successivi