Vytváření vizualizérů ladicího programu sady Visual Studio
Vizualizéry ladicího programu jsou funkce sady Visual Studio, která poskytuje vlastní vizualizaci proměnných nebo objektů konkrétního typu .NET během ladicí relace.
Vizualizéry ladicího programu jsou přístupné z datového tipu, který se zobrazí při najetí myší na proměnnou nebo z oken Automatické hodnoty, Místní hodnoty a Kukátku:
Začínáme
Postupujte podle oddílu Vytvořit projekt rozšíření v části Začínáme.
Pak přidejte rozšiřující třídu DebuggerVisualizerProvider
a použijte na VisualStudioContribution
ni atribut:
/// <summary>
/// Debugger visualizer provider class for <see cref="System.String"/>.
/// </summary>
[VisualStudioContribution]
internal class StringDebuggerVisualizerProvider : DebuggerVisualizerProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="StringDebuggerVisualizerProvider"/> class.
/// </summary>
/// <param name="extension">Extension instance.</param>
/// <param name="extensibility">Extensibility object.</param>
public StringDebuggerVisualizerProvider(StringDebuggerVisualizerExtension extension, VisualStudioExtensibility extensibility)
: base(extension, extensibility)
{
}
/// <inheritdoc/>
public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new("My string visualizer", typeof(string));
/// <inheritdoc/>
public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
{
string targetObjectValue = await visualizerTarget.ObjectSource.RequestDataAsync<string>(jsonSerializer: null, cancellationToken);
return new MyStringVisualizerControl(targetObjectValue);
}
}
Předchozí kód definuje nový vizualizér ladicího programu, který se vztahuje na objekty typu string
:
- Vlastnost
DebuggerVisualizerProviderConfiguration
definuje zobrazovaný název vizualizéru a podporovaný typ .NET. - Metoda
CreateVisualizerAsync
je vyvolána sadou Visual Studio, když uživatel požádá o zobrazení vizualizéru ladicího programu pro určitou hodnotu.CreateVisualizerAsync
pomocí objektuVisualizerTarget
načte hodnotu, která se má vizualizovat, a předá ji vlastnímu vzdálenému uživatelskému ovládacímu prvku (odkaz na dokumentaci k vzdálenému uživatelskému rozhraní ). Vzdálený uživatelský ovládací prvek se pak vrátí a zobrazí se v automaticky otevírané okně v sadě Visual Studio.
Cílení na více typů
Vlastnost konfigurace umožňuje vizualizéru cílit na více typů, pokud je to vhodné. Dokonalým příkladem je Vizualizér datové sady, který podporuje vizualizaci DataSet
objektů , DataView
DataTable
, a DataViewManager
objektů. Tato funkce usnadňuje vývoj rozšíření, protože podobné typy můžou sdílet stejné uživatelské rozhraní, zobrazit modely a zdroj objektů vizualizéru.
/// <inheritdoc/>
public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new DebuggerVisualizerProviderConfiguration(
new VisualizerTargetType("DataSet Visualizer", typeof(System.Data.DataSet)),
new VisualizerTargetType("DataTable Visualizer", typeof(System.Data.DataTable)),
new VisualizerTargetType("DataView Visualizer", typeof(System.Data.DataView)),
new VisualizerTargetType("DataViewManager Visualizer", typeof(System.Data.DataViewManager)));
/// <inheritdoc/>
public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
{
...
}
Zdroj objektu vizualizéru
Zdroj objektu vizualizéru je třída .NET, která je načtena ladicím programem v procesu ladění. Vizualizér ladicího programu může načíst data ze zdroje objektu vizualizéru pomocí metod vystavených metodou VisualizerTarget.ObjectSource
.
Výchozí zdroj objektu vizualizéru umožňuje vizualizérům ladicího programu načíst hodnotu objektu, která se má vizualizovat voláním RequestDataAsync<T>(JsonSerializer?, CancellationToken)
metody. Výchozí zdroj objektu vizualizéru používá k serializaci hodnoty Newtonsoft.Json a knihovny VisualStudio.Extensibility také pro deserializaci používají Newtonsoft.Json. Alternativně můžete použít RequestDataAsync(CancellationToken)
k načtení serializované hodnoty jako JToken
.
Pokud chcete vizualizovat typ .NET, který je nativně podporován Newtonsoft.Json, nebo chcete vizualizovat vlastní typ a můžete ho serializovat, předchozí pokyny stačí k vytvoření jednoduchého vizualizéru ladicího programu. Přečtěte si, jestli chcete podporovat složitější typy nebo používat pokročilejší funkce.
Použití vlastního zdroje objektů vizualizéru
Pokud typ, který chcete vizualizovat, nelze automaticky serializovat pomocí Newtonsoft.Json, můžete vytvořit vlastní zdroj vizualizéru objektu pro zpracování serializace.
- Vytvořte nový projekt knihovny tříd .NET, který cílí
netstandard2.0
na . V případě potřeby můžete cílit na konkrétnější verzi rozhraní .NET Framework nebo .NET (napříkladnet472
)net6.0
k serializaci objektu, který chcete vizualizovat. - Přidejte odkaz na balíček verze
DebuggerVisualizers
17.6 nebo novější. - Přidejte třídu, která rozšiřuje
VisualizerObjectSource
a přepisujeGetData
zápis serializované hodnoty do datovéhooutgoingData
target
proudu.
public class MyObjectSource : VisualizerObjectSource
{
/// <inheritdoc/>
public override void GetData(object target, Stream outgoingData)
{
MySerializableType result = Convert(match);
SerializeAsJson(outgoingData, result);
}
private static MySerializableType Convert(object target)
{
// Add your code here to convert target into a type serializable by Newtonsoft.Json
...
}
}
Použití vlastní serializace
Tuto metodu VisualizerObjectSource.SerializeAsJson
můžete použít k serializaci objektu pomocí Newtonsoft.Json na objekt Stream
bez přidání odkazu na Newtonsoft.Json do knihovny. Vyvolání SerializeAsJson
se načte prostřednictvím reflexe verze sestavení Newtonsoft.Json do laděného procesu.
Pokud potřebujete odkazovat na Newtonsoft.Json, měli byste použít stejnou verzi, na kterou odkazuje Microsoft.VisualStudio.Extensibility.Sdk
balíček, ale je vhodnější použít DataContract
a DataMember
atributy pro podporu serializace objektů místo toho, abyste se museli spoléhat na typy Newtonsoft.Json.
Alternativně můžete implementovat vlastní serializace (například binární serializace) zápis přímo do outgoingData
.
Přidání knihovny DLL zdrojového objektu vizualizéru do rozšíření
Upravte soubor přípony .csproj
ProjectReference
přidáním do projektu zdrojové knihovny vizualizéru objektů, který zajistí, že je zdrojová knihovna vizualizéru před zabaleným rozšířením sestavena.
Přidejte Content
také položku včetně knihovny DLL zdrojové knihovny vizualizéru do netstandard2.0
podsložky rozšíření.
<ItemGroup>
<Content Include="pathToTheObjectSourceDllBinPath\$(Configuration)\netstandard2.0\MyObjectSourceLibrary.dll" Link="netstandard2.0\MyObjectSourceLibrary.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyObjectSourceLibrary\MyObjectSourceLibrary.csproj" />
</ItemGroup>
Alternativně můžete použít net4.6.2
netcoreapp
podsložky, pokud jste vytvořili zdrojová knihovna vizualizéru, která cílí na rozhraní .NET Framework nebo .NET. Můžete dokonce zahrnout všechny tři podsložky s různými verzemi zdrojové knihovny vizualizéru objektů, ale je lepší cílit netstandard2.0
pouze na ně.
Měli byste se pokusit minimalizovat počet závislostí knihovny DLL zdrojové knihovny vizualizéru objektů. Pokud zdrojová knihovna vizualizéru obsahuje jiné závislosti než Microsoft.VisualStudio.DebuggerVisualizers a knihovny, které jsou již zaručeny pro načtení v procesu ladění, nezapomeňte tyto soubory DLL zahrnout do stejné podsložky jako knihovna DLL vizualizéru zdrojové knihovny DLL.
Aktualizace zprostředkovatele vizualizéru ladicího programu tak, aby používala vlastní zdroj objektů vizualizéru
Potom můžete aktualizovat DebuggerVisualizerProvider
konfiguraci tak, aby odkazovat na zdroj vlastního vizualizéru:
public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new("My visualizer", typeof(TypeToVisualize))
{
VisualizerObjectSourceType = new(typeof(MyObjectSource)),
};
public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
{
MySerializableType result = await visualizerTarget.ObjectSource.RequestDataAsync<MySerializableType>(jsonSerializer: null, cancellationToken);
return new MyVisualizerUserControl(result);
}
Práce s velkými a složitými objekty
Pokud načtení dat ze zdroje objektu vizualizéru nejde provést s jediným voláním RequestDataAsync
bez parametrů , můžete místo toho provést složitější výměnu zpráv se zdrojem vizualizéru objektu tak, že několikrát vyvoláte RequestDataAsync<TMessage, TResponse>(TMessage, JsonSerializer?, CancellationToken)
a odešlete různé zprávy do zdroje vizualizéru objektu. Zprávu i odpověď serializuje infrastruktura VisualStudio.Extensibility pomocí Newtonsoft.Json. Další přepsání RequestDataAsync
umožňují používat JToken
objekty nebo implementovat vlastní serializaci a deserializaci.
Pomocí různých zpráv můžete implementovat libovolný vlastní protokol, který načte informace ze zdroje vizualizéru objektů. Nejběžnějším případem použití této funkce je přerušení načítání potenciálně velkého objektu na několik volání, aby se zabránilo časovému RequestDataAsync
limitu.
Toto je příklad, jak můžete načíst obsah potenciálně velké kolekce po jedné položce:
for (int i = 0; ; i++)
{
MySerializableType? collectionEntry = await visualizerTarget.ObjectSource.RequestDataAsync<int, MySerializableType?>(i, jsonSerializer: null, cancellationToken);
if (collectionEntry is null)
{
break;
}
observableCollection.Add(collectionEntry);
}
Výše uvedený kód používá jednoduchý index jako zprávu pro RequestDataAsync
volání. Odpovídající zdrojový kód vizualizéru by přepsal metodu TransferData
(místo GetData
):
public class MyCollectionTypeObjectSource : VisualizerObjectSource
{
public override void TransferData(object target, Stream incomingData, Stream outgoingData)
{
var index = (int)DeserializeFromJson(incomingData, typeof(int))!;
if (target is MyCollectionType collection && index < collection.Count)
{
var result = Convert(collection[index]);
SerializeAsJson(outgoingData, result);
}
else
{
SerializeAsJson(outgoingData, null);
}
}
private static MySerializableType Convert(object target)
{
// Add your code here to convert target into a type serializable by Newtonsoft.Json
...
}
}
Výše uvedený zdroj vizualizéru využívá metodu VisualizerObjectSource.DeserializeFromJson
k deserializaci zprávy odeslané poskytovatelem vizualizéru z incomingData
.
Při implementaci zprostředkovatele vizualizéru ladicího programu, který provádí složitou interakci se zdrojem objektu vizualizéru, je obvykle lepší předat VisualizerTarget
vizualizéru RemoteUserControl
, aby se výměna zpráv stala asynchronně při načtení ovládacího prvku. VisualizerTarget
Předáním také můžete odesílat zprávy do zdroje objektu vizualizéru za účelem načtení dat na základě interakcí uživatele s uživatelským rozhraním vizualizéru.
public override Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
{
return Task.FromResult<IRemoteUserControl>(new MyVisualizerUserControl(visualizerTarget));
}
internal class MyVisualizerUserControl : RemoteUserControl
{
private readonly VisualizerTarget visualizerTarget;
public MyVisualizerUserControl(VisualizerTarget visualizerTarget)
: base(new MyDataContext())
{
this.visualizerTarget = visualizerTarget;
}
public override async Task ControlLoadedAsync(CancellationToken cancellationToken)
{
// Start querying the VisualizerTarget here
...
}
...
Otevírání vizualizérů ve Windows nástrojů
Ve výchozím nastavení se všechna rozšíření vizualizéru ladicího programu otevírají jako modální dialogová okna v popředí sady Visual Studio. Proto pokud chce uživatel pokračovat v interakci s integrovaným vývojovém prostředím ( IDE), musí být vizualizér zavřený. Pokud je však Style
vlastnost nastavena na ToolWindow
vlastnost DebuggerVisualizerProviderConfiguration
, bude vizualizér otevřen jako nemodální okno nástroje, které může zůstat otevřené ve zbytku ladicí relace. Pokud není deklarován žádný styl, použije se výchozí hodnota ModalDialog
.
public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new("My visualizer", typeof(TypeToVisualize))
{
Style = VisualizerStyle.ToolWindow
};
public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
{
// The control will be in charge of calling the RequestDataAsync method from the visualizer object source and disposing of the visualizer target.
return new MyVisualizerUserControl(visualizerTarget);
}
Kdykoli se vizualizér rozhodne být otevřen jako , ToolWindow
bude muset přihlásit k odběru StateChanged události VisualizerTarget
. Když je vizualizér otevřen jako okno nástroje, nezablokuje uživatele v pozastavení ladicí relace. Výše uvedená událost se tedy aktivuje ladicím programem při každé změně stavu cíle ladění. Autoři rozšíření Vizualizéru by měli věnovat zvláštní pozornost těmto oznámením, protože cíl vizualizéru je k dispozici pouze v případě, že je aktivní ladicí relace a cíl ladění je pozastavený. Pokud cíl vizualizéru není k dispozici, volání metod ObjectSource
selže s chybou VisualizerTargetUnavailableException
.
internal class MyVisualizerUserControl : RemoteUserControl
{
private readonly VisualizerDataContext dataContext;
#pragma warning disable CA2000 // Dispose objects before losing scope
public MyVisualizerUserControl(VisualizerTarget visualizerTarget)
: base(dataContext: new VisualizerDataContext(visualizerTarget))
#pragma warning restore CA2000 // Dispose objects before losing scope
{
this.dataContext = (VisualizerDataContext)this.DataContext!;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.dataContext.Dispose();
}
}
[DataContract]
private class VisualizerDataContext : NotifyPropertyChangedObject, IDisposable
{
private readonly VisualizerTarget visualizerTarget;
private MySerializableType? _value;
public VisualizerDataContext(VisualizerTarget visualizerTarget)
{
this.visualizerTarget = visualizerTarget;
visualizerTarget.StateChanged += this.OnStateChangedAsync;
}
[DataMember]
public MySerializableType? Value
{
get => this._value;
set => this.SetProperty(ref this._value, value);
}
public void Dispose()
{
this.visualizerTarget.Dispose();
}
private async Task OnStateChangedAsync(object? sender, VisualizerTargetStateNotification args)
{
switch (args)
{
case VisualizerTargetStateNotification.Available:
case VisualizerTargetStateNotification.ValueUpdated:
Value = await visualizerTarget.ObjectSource.RequestDataAsync<MySerializableType>(jsonSerializer: null, CancellationToken.None);
break;
case VisualizerTargetStateNotification.Unavailable:
Value = null;
break;
default:
throw new NotSupportedException("Unexpected visualizer target state notification");
}
}
}
}
Oznámení Available
se obdrží po RemoteUserControl
vytvoření a těsně před tím, než se zobrazí v nově vytvořeném okně nástroje vizualizéru. Pokud vizualizér zůstane otevřený, ostatní hodnoty lze přijímat pokaždé, VisualizerTargetStateNotification
když cíl ladění změní svůj stav. Oznámení ValueUpdated
se používá k označení, že poslední výraz otevřený vizualizérem byl úspěšně znovu vyhodnocen, kde ladicí program zastavil a měl by být aktualizován uživatelským rozhraním. Na druhé straně, kdykoli je cíl ladění obnoven nebo výraz nelze po zastavení znovu vyhodnotit, Unavailable
oznámení se obdrží.
Aktualizace vizualizované hodnoty objektu
Pokud VisualizerTarget.IsTargetReplaceable
je pravda, vizualizér ladicího programu může použít metodu ReplaceTargetObjectAsync
k aktualizaci hodnoty vizualizovaného objektu v procesu ladění.
Zdroj objektu vizualizéru musí přepsat metodu CreateReplacementObject
:
public override object CreateReplacementObject(object target, Stream incomingData)
{
// Use DeserializeFromJson to read from incomingData
// the new value of the object being visualized
...
return newValue;
}
Související obsah
Vyzkoušejte si ukázku RegexMatchDebugVisualizer
a podívejte se na tyto techniky v akci.