procedura dettagliata: Visualizzazione di completamento delle istruzioni
È possibile distribuire le funzionalità a livello di linguaggio quali il completamento delle istruzioni definizione di identificatori per il quale si desidera fornire il completamento e quindi su una sessione di completamento. È possibile definire il completamento delle istruzioni nel contesto di un servizio di linguaggio, oppure è possibile definire diventi proprietaria l'estensione di file e tipo di contenuto e completamento della visualizzazione solo per tale tipo, oppure è possibile attivare il completamento di un tipo di contenuto esistente (ad esempio “solo testo„). In questa procedura dettagliata viene illustrato come attivare il completamento di istruzioni per il tipo di contenuto “non crittografato„, che è il tipo di contenuto di un file di testo. Il tipo di contenuto “text„ è il predecessore di tutti gli altri tipi di contenuto, incluso il codice e file XML.
Il completamento di istruzioni in genere viene attivato digitando determinati caratteri, ad esempio, digitando l'inizio di un identificatore come “utilizzando„. In genere viene chiusa premendo BARRA SPAZIATRICE, TAB, o INVIO per eseguire il commit di una selezione. Le funzionalità di IntelliSense che vengono attivate digitando un carattere possono essere distribuite tramite un gestore comando per le sequenze di tasti (l'interfaccia di IOleCommandTarget ) e un provider del gestore che implementa l'interfaccia di IVsTextViewCreationListener . Per creare l'origine di completamento, che è l'elenco degli identificatori che partecipano al completamento, implementare l'interfaccia di ICompletionSource e un provider di origine di completamento (l'interfaccia di ICompletionSourceProvider ). I provider sono elementi gestiti (MEF) del Framework di estensibilità e sono responsabili dell'esportazione del database di origine e classi controller e di includere i servizi e i Service Broker, ad esempio, ITextStructureNavigatorSelectorService, che consente di spostarsi nel buffer di testo e ICompletionBroker, che genera la sessione di completamento.
In questa procedura dettagliata viene illustrato come implementare completamento delle istruzioni per un set specificato a livello di codice di identificatori. Nelle implementazioni complete, il servizio di linguaggio e la documentazione del linguaggio sono responsabili della visualizzazione del contenuto.
Prerequisiti
Per completare questa procedura dettagliata, è necessario installare Visual Studio 2010 SDK.
Nota
per ulteriori informazioni su Visual Studio SDK, vedere Cenni preliminari sull'estensione di Visual Studio.Per ulteriori informazioni su come scaricare Visual Studio SDK, vedere Centro per sviluppatori di estensibilità di Visual Studio il sito Web MSDN.
Creare un progetto MEF
Per creare un progetto MEF
Creare un progetto di classificatore editor. Assegnare alla soluzione CompletionTest.
Aprire il file source.extension.vsixmanifest nell'editor del manifesto VSIX.
Assicurarsi che la direzione di contenuto contenga un tipo di contenuto di Componente MEF e che percorso è impostato su CompletionTest.dll.
salvare e chiudere source.extension.vsixmanifest.
Aggiungere i seguenti riferimenti al progetto e assicurarsi che CopyLocal sia impostata su false:
Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Language.Intellisense
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.10.0
Microsoft.VisualStudio.Shell.Immutable.10.0
Microsoft.VisualStudio.TextManager.Interop
Eliminare i file di classe esistenti.
Distribuire il database di origine di completamento
L'origine di completamento è responsabile della raccolta del set di identificatori e dell'aggiunta di contenuto alla finestra di completamento quando si digita un trigger di completamento, ad esempio le prime lettere di un identificatore. In questo esempio, gli identificatori e le relative descrizioni sono specificati nel metodo diAugmentCompletionSession . In utilizza più realistici, si utilizzerebbe il parser del linguaggio per ottenere i token per compilare l'elenco di completamento.
Per distribuire il database di origine di completamento
Aggiungere il file di classe e denominarlo TestCompletionSource.
Aggiungere i seguenti elementi importati.
Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.ComponentModel.Composition Imports Microsoft.VisualStudio.Language.Intellisense Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Operations Imports Microsoft.VisualStudio.Utilities
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities;
Modificare la dichiarazione di classe per TestCompletionSource in modo che venga distribuito ICompletionSource.
Friend Class TestCompletionSource Implements ICompletionSource
internal class TestCompletionSource : ICompletionSource
Aggiungere campi privati per il provider di origine, il buffer di testo e un elenco di oggetti di Completion (che corrispondono agli identificatori che prenderanno parte alla sessione di completamento).
Private m_sourceProvider As TestCompletionSourceProvider Private m_textBuffer As ITextBuffer Private m_compList As List(Of Completion)
private TestCompletionSourceProvider m_sourceProvider; private ITextBuffer m_textBuffer; private List<Completion> m_compList;
Aggiungere un costruttore che imposta il provider di origine e il buffer. La classe di TestCompletionSourceProvider è definita nei passaggi successivi.
Public Sub New(ByVal sourceProvider As TestCompletionSourceProvider, ByVal textBuffer As ITextBuffer) m_sourceProvider = sourceProvider m_textBuffer = textBuffer End Sub
public TestCompletionSource(TestCompletionSourceProvider sourceProvider, ITextBuffer textBuffer) { m_sourceProvider = sourceProvider; m_textBuffer = textBuffer; }
Implementare il metodo di AugmentCompletionSession aggiungendo il completamento impostato contenente i completamenti che si desidera fornire il contesto. Ogni set di completamenti contiene un set di completamenti di Completion e corrisponde a una scheda della finestra di completamento. (Nei progetti di Visual Basic., schede di completamento della finestra sono denominate Comune e Tutto). Il metodo di FindTokenSpanAtPosition è definito nel passaggio successivo.
Private Sub AugmentCompletionSession(ByVal session As ICompletionSession, ByVal completionSets As IList(Of CompletionSet)) Implements ICompletionSource.AugmentCompletionSession Dim strList As New List(Of String)() strList.Add("addition") strList.Add("adaptation") strList.Add("subtraction") strList.Add("summation") m_compList = New List(Of Completion)() For Each str As String In strList m_compList.Add(New Completion(str, str, str, Nothing, Nothing)) Next str completionSets.Add(New CompletionSet( "Tokens", "Tokens", FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, Nothing)) End Sub
void ICompletionSource.AugmentCompletionSession(ICompletionSession session, IList<CompletionSet> completionSets) { List<string> strList = new List<string>(); strList.Add("addition"); strList.Add("adaptation"); strList.Add("subtraction"); strList.Add("summation"); m_compList = new List<Completion>(); foreach (string str in strList) m_compList.Add(new Completion(str, str, str, null, null)); completionSets.Add(new CompletionSet( "Tokens", //the non-localized title of the tab "Tokens", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); }
Il metodo seguente viene utilizzato per trovare la parola corrente dalla posizione del cursore.
Private Function FindTokenSpanAtPosition(ByVal point As ITrackingPoint, ByVal session As ICompletionSession) As ITrackingSpan Dim currentPoint As SnapshotPoint = (session.TextView.Caret.Position.BufferPosition) - 1 Dim navigator As ITextStructureNavigator = m_sourceProvider.NavigatorService.GetTextStructureNavigator(m_textBuffer) Dim extent As TextExtent = navigator.GetExtentOfWord(currentPoint) Return currentPoint.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive) End Function
private ITrackingSpan FindTokenSpanAtPosition(ITrackingPoint point, ICompletionSession session) { SnapshotPoint currentPoint = (session.TextView.Caret.Position.BufferPosition) - 1; ITextStructureNavigator navigator = m_sourceProvider.NavigatorService.GetTextStructureNavigator(m_textBuffer); TextExtent extent = navigator.GetExtentOfWord(currentPoint); return currentPoint.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive); }
implementare il metodo di Dispose():
Private m_isDisposed As Boolean Public Sub Dispose() Implements IDisposable.Dispose If Not m_isDisposed Then GC.SuppressFinalize(Me) m_isDisposed = True End If End Sub
private bool m_isDisposed; public void Dispose() { if (!m_isDisposed) { GC.SuppressFinalize(this); m_isDisposed = true; } }
implementare il provider di origine di completamento
Il provider di origine di completamento è l'elemento MEF che crea un'istanza del database di origine di completamento.
Per implementare il provider di origine di completamento
Aggiungere TestCompletionSourceProvider classe denominata che implementa ICompletionSourceProvider. Esportare questa classe con ContentTypeAttribute “di solo testo„ e NameAttribute “di completamento del test„.
<Export(GetType(ICompletionSourceProvider)), ContentType("plaintext"), Name("token completion")> Friend Class TestCompletionSourceProvider Implements ICompletionSourceProvider
[Export(typeof(ICompletionSourceProvider))] [ContentType("plaintext")] [Name("token completion")] internal class TestCompletionSourceProvider : ICompletionSourceProvider
Importare ITextStructureNavigatorSelectorService, che deve essere utilizzato per trovare la parola corrente nel database di origine di completamento.
<Import()> Friend Property NavigatorService() As ITextStructureNavigatorSelectorService
[Import] internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
Implementare il metodo di TryCreateCompletionSource per creare un'istanza del database di origine di completamento.
Public Function TryCreateCompletionSource(ByVal textBuffer As ITextBuffer) As ICompletionSource Implements ICompletionSourceProvider.TryCreateCompletionSource Return New TestCompletionSource(Me, textBuffer) End Function
public ICompletionSource TryCreateCompletionSource(ITextBuffer textBuffer) { return new TestCompletionSource(this, textBuffer); }
Implementare il provider di completamento del gestore comando
Il provider di completamento del gestore comando è derivato da IVsTextViewCreationListener, in ascolto di un evento di creazione della visualizzazione di testo e converte la visualizzazione da IVsTextView (che consente il comando aggiungere alla catena di comando della shell di Visual Studio) a ITextView. Poiché questa classe è un'esportazione MEF, è possibile utilizzarlo anche per includere i servizi che saranno necessari dal gestore comando stesso.
Per implementare il provider di completamento del gestore comando
aggiungere un file denominato TestCompletionCommandHandler.
Aggiungere le istruzioni using riportate di seguito.
Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.ComponentModel.Composition Imports System.Runtime.InteropServices Imports Microsoft.VisualStudio Imports Microsoft.VisualStudio.Editor Imports Microsoft.VisualStudio.Language.Intellisense Imports Microsoft.VisualStudio.OLE.Interop Imports Microsoft.VisualStudio.Shell Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.TextManager.Interop Imports Microsoft.VisualStudio.Utilities
using System; using System.ComponentModel.Composition; using System.Runtime.InteropServices; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities;
Aggiungere TestCompletionHandlerProvider classe denominata che implementa IVsTextViewCreationListener. Esportare questa classe con NameAttribute “del gestore di completamento token„, ContentTypeAttribute “di solo testo„ e TextViewRoleAttribute di Editable.
<Export(GetType(IVsTextViewCreationListener))> <Name("token completion handler")> <ContentType("plaintext")> <TextViewRole(PredefinedTextViewRoles.Editable)> Friend Class TestCompletionHandlerProvider Implements IVsTextViewCreationListener
[Export(typeof(IVsTextViewCreationListener))] [Name("token completion handler")] [ContentType("plaintext")] [TextViewRole(PredefinedTextViewRoles.Editable)] internal class TestCompletionHandlerProvider : IVsTextViewCreationListener
Importare IVsEditorAdaptersFactoryService, che consente di convertire da IVsTextView a ITextView, a ICompletionBrokere a SVsServiceProvider, che consente l'accesso ai servizi standard di accesso di Visual Studio.
<Import()> Friend AdapterService As IVsEditorAdaptersFactoryService = Nothing <Import()> Friend Property CompletionBroker() As ICompletionBroker <Import()> Friend Property ServiceProvider() As SVsServiceProvider
[Import] internal IVsEditorAdaptersFactoryService AdapterService = null; [Import] internal ICompletionBroker CompletionBroker { get; set; } [Import] internal SVsServiceProvider ServiceProvider { get; set; }
Implementare il metodo di VsTextViewCreated per creare un'istanza del gestore comando.
Public Sub VsTextViewCreated(ByVal textViewAdapter As IVsTextView) Implements IVsTextViewCreationListener.VsTextViewCreated Dim textView As ITextView = AdapterService.GetWpfTextView(textViewAdapter) If textView Is Nothing Then Return End If Dim createCommandHandler As Func(Of TestCompletionCommandHandler) = Function() New TestCompletionCommandHandler(textViewAdapter, textView, Me) textView.Properties.GetOrCreateSingletonProperty(createCommandHandler) End Sub
public void VsTextViewCreated(IVsTextView textViewAdapter) { ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); if (textView == null) return; Func<TestCompletionCommandHandler> createCommandHandler = delegate() { return new TestCompletionCommandHandler(textViewAdapter, textView, this); }; textView.Properties.GetOrCreateSingletonProperty(createCommandHandler); }
Implementazione del gestore comando di completamento
Poiché il completamento delle istruzioni è attivato dalle sequenze di tasti, è necessario implementare l'interfaccia di IOleCommandTarget per ricevere ed elaborare le sequenze di tasti che attivano, esegue il commit e chiudere la sessione di completamento.
Per implementare il gestore comando di completamento
Aggiungere TestCompletionCommandHandler classe denominata che implementa IOleCommandTarget.
Friend Class TestCompletionCommandHandler Implements IOleCommandTarget
internal class TestCompletionCommandHandler : IOleCommandTarget
Aggiungere campi privati per il gestore comando seguente (che viene passato al comando), la visualizzazione di testo, il provider del gestore comando (che consente di accedere a diversi servizi) e una sessione di completamento.
Private m_nextCommandHandler As IOleCommandTarget Private m_textView As ITextView Private m_provider As TestCompletionHandlerProvider Private m_session As ICompletionSession
private IOleCommandTarget m_nextCommandHandler; private ITextView m_textView; private TestCompletionHandlerProvider m_provider; private ICompletionSession m_session;
Aggiungere un costruttore che imposta la visualizzazione di testo e campi del provider e aggiunto il comando nella catena di comando.
Friend Sub New(ByVal textViewAdapter As IVsTextView, ByVal textView As ITextView, ByVal provider As TestCompletionHandlerProvider) Me.m_textView = textView Me.m_provider = provider 'add the command to the command chain textViewAdapter.AddCommandFilter(Me, m_nextCommandHandler) End Sub
internal TestCompletionCommandHandler(IVsTextView textViewAdapter, ITextView textView, TestCompletionHandlerProvider provider) { this.m_textView = textView; this.m_provider = provider; //add the command to the command chain textViewAdapter.AddCommandFilter(this, out m_nextCommandHandler); }
Implementare il metodo di QueryStatus passando il comando in avanti.
Public Function QueryStatus(ByRef pguidCmdGroup As Guid, ByVal cCmds As UInteger, ByVal prgCmds() As OLECMD, ByVal pCmdText As IntPtr) As Integer Implements IOleCommandTarget.QueryStatus Return m_nextCommandHandler.QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText) End Function
public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { return m_nextCommandHandler.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); }
Implementare il metodo Exec. Quando questo metodo riceve un tasto, deve effettuare una delle seguenti operazioni:
Consentire il carattere da scrivere nel buffer e quindi attivare o filtrare il completamento (i caratteri di stampa questa operazione).
Eseguire il commit del completamento, ma non consentire il carattere da scrivere nel buffer (vuoto, CATALOGARE e all'INVIO questa operazione quando una sessione di completamento video).
Consentire il comando essere passato al gestore successivo (tutti gli altri controlli.
Poiché questo metodo può visualizzare l'interfaccia utente, è necessario assicurarsi che non sia chiamata in un contesto di automazione chiamando IsInAutomationFunction.
Public Function Exec(ByRef pguidCmdGroup As Guid, ByVal nCmdID As UInteger, ByVal nCmdexecopt As UInteger, ByVal pvaIn As IntPtr, ByVal pvaOut As IntPtr) As Integer Implements IOleCommandTarget.Exec If VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider) Then Return m_nextCommandHandler.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut) End If 'make a copy of this so we can look at it after forwarding some commands Dim commandID As UInteger = nCmdID Dim typedChar As Char = Char.MinValue 'make sure the input is a char before getting it If pguidCmdGroup = VSConstants.VSStd2K AndAlso nCmdID = CUInt(VSConstants.VSStd2KCmdID.TYPECHAR) Then typedChar = CChar(ChrW(CUShort(Marshal.GetObjectForNativeVariant(pvaIn)))) End If 'check for a commit character If nCmdID = CUInt(VSConstants.VSStd2KCmdID.RETURN) OrElse nCmdID = CUInt(VSConstants.VSStd2KCmdID.TAB) OrElse (Char.IsWhiteSpace(typedChar) OrElse Char.IsPunctuation(typedChar)) Then 'check for a a selection If m_session IsNot Nothing AndAlso (Not m_session.IsDismissed) Then 'if the selection is fully selected, commit the current session If m_session.SelectedCompletionSet.SelectionStatus.IsSelected Then m_session.Commit() 'also, don't add the character to the buffer Return VSConstants.S_OK Else 'if there is no selection, dismiss the session m_session.Dismiss() End If End If End If 'pass along the command so the char is added to the buffer Dim retVal As Integer = m_nextCommandHandler.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut) Dim handled As Boolean = False If (Not typedChar.Equals(Char.MinValue)) AndAlso Char.IsLetterOrDigit(typedChar) Then If m_session Is Nothing OrElse m_session.IsDismissed Then ' If there is no active session, bring up completion Me.TriggerCompletion() m_session.Filter() Else 'the completion session is already active, so just filter m_session.Filter() End If handled = True ElseIf commandID = CUInt(VSConstants.VSStd2KCmdID.BACKSPACE) OrElse commandID = CUInt(VSConstants.VSStd2KCmdID.DELETE) Then 'redo the filter if there is a deletion If m_session IsNot Nothing AndAlso (Not m_session.IsDismissed) Then m_session.Filter() End If handled = True End If If handled Then Return VSConstants.S_OK End If Return retVal End Function
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); } //make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; //make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } //check for a commit character if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB || (char.IsWhiteSpace(typedChar) || char.IsPunctuation(typedChar))) { //check for a a selection if (m_session != null && !m_session.IsDismissed) { //if the selection is fully selected, commit the current session if (m_session.SelectedCompletionSet.SelectionStatus.IsSelected) { m_session.Commit(); //also, don't add the character to the buffer return VSConstants.S_OK; } else { //if there is no selection, dismiss the session m_session.Dismiss(); } } } //pass along the command so the char is added to the buffer int retVal = m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); bool handled = false; if (!typedChar.Equals(char.MinValue) && char.IsLetterOrDigit(typedChar)) { if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion { this.TriggerCompletion(); m_session.Filter(); } else //the completion session is already active, so just filter { m_session.Filter(); } handled = true; } else if (commandID == (uint)VSConstants.VSStd2KCmdID.BACKSPACE //redo the filter if there is a deletion || commandID == (uint)VSConstants.VSStd2KCmdID.DELETE) { if (m_session != null && !m_session.IsDismissed) m_session.Filter(); handled = true; } if (handled) return VSConstants.S_OK; return retVal; }
Il codice seguente è un metodo privato che attiva la sessione di completamento.
Private Function TriggerCompletion() As Boolean 'the caret must be in a non-projection location Dim caretPoint As SnapshotPoint? = m_textView.Caret.Position.Point.GetPoint(Function(textBuffer) ((Not textBuffer.ContentType.IsOfType("projection"))), PositionAffinity.Predecessor) If Not caretPoint.HasValue Then Return False End If m_session = m_provider.CompletionBroker.CreateCompletionSession(m_textView, caretPoint.Value.Snapshot.CreateTrackingPoint(caretPoint.Value.Position, PointTrackingMode.Positive), True) 'subscribe to the Dismissed event on the session AddHandler m_session.Dismissed, AddressOf OnSessionDismissed m_session.Start() Return True End Function
private bool TriggerCompletion() { //the caret must be in a non-projection location SnapshotPoint? caretPoint = m_textView.Caret.Position.Point.GetPoint( textBuffer => (!textBuffer.ContentType.IsOfType("projection")), PositionAffinity.Predecessor); if (!caretPoint.HasValue) { return false; } m_session = m_provider.CompletionBroker.CreateCompletionSession (m_textView, caretPoint.Value.Snapshot.CreateTrackingPoint(caretPoint.Value.Position, PointTrackingMode.Positive), true); //subscribe to the Dismissed event on the session m_session.Dismissed += this.OnSessionDismissed; m_session.Start(); return true; }
Il codice seguente è un metodo privato che disdice dall'evento di Dismissed .
Private Sub OnSessionDismissed(ByVal sender As Object, ByVal e As EventArgs) RemoveHandler m_session.Dismissed, AddressOf OnSessionDismissed m_session = Nothing End Sub
private void OnSessionDismissed(object sender, EventArgs e) { m_session.Dismissed -= this.OnSessionDismissed; m_session = null; }
Compilazione e test del codice
Per testare questo codice, compilare la soluzione di CompletionTest ed eseguirla nell'istanza sperimentale.
Per compilare e testare la soluzione di CompletionTest
Compilare la soluzione.
Quando si esegue il progetto nel debugger, una seconda istanza di Visual Studio viene creata un'istanza.
Creare un file di testo e digitare un testo che include la parola “add„.
Non appena digitato innanzitutto “a„ e “d„, un elenco contenente “add„ e “l'adattamento„ deve essere visualizzato. Si noti che l'aggiunta è selezionata. Quando si digita un'altra “d„, l'elenco deve contenere solo “add„, che è selezionata. È possibile applicare “add„ premendo BARRA SPAZIATRICE, CATALOGARE, o INVIO, o chiudere l'elenco digitando ESC o qualsiasi altro carattere.
Vedere anche
Attività
procedura dettagliata: Collegare un tipo di contenuto a un'estensione di file