Compare commits

...

19 Commits

Author SHA1 Message Date
Matthias Heil 36160f709c Deleted MicroSoft_IEnumerableVisualizer/IEnumerableViewModel.cs
Deleted    MicroSoft_IEnumerableVisualizer/IEnumerableVisualizer.cs
Deleted    MicroSoft_IEnumerableVisualizer/IEnumerableVisualizerControl.cs
Deleted    MicroSoft_IEnumerableVisualizer/IEnumerableVisualizerProvider.cs
Deleted    MicroSoft_IEnumerableVisualizer/IEnumerableVisualizerSource.cs
Deleted    MicroSoft_IEnumerableVisualizer/LatestDataAvailable.cs
2026-04-15 16:06:12 +02:00
Matthias Heil 5c9dded0a4 Deleted obsolete files 2026-04-15 16:03:09 +02:00
Matthias Heil 001b945ea1 NrxDebugVisualizer shows objects.
TODO: Update SceneTreeView after adding new objects
2026-04-15 15:59:42 +02:00
Matthias Heil 4549022153 Added Settings and ShowLogViewer option 2026-04-15 13:45:42 +02:00
Matthias Heil d3b5b624b5 Added necessary projects for DebugVisualizer 2026-04-15 10:09:49 +02:00
Matthias Heil ebe055ddfe Merge branch 'main' of https://hetzner.8837053.ch/matt/DebugVisualizerExtension 2026-04-15 09:06:47 +02:00
Matthias Heil 0a24d616b0 Rename MicroSoft_IEnumerableVisualizer(from IEnumerableVisualizer)
Modified   .gitignore
Modified   NrxDebugVisualizer/Program.cs
Modified   NrxDebugVisualizer/Views/MainWindow.axaml
2026-04-15 09:06:03 +02:00
Matthias Heil 40c72fc868 Updated avalonia packages 2026-04-14 15:26:54 +02:00
Matthias Heil 5ab615ffe2 nuget update 2026-04-10 16:59:24 +02:00
Matthias Heil 30aa75dc8d Added NrxDebugVisualizer, not working 2026-04-08 16:06:35 +02:00
Matthias Heil 0f145eaebd Rename NrxDebuggerVisualizerProvider/NrxDebugVisualizerProvider.csproj (from NrxDebuggerVisualizerProvider/NrxDebugVisualizer.csproj)
Modified   DebugVisualizerExtension.slnx
2026-04-08 14:26:29 +02:00
Matthias Heil b56974ec06 Copy TestServer to Visual Studio 2026 2026-04-08 14:24:40 +02:00
Matthias Heil 49e94b9b45 use own PipeSerializer
SendToVisualizerAsync uses await
2026-04-08 08:20:33 +02:00
Matthias Heil 96bb165f68 Deleted .idea 2026-04-08 08:00:13 +02:00
Matthias Heil 6f7a3a5e31 Works with NetJsonPipeSerializer 2026-04-07 13:38:08 +02:00
Matthias Heil 7b4bbd28a1 Not working 2026-04-07 12:21:37 +02:00
Matthias Heil 8038c76307 Enhanced VisualizerModel and Vector3VisualizerTest 2026-04-07 11:16:24 +02:00
Matthias Heil 26864a695f Rename DebugVisualizerExtension.slnx (from VisualizerExtensionExample.slnx) 2026-04-07 09:43:21 +02:00
Matthias Heil 38c916b86b Works for IEnumerable 2026-04-02 15:20:12 +02:00
45 changed files with 705 additions and 1484 deletions
+3
View File
@@ -33,6 +33,9 @@ bld/
[Ll]og/ [Ll]og/
[Ll]ogs/ [Ll]ogs/
# Jetbrains Rider
.idea/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/
# Uncomment if you have tasks that create the project's static files in wwwroot # Uncomment if you have tasks that create the project's static files in wwwroot
-15
View File
@@ -1,15 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/projectSettingsUpdater.xml
/modules.xml
/contentModel.xml
/.idea.VisualizerExtensionExample.iml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>
+33
View File
@@ -0,0 +1,33 @@
<Solution>
<Folder Name="/Avalonia/">
<Project Path="../Num.Roto.Visualization/Avalonia/Avalonia.PropertyGrid/Avalonia.PropertyGrid.csproj" />
<Project Path="../Num.Roto.Visualization/Avalonia/Controls/Controls.csproj" />
<Project Path="../Num.Roto.Visualization/Avalonia/LogViewer/LogViewer.csproj" />
<Project Path="../Num.Roto.Visualization/Tests/Avalonia/TestAvaloniaProportionalDock/TestAvaloniaProportionalDock.csproj" />
</Folder>
<Folder Name="/NamedPipes/">
<Project Path="NamedPipes/NamedPipes.csproj" Id="8d7b1151-8b57-4411-a361-47ed9c504a22" />
</Folder>
<Folder Name="/Num.Roto/">
<Project Path="../Num.Roto.Visualization/Base/Num.Roto.Visualization.Base.csproj" />
<Project Path="../Num.Roto.Visualization/Math/Num.Roto.Visualization.Math.csproj" />
<Project Path="../Num.Roto.Visualization/Models/Num.Roto.Visualization.Models/Num.Roto.Visualization.Models.csproj" />
<Project Path="../Num.Roto.Visualization/SceneGraph/Num.Roto.Visualization.SceneGraph.csproj" />
<Project Path="../Num.Roto.Visualization/Types/Num.Roto.Visualization.Types.csproj" />
<Project Path="../Num.Roto.Visualization/Vulkan/VulkanLib/Num.Roto.Visualization.VulkanLib.csproj" />
</Folder>
<Folder Name="/Test/">
<Project Path="TestClient/TestClient.csproj" />
<Project Path="TestServer/TestServer.csproj" Id="c81154ef-d854-4f1e-9d14-2cf674885291" />
<Project Path="Vector3VisualizerTest/Vector3VisualizerTest.csproj" />
</Folder>
<Folder Name="/Visualizer/">
<Project Path="NrxDebuggerVisualizerProvider/NrxDebugVisualizerProvider.csproj">
<Deploy />
</Project>
<Project Path="NrxDebugVisualizer/NrxDebugVisualizer.csproj" Id="807bb178-0fd6-4ae8-8c48-1088fa8a01fd">
<Deploy />
</Project>
<Project Path="NrxVisualizerObjectSource/NrxVisualizerObjectSource.csproj" />
</Folder>
</Solution>
@@ -1,2 +0,0 @@
[InternetShortcut]
URL=https://blog.elmah.io/creating-custom-debug-visualizers-for-visual-studio-2022/
@@ -1,527 +0,0 @@
// Decompiled with JetBrains decompiler
// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableViewModel
// Assembly: IEnumerableVisualizer.UI, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: 8D605BCB-B575-4FCD-B6C8-446C06150F34
// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\IEnumerableVisualizer.UI.dll
using Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Copilot;
using Microsoft.VisualStudio.Debugger.TabularDataUIShared;
using Microsoft.VisualStudio.Debugger.TabularDataUIShared.WPF;
using Microsoft.VisualStudio.DebuggerVisualizers;
using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
#nullable enable
namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer;
internal class IEnumerableViewModel : TabularDataViewModel, IExpressionCopilotHandler
{
private bool isMaximized;
public volatile int LoadedDebuggeeSideRowCount;
private bool _showTables;
private
#nullable disable
ObservableCollection<BaseTableViewModel> _tables;
internal object VisualizerObjectSource { get; }
internal VisualizerCommunicator Communicator { get; }
public override string VisualizerTelemetryPrefix
{
get => "VS/Diagnostics/Debugger/IEnumerableVisualizer/";
}
public bool IsMaximized
{
get => this.isMaximized;
set
{
if (this.isMaximized == value)
return;
this.isMaximized = value;
this.OnPropertyChanged(nameof (IsMaximized));
}
}
protected override async Task<bool> ExportDataToCSVAsync(string filePath, Encoding fileEncoding)
{
bool csvHelperAsync;
using (StreamWriter writer = new StreamWriter(filePath, false, fileEncoding))
csvHelperAsync = await this.ExportToCSVHelperAsync((TextWriter) writer, true);
return csvHelperAsync;
}
protected override async Task<bool> CopyDataToClipboardAsync()
{
StringBuilder sb = new StringBuilder();
bool clipboardAsync;
using (StringWriter writer = new StringWriter(sb))
{
int num = await this.ExportToCSVHelperAsync((TextWriter) writer, false) ? 1 : 0;
if (num != 0)
Clipboard.SetText(sb.ToString());
clipboardAsync = num != 0;
}
sb = (StringBuilder) null;
return clipboardAsync;
}
protected override async Task<bool> CopySelectedDataToClipboardAsync(
ICollection<Tuple<int, int>> cells,
int columnStartIndex,
int columnEndIndex,
int rowStartIndex,
int rowEndIndex,
bool isRectangularAndDense)
{
IEnumerableViewModel ienumerableViewModel = this;
List<ColumnInfo> list = ienumerableViewModel.SelectedTable.Columns.Where<ColumnInfo>((Func<ColumnInfo, bool>) (c => c.IsVisible && c.Index >= columnStartIndex && c.Index <= columnEndIndex)).ToList<ColumnInfo>();
StringBuilder sb = new StringBuilder();
using (StringWriter writer = new StringWriter(sb))
{
CSVWriter csvWriter = new CSVWriter(list.Select<ColumnInfo, string>((Func<ColumnInfo, string>) (c => c.DisplayName)), (TextWriter) writer);
IEnumerable<int> columnIndiciesToExport = list.Select<ColumnInfo, int>((Func<ColumnInfo, int>) (c => c.Index));
List<string> rowData = new List<string>(columnEndIndex - columnStartIndex);
for (int rowIndex = rowStartIndex; rowIndex <= rowEndIndex; ++rowIndex)
{
if (ienumerableViewModel.CancellableAsyncOperationToken.IsCancellationRequested || ienumerableViewModel.WindowClosingCancellationToken.IsCancellationRequested)
return false;
RowViewModel itemAt = (RowViewModel) ienumerableViewModel.SelectedTable.CollectionView.GetItemAt(rowIndex);
foreach (int columnIndex in columnIndiciesToExport)
{
if (isRectangularAndDense || cells.Contains(Tuple.Create<int, int>(rowIndex, columnIndex)))
rowData.Add(itemAt[columnIndex]);
else
rowData.Add(string.Empty);
}
await csvWriter.WriteRowAsync((IEnumerable<string>) rowData);
rowData.Clear();
}
csvWriter = (CSVWriter) null;
columnIndiciesToExport = (IEnumerable<int>) null;
rowData = (List<string>) null;
}
Clipboard.SetText(sb.ToString());
return true;
}
protected override async Task<bool> RefreshUnderlyingDataAsync(CancellationToken cancelToken)
{
IEnumerableViewModel iEnumerableViewModel = this;
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(new CancellationToken());
iEnumerableViewModel.LoadedDebuggeeSideRowCount = 0;
iEnumerableViewModel.IsStillLoadingDebuggeeSideRows = true;
iEnumerableViewModel.LastSearchQuery = (IVsSearchQuery) null;
if (cancelToken.IsCancellationRequested)
return false;
List<TableDisplayCache> tableListAsync = await iEnumerableViewModel.Communicator.GetTableListAsync(cancelToken, true);
int num = Math.Min(tableListAsync.Count, iEnumerableViewModel.Tables.Count);
int count = iEnumerableViewModel.Tables.Count;
for (int index = 0; index < num; ++index)
{
if (cancelToken.IsCancellationRequested)
return false;
TableDisplayCache tableModel = tableListAsync[index];
if (iEnumerableViewModel.Tables[index] is TableViewModel table)
{
IReadOnlyList<ColumnInfo> columns = table.Columns;
string name = table.Name;
table.SetModel(tableModel);
if (table.Name.Equals(name, StringComparison.Ordinal))
iEnumerableViewModel.RestoreColumnInfo(table, columns);
table.LoadRows();
}
else if (iEnumerableViewModel.Tables[index] is EmptyTableViewModel)
{
iEnumerableViewModel.Tables[index].ClearCache();
iEnumerableViewModel.Tables[index] = (BaseTableViewModel) new TableViewModel(iEnumerableViewModel, tableModel);
}
}
if (count > tableListAsync.Count)
{
for (int index = count - 1; index >= tableListAsync.Count; --index)
{
iEnumerableViewModel.Tables[index].ClearCache();
iEnumerableViewModel.Tables.RemoveAt(index);
}
}
else if (count < tableListAsync.Count)
{
for (int index = num; index < tableListAsync.Count; ++index)
{
TableDisplayCache tableModel = tableListAsync[index];
iEnumerableViewModel.Tables.Add((BaseTableViewModel) new TableViewModel(iEnumerableViewModel, tableModel));
}
}
await iEnumerableViewModel.InitializeVisualizerPropertiesAsync();
return true;
}
private void RestoreColumnInfo(
TableViewModel updatedTableViewModel,
IReadOnlyList<ColumnInfo> originalColumnInfos)
{
foreach (ColumnInfo columnInfo in originalColumnInfos.Skip<ColumnInfo>(1))
{
if (columnInfo.Index >= updatedTableViewModel.Columns.Count)
break;
ColumnInfo column = updatedTableViewModel.Columns[columnInfo.Index];
if (column.DisplayName.Equals(columnInfo.DisplayName, StringComparison.Ordinal))
{
bool flag = false;
if (columnInfo.ChildrenIndices != null && column.IsExpandable)
{
this.Communicator.ExpandColumn(updatedTableViewModel.Index, columnInfo.Index);
flag = true;
}
if (!columnInfo.IsVisible)
{
this.Communicator.HideColumn(updatedTableViewModel.Index, columnInfo.Index);
flag = true;
}
if (flag)
updatedTableViewModel.RefreshColumns();
}
}
}
private async Task<bool> ExportToCSVHelperAsync(TextWriter writer, bool shouldWriteHeaders)
{
IEnumerableViewModel ienumerableViewModel = this;
List<ColumnInfo> columns = ienumerableViewModel.SelectedTable.Columns.Where<ColumnInfo>((Func<ColumnInfo, bool>) (c => c.IsVisible)).ToList<ColumnInfo>();
if (columns.Count == 0)
{
await writer.WriteLineAsync();
return true;
}
CSVWriter csvWriter = new CSVWriter(columns.Select<ColumnInfo, string>((Func<ColumnInfo, string>) (c => c.DisplayName)), writer);
if (shouldWriteHeaders)
await csvWriter.WriteHeaderAsync();
IEnumerable<int> columnIndiciesToExport = (IEnumerable<int>) columns.Select<ColumnInfo, int>((Func<ColumnInfo, int>) (c => c.Index)).ToList<int>();
foreach (RowViewModel rowViewModel in (IEnumerable) ienumerableViewModel.SelectedTable.CollectionView)
{
CancellationToken cancellationToken = ienumerableViewModel.CancellableAsyncOperationToken;
if (!cancellationToken.IsCancellationRequested)
{
cancellationToken = ienumerableViewModel.WindowClosingCancellationToken;
if (!cancellationToken.IsCancellationRequested)
{
List<string> rowData = new List<string>();
foreach (int columnIndex in columnIndiciesToExport)
rowData.Add(rowViewModel[columnIndex]);
await csvWriter.WriteRowAsync((IEnumerable<string>) rowData);
continue;
}
}
return false;
}
return true;
}
public IEnumerableViewModel(
VisualizerObjectSourceClient visualizerObjectSource,
string visualizedExpression,
JoinableTaskFactory jtf)
: base(visualizedExpression, jtf)
{
this.VisualizerObjectSource = (object) (visualizerObjectSource ?? throw new ArgumentNullException(nameof (visualizerObjectSource)));
this.Tables = new ObservableCollection<BaseTableViewModel>();
this.Communicator = new VisualizerCommunicator(this);
}
public IEnumerableViewModel(
IAsyncVisualizerObjectProvider visualizerObjectSource,
JoinableTaskFactory jtf)
: base((string) null, jtf)
{
this.VisualizerObjectSource = (object) (visualizerObjectSource ?? throw new ArgumentNullException(nameof (visualizerObjectSource)));
this.Tables = new ObservableCollection<BaseTableViewModel>();
this.Communicator = new VisualizerCommunicator(this);
}
public event EventHandler<ContextMenuCommandArgs> ColumnsUpdateAttempted;
public event EventHandler<LatestDataAvailable> LatestDataAvailableUpdated;
public event EventHandler<PausedOperationInfo> ContinuePausedOperation;
public IViewHelper ViewHelper { get; set; }
internal UpdateTableRowResult UpdateTableRowResults(
TableViewModel table,
int rowIndex,
string[] values)
{
return this.Communicator.UpdateTableRowResults(table.Index, rowIndex, values);
}
internal TableDisplayCache InvalidateTableModel(TableViewModel table, out bool succeeded)
{
return this.Communicator.InvalidateTableCache(table.Index, out succeeded);
}
internal bool IsColumnLocked(ColumnInfo columnInfo)
{
return this.Communicator.IsColumnLocked(columnInfo);
}
internal void ExpandColumn(int columnIndex)
{
this.ContextMenuCommandHelper(Resources.G_ExpandColumnError, (Func<(bool, bool)>) (() =>
{
SuccessResult successResult = this.Communicator.ExpandColumn(this.SelectedTableIndex, columnIndex);
TelemetryUtilities.LogVisualizerTelemetry((TabularDataViewModel) this, TelemetryConstants.ExpandColumnCompletedEvent, TelemetryConstants.ExpandColumnEvent, (IEnumerable<(string, string)>) new List<(string, string)>()
{
(TelemetryConstants.PropertyResult, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) successResult.WasSuccessful)))
});
return (successResult.WasSuccessful, true);
}));
}
internal void HideColumn(int columnIndex)
{
this.ContextMenuCommandHelper(Resources.G_HideColumnError, (Func<(bool, bool)>) (() =>
{
SuccessResult successResult = this.Communicator.HideColumn(this.SelectedTableIndex, columnIndex);
if (successResult.WasSuccessful)
this.SelectedTable.Columns[columnIndex].IsVisible = false;
TelemetryUtilities.LogVisualizerTelemetry((TabularDataViewModel) this, TelemetryConstants.HideColumnCompletedEvent, TelemetryConstants.HideColumnEvent, (IEnumerable<(string, string)>) new List<(string, string)>()
{
(TelemetryConstants.PropertyResult, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) successResult.WasSuccessful)))
});
return (successResult.WasSuccessful, false);
}));
}
internal void HideChildren(int columnIndex)
{
this.ContextMenuCommandHelper(Resources.G_HideColumnsError, (Func<(bool, bool)>) (() =>
{
SuccessResult successResult = this.Communicator.HideChildren(this.SelectedTableIndex, columnIndex);
if (successResult.WasSuccessful)
{
foreach (int childrenIndex in this.SelectedTable.Columns[columnIndex].ChildrenIndices)
this.SelectedTable.Columns[childrenIndex].IsVisible = false;
}
TelemetryUtilities.LogVisualizerTelemetry((TabularDataViewModel) this, TelemetryConstants.HideChildrenCompletedEvent, TelemetryConstants.HideChildrenEvent, (IEnumerable<(string, string)>) new List<(string, string)>()
{
(TelemetryConstants.PropertyResult, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) successResult.WasSuccessful)))
});
return (successResult.WasSuccessful, false);
}));
}
private void ContextMenuCommandHelper(string errorMessage, Func<(bool, bool)> customOperation)
{
(bool wasSuccessful, bool flag) = customOperation();
if (wasSuccessful & flag)
{
this.SelectedTable.RefreshColumns();
this.SelectedTable.InvalidateTableModel();
}
EventHandler<ContextMenuCommandArgs> columnsUpdateAttempted = this.ColumnsUpdateAttempted;
if (columnsUpdateAttempted == null)
return;
columnsUpdateAttempted((object) this, new ContextMenuCommandArgs(wasSuccessful, errorMessage));
}
public DataObjectTypeName DataObjectType { get; private set; }
public bool ShowTables
{
get => this._showTables;
set
{
if (this._showTables == value)
return;
this._showTables = value;
this.OnPropertyChanged(nameof (ShowTables));
}
}
public ObservableCollection<BaseTableViewModel> Tables
{
get => this._tables;
set
{
if (this._tables == value)
return;
this._tables = value;
this.OnPropertyChanged(nameof (Tables));
}
}
public TableViewModel SelectedTable => this.Tables[this.SelectedTableIndex] as TableViewModel;
public override ITableViewModel SelectedSimplifiedTable => (ITableViewModel) this.SelectedTable;
public async Task StartFetchingRemainingRowsAsync(CancellationToken cancellationToken)
{
IEnumerableViewModel sender = this;
bool shouldExit = false;
sender.IsStillLoadingDebuggeeSideRows = true;
while (!shouldExit)
{
CancellationToken cancellationToken1 = sender.WindowClosingCancellationToken;
if (cancellationToken1.IsCancellationRequested || cancellationToken.IsCancellationRequested)
break;
int newlyLoadedRowCount = (await sender.Communicator.AdvanceIEnumerableDataSourceAsync(5000, cancellationToken)).Value;
if (newlyLoadedRowCount != 5000)
shouldExit = true;
await Task.Yield();
cancellationToken1 = sender.WindowClosingCancellationToken;
if (!cancellationToken1.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
{
TableViewModel selectedTable = sender.SelectedTable;
sender.LoadedDebuggeeSideRowCount += newlyLoadedRowCount;
if (sender.LoadedDebuggeeSideRowCount > 1000000)
{
sender.LoadedDebuggeeSideRowCount = 1000000;
sender.StatusBarMessage = string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.G_MaxRowsReached);
shouldExit = true;
}
if (shouldExit)
{
sender.IsStillLoadingDebuggeeSideRows = false;
IEnumerableViewModel viewModel = sender;
string debuggeeSideRowsEvent = TelemetryConstants.FinishedLoadingDebuggeeSideRowsEvent;
List<(string, string)> valueTupleList1 = new List<(string, string)>();
valueTupleList1.Add((TelemetryConstants.PropertyLoadedDebuggeeSideRowCount, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) sender.LoadedDebuggeeSideRowCount))));
valueTupleList1.Add((TelemetryConstants.PropertyCurrentTableRowsCount, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) selectedTable?.TableRows?.Count.GetValueOrDefault()))));
List<(string, string)> valueTupleList2 = valueTupleList1;
string tableColumnsCount = TelemetryConstants.PropertyCurrentTableColumnsCount;
object[] objArray = new object[1];
TableViewModel tableViewModel = selectedTable;
objArray[0] = (object) (tableViewModel != null ? tableViewModel.ColumnCount : 0);
string str = FormattableString.Invariant(FormattableStringFactory.Create("{0}", objArray));
(string, string) valueTuple = (tableColumnsCount, str);
valueTupleList2.Add(valueTuple);
List<(string, string)> properties = valueTupleList1;
await TelemetryUtilities.LogVisualizerTelemetryAsync((TabularDataViewModel) viewModel, debuggeeSideRowsEvent, (IEnumerable<(string, string)>) properties);
if (sender.IsCancellableAsyncOperationInProgress)
{
EventHandler<PausedOperationInfo> continuePausedOperation = sender.ContinuePausedOperation;
if (continuePausedOperation != null)
continuePausedOperation((object) sender, new PausedOperationInfo(sender.AsyncOperationsTracker.LastUserAsyncOperation));
}
}
if (selectedTable?.TableRows != null && selectedTable.TableRows.Count < sender.LoadedDebuggeeSideRowCount)
{
int num = selectedTable.TableRows.Count + (sender.LoadedDebuggeeSideRowCount - selectedTable.TableRows.Count);
for (int count = selectedTable.TableRows.Count; count < num; ++count)
selectedTable.TableRows.Add(new RowViewModel((ITableViewModel) selectedTable, count));
}
EventHandler<LatestDataAvailable> availableUpdated = sender.LatestDataAvailableUpdated;
if (availableUpdated != null)
availableUpdated((object) sender, new LatestDataAvailable(sender.LoadedDebuggeeSideRowCount));
sender.RefreshSelectedRowsStatusBarText();
selectedTable = (TableViewModel) null;
}
}
}
public override void TableRowsHaveAvailableData(
int rowIndex,
int rowCount,
bool hideProgressOnCompletion)
{
if (this.SelectedTable?.TableRows != null)
{
for (int index = 0; index < rowCount; ++index)
{
this.SelectedTable.TableRows[rowIndex + index].HasData = true;
this.SelectedTable.TableRows[rowIndex + index].OnValuesChanged();
}
}
if (!hideProgressOnCompletion)
return;
this.AsyncOperationsTracker.SetAsyncOperationStatusFor(AsyncOperationsTracker.AsyncOperationType.FETCHING_PAGE, false);
}
public async Task InitializeAsync()
{
IEnumerableViewModel iEnumerableViewModel = this;
try
{
foreach (TableDisplayCache tableModel in await iEnumerableViewModel.Communicator.GetTableListAsync(CancellationToken.None))
iEnumerableViewModel.Tables.Add((BaseTableViewModel) new TableViewModel(iEnumerableViewModel, tableModel));
await iEnumerableViewModel.InitializeVisualizerPropertiesAsync();
}
catch (Exception ex)
{
iEnumerableViewModel.IsEnabled = false;
}
}
private async Task InitializeVisualizerPropertiesAsync()
{
IEnumerableViewModel ienumerableViewModel = this;
ienumerableViewModel.DataObjectType = ienumerableViewModel.Communicator.DataObjectType;
ienumerableViewModel.ShowTables = ienumerableViewModel.Tables.Count > 1;
ienumerableViewModel.IsEnabled = ienumerableViewModel.Tables.Any<BaseTableViewModel>();
if (!ienumerableViewModel.IsEnabled)
ienumerableViewModel.Tables.Add((BaseTableViewModel) new EmptyTableViewModel(ienumerableViewModel));
if (ienumerableViewModel.Tables.Count > 0)
ienumerableViewModel.SelectedTableIndex = 0;
await TelemetryUtilities.LogVisualizerTelemetryAsync((TabularDataViewModel) ienumerableViewModel, TelemetryConstants.InitializedEvent, (IEnumerable<(string, string)>) new List<(string, string)>()
{
(TelemetryConstants.PropertyTableCount, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) ienumerableViewModel.Tables.Count)))
});
}
public async Task<CopilotSessionStartResult> StartCopilotSessionAsync(
string currentExpression,
ExpressionReplaceCallback replaceCallback,
CopilotSessionType sessionType,
string additionalUserContext,
CancellationToken cancellationToken)
{
TableViewModel selectedTable = this.SelectedTable;
string type = selectedTable != null ? selectedTable.Columns.FirstOrDefault<ColumnInfo>()?.Type : (string) null;
return NullableHelpers.IsNullOrEmpty(type) || IEnumerableCopilotService.Instance == null ? IEnumerableCopilotService.FailedSessionStart : await IEnumerableCopilotService.Instance.StartCopilotAsync(sessionType, currentExpression, type, this.GetVisualizedExpressionTypeMembers(type), replaceCallback, additionalUserContext, cancellationToken);
}
public async Task<bool> UpdateSessionContextAsync(
Guid sessionId,
string updatedExpression,
string additionalUserContext,
CancellationToken cancellationToken)
{
TableViewModel selectedTable = this.SelectedTable;
string type = selectedTable != null ? selectedTable.Columns.FirstOrDefault<ColumnInfo>()?.Type : (string) null;
return !NullableHelpers.IsNullOrEmpty(type) && IEnumerableCopilotService.Instance != null && await IEnumerableCopilotService.Instance.UpdateSessionContextAsync(sessionId, updatedExpression, type, this.GetVisualizedExpressionTypeMembers(type), additionalUserContext, cancellationToken);
}
public Task<CopilotSessionStartResult> OpenSessionInChatAsync(
Guid sessionId,
CancellationToken cancellationToken)
{
return IEnumerableCopilotService.Instance == null ? Task.FromResult<CopilotSessionStartResult>(new CopilotSessionStartResult(false, new Guid?(sessionId))) : IEnumerableCopilotService.Instance.OpenSessionInChatAsync(sessionId, cancellationToken);
}
public Task ShowCopilotChatToolWindowAsync(CancellationToken cancellationToken)
{
return IEnumerableCopilotService.ShowCopilotChatToolWindowAsync(cancellationToken);
}
private IEnumerable<IEnumerableTypeDefinitionEntry> GetVisualizedExpressionTypeMembers(
string expressionTypeName)
{
return this.SelectedTable.Columns.Skip<ColumnInfo>(1).Select<ColumnInfo, IEnumerableTypeDefinitionEntry>((Func<ColumnInfo, IEnumerableTypeDefinitionEntry>) (c => new IEnumerableTypeDefinitionEntry(c.Type, PackageUtilities.TrimPrefix(c.DisplayName, expressionTypeName + ".", StringComparison.Ordinal))));
}
}
@@ -1,71 +0,0 @@
// Decompiled with JetBrains decompiler
// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizer
// Assembly: IEnumerableVisualizer.UI, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: 8D605BCB-B575-4FCD-B6C8-446C06150F34
// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\IEnumerableVisualizer.UI.dll
using Microsoft.VisualStudio.DebuggerVisualizers;
using Microsoft.VisualStudio.DebuggerVisualizers.Common;
using Microsoft.VisualStudio.OutOfProcessVisualizers;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Utilities;
using System;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
#nullable enable
namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer;
public class IEnumerableVisualizer : DialogDebuggerVisualizer
{
public IEnumerableVisualizer()
: base((FormatterPolicy) 1)
{
}
protected internal virtual void Show(
#nullable disable
IDialogVisualizerService windowService,
IVisualizerObjectProvider objectProvider)
{
if (!(windowService is IWin32Window win32Window))
throw new ApplicationException("This debugger does not support modal visualizers");
using (DpiAwareness.EnterDpiScope((DpiAwarenessContext) -4))
{
IEnumerableViewModel viewModel = new IEnumerableViewModel((IAsyncVisualizerObjectProvider) objectProvider, VsTaskLibraryHelper.WithPriority(ThreadHelper.JoinableTaskFactory, (VsTaskRunContext) 6));
viewModel.UINormalPriorityJTF.Run((Func<Task>) (async () => await viewModel.InitializeAsync()));
IEnumerableVisualizerControl visualizerControl1 = new IEnumerableVisualizerControl();
visualizerControl1.DataContext = (object) viewModel;
visualizerControl1.Debugger = (IVsDebugger) null;
visualizerControl1.AreDataTipsEnabled = false;
IEnumerableVisualizerControl visualizerControl2 = visualizerControl1;
OutOfProcessVisualizerModalDialogViewModel modalDialogViewModel = new OutOfProcessVisualizerModalDialogViewModel((OutOfProcessVisualizer) null, string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.G_TitlebarCaption, (object) viewModel.DataObjectType.ToString()), Guid.Empty, string.Empty, string.Empty, viewModel.UINormalPriorityJTF);
((OutOfProcessVisualizerViewModel) modalDialogViewModel).MainContent = (IDisposable) visualizerControl2;
((OutOfProcessVisualizerViewModel) modalDialogViewModel).DialogState = (DialogState) 1;
ModalVisualizerDialog visualizerDialog = new ModalVisualizerDialog();
((FrameworkElement) visualizerDialog).DataContext = (object) modalDialogViewModel;
Window window = (Window) visualizerDialog;
try
{
if (viewModel.FatalExceptionCaught)
return;
Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizer.ShowWpfDialog(window, win32Window.Handle);
}
catch (Exception ex)
{
if (viewModel.FatalExceptionCaught)
return;
throw;
}
}
}
private static void ShowWpfDialog(Window window, IntPtr hwndParent)
{
WindowExtensions.SetOwner(window, hwndParent);
window.ShowDialog();
}
}
@@ -1,525 +0,0 @@
// Decompiled with JetBrains decompiler
// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizerControl
// Assembly: IEnumerableVisualizer.UI, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: 8D605BCB-B575-4FCD-B6C8-446C06150F34
// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\IEnumerableVisualizer.UI.dll
using Microsoft.Internal.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Copilot;
using Microsoft.VisualStudio.Debugger.Interop;
using Microsoft.VisualStudio.Debugger.Interop.Internal;
using Microsoft.VisualStudio.Debugger.TabularDataUIShared;
using Microsoft.VisualStudio.Debugger.TabularDataUIShared.WPF;
using Microsoft.VisualStudio.DebuggerVisualizers;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.OutOfProcessVisualizers.CommonControls.TippingService;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Utilities;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Markup;
using System.Windows.Media;
#nullable enable
namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer;
public class IEnumerableVisualizerControl :
UserControl,
IViewHelper,
IVsWindowSearch,
IDisposable,
IComponentConnector,
IStyleConnector
{
private
#nullable disable
IEnumerableViewModel m_viewModel;
private readonly IVsWindowSearchHost m_windowSearchHost;
private readonly Guid m_searchCategory = new Guid("BA594F87-ABA8-4847-A2FA-5C16C2DB51F3");
private const string IEnumerableVisualizerOpenedSettingsStorePath = "IsUserAwareOfIEnumerableVisualizer";
private CancellationTokenSource m_hoverCancellationTokenSource = new CancellationTokenSource();
private readonly Guid m_guidCSharpLanguage = new Guid("{3f5162f8-07c6-11d3-9053-00c04fa302a1}");
private readonly Guid m_guidFSharpLanguage = new Guid("{ab4f38c9-b6e6-43ba-be3b-58080b2ccce3}");
private readonly Guid m_guidVBLanguage = new Guid("{3a12d0b8-c26c-11d0-b442-00a0244a1dd2}");
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal Border searchControlContainer;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal Label labelTable;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal TableComboBox comboBoxTable;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal ExportButton exportButton;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal SortableDataGrid emptyDataGridView;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal SortableDataGrid dataGridView;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal ProgressBar progressBar;
private bool _contentLoaded;
public required IVsDebugger Debugger { get; init; }
public required bool AreDataTipsEnabled { get; init; }
public IEnumerableVisualizerControl()
{
ThreadHelper.ThrowIfNotOnUIThread(".ctor");
this.InitializeComponent();
this.comboBoxTable.SelectionChanged += new SelectionChangedEventHandler(this.ComboBoxTable_SelectionChanged);
this.DataContextChanged += new DependencyPropertyChangedEventHandler(this.OnDataContextChanged);
this.dataGridView.SelectedCellsChanged += new SelectedCellsChangedEventHandler(this.DataGridView_SelectedCellsChanged);
if (ServiceProvider.GlobalProvider.GetService(typeof (SVsWindowSearchHostFactory)) is IVsWindowSearchHostFactory service)
{
this.m_windowSearchHost = service.CreateWindowSearchHost((object) this.searchControlContainer, (IDropTarget) null);
this.m_windowSearchHost.SetupSearch((IVsWindowSearch) this);
SearchControl descendant = ExtensionMethods.FindDescendant<SearchControl>((DependencyObject) this.searchControlContainer);
((Control) descendant).TabIndex = 0;
((UIElement) descendant).Focus();
}
VisualizersTippingService.Instance?.RecordVisualizerOpened("IsUserAwareOfIEnumerableVisualizer");
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
try
{
this.m_viewModel = (IEnumerableViewModel) e.NewValue;
this.comboBoxTable.TabularDataViewModel = (TabularDataViewModel) this.m_viewModel;
this.dataGridView.TabularDataViewModel = (TabularDataViewModel) this.m_viewModel;
this.emptyDataGridView.TabularDataViewModel = (TabularDataViewModel) this.m_viewModel;
this.dataGridView.SelectCellByIndex(0, 0, false);
this.m_viewModel.RefreshSelectedRowsStatusBarText();
this.m_viewModel.ColumnsUpdateAttempted += new EventHandler<ContextMenuCommandArgs>(this.ColumnsUpdateAttempted);
this.m_viewModel.ContinuePausedOperation += new EventHandler<PausedOperationInfo>(this.ResumeCancellableAsyncOperation);
this.m_viewModel.AsyncUserOperationStateChanged += new EventHandler<AsyncOperationStatus>(this.OnAsyncUserOperationStateChanged);
this.m_viewModel.SearchStarted += new EventHandler<SearchStartedEventArgs>(this.OnSearchStarted);
this.m_viewModel.ShowErrorMessage += new EventHandler<string>(this.ShowErrorMessageBox);
this.m_viewModel.UnderlyingDataChanged += new EventHandler(this.OnUnderlyingDataChanged);
if (this.m_viewModel.FatalExceptionCaught)
return;
VsTaskLibraryHelper.FileAndForget(this.m_viewModel.UINormalPriorityJTF.RunAsync((Func<Task>) (async () => await this.m_viewModel.StartFetchingRemainingRowsAsync(this.m_viewModel.CancellableAsyncOperationToken))), this.m_viewModel.VisualizerTelemetryPrefix + TelemetryConstants.FetchingRemainingRowsFileAndForgetEvent, (string) null, (Func<Exception, bool>) null);
}
catch (Exception ex)
{
if (!((sender is IEnumerableVisualizerControl visualizerControl ? visualizerControl.DataContext : (object) null) is TabularDataViewModel dataContext))
return;
dataContext.HandleFatalException(ex.GetType().FullName, ex.Message, ex.StackTrace);
TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(dataContext, ex, nameof (OnDataContextChanged));
}
}
private void ResumeCancellableAsyncOperation(object sender, PausedOperationInfo operationInfo)
{
try
{
ThreadHelper.ThrowIfNotOnUIThread(nameof (ResumeCancellableAsyncOperation));
if (!this.m_viewModel.IsCancellableAsyncOperationInProgress)
return;
switch (operationInfo.OperationType)
{
case AsyncOperationsTracker.AsyncOperationType.SORTING:
this.dataGridView.OnSorting((object) this, new DataGridSortingEventArgs(this.dataGridView.Columns[this.dataGridView.SortingColumnIndex]));
break;
case AsyncOperationsTracker.AsyncOperationType.EXPORTING:
this.exportButton.SelectedCommand.Command.Execute((object) this);
break;
case AsyncOperationsTracker.AsyncOperationType.FILTERING:
this.m_windowSearchHost.SearchAsync(this.m_viewModel.LastSearchQuery);
break;
case AsyncOperationsTracker.AsyncOperationType.COPYING:
this.dataGridView.ContextMenu_CopyAll((object) null, (EventArgs) null);
break;
case AsyncOperationsTracker.AsyncOperationType.COPYING_SELECTION:
this.dataGridView.ContextMenu_Copy((object) null, (EventArgs) null);
break;
}
}
catch (Exception ex)
{
if (!(sender is TabularDataViewModel viewModel))
return;
TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (ResumeCancellableAsyncOperation));
}
}
private void OnSearchStarted(object sender, SearchStartedEventArgs args)
{
try
{
this.PerformFilterOperation(args.SearchText);
}
catch (Exception ex)
{
if (!(sender is TabularDataViewModel viewModel))
return;
TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (OnSearchStarted));
}
}
private void PerformFilterOperation(string filter)
{
this.dataGridView.Filter(filter);
this.m_viewModel.RefreshSelectedRowsStatusBarText();
}
[SuppressMessage("Reliability", "VSSDK007:ThreadHelper.JoinableTaskFactory.RunAsync", Justification = "This is intentional. We are using FileAndForget to catch any issues that might appear down the road.")]
private void DataGridCell_MouseEnter(object sender, MouseEventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread(nameof (DataGridCell_MouseEnter));
if (!this.AreDataTipsEnabled)
return;
DataGridCell dataGridCell = sender as DataGridCell;
if (dataGridCell == null)
return;
IDebuggerInternal debuggerInternal = this.Debugger as IDebuggerInternal;
if (debuggerInternal == null)
return;
IEnumerableColumnHeaderViewModel colHeaderViewModel = dataGridCell.Column.Header as IEnumerableColumnHeaderViewModel;
if (colHeaderViewModel == null)
return;
CancellationToken cancellationToken = this.m_hoverCancellationTokenSource.Token;
VsTaskLibraryHelper.FileAndForget(ThreadHelper.JoinableTaskFactory.RunAsync((Func<Task>) (async () =>
{
await Task.Delay(350, cancellationToken);
if (!dataGridCell.IsMouseOver || !Utils.TryGetVisualizerExpressionContext(this.Debugger, out IDebugExpressionContext2 _) || !Utils.TryGetLanguageId(this.Debugger, out Guid _))
return;
Window window = Window.GetWindow((DependencyObject) this);
Point position = e.GetPosition((IInputElement) window);
Point devicePoint = DpiAwareness.LogicalToDevicePoint((Visual) window, position);
POINT point = new POINT()
{
x = (int) devicePoint.X,
y = (int) devicePoint.Y
};
Rect rect1 = dataGridCell.TransformToAncestor((Visual) window).TransformBounds(new Rect(0.0, 0.0, dataGridCell.ActualWidth, dataGridCell.ActualHeight));
Rect deviceRect = DpiAwareness.LogicalToDeviceRect((Visual) window, rect1);
RECT rect2 = new RECT()
{
left = (int) deviceRect.Left,
top = (int) deviceRect.Top,
bottom = (int) deviceRect.Bottom,
right = (int) deviceRect.Right
};
DataTipIdentity[] dataTipIdentityArray = new DataTipIdentity[1];
dataTipIdentityArray[0].moniker = "IEnumerableVisualizerDataTip";
dataTipIdentityArray[0].position = 1U;
IntPtr handle = new WindowInteropHelper(window).Handle;
string str = $"Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizerSource.GetObjectAt({this.m_viewModel.SelectedTableIndex},{((RowViewModel) dataGridCell.DataContext).RowIndex},{colHeaderViewModel.Index})";
ErrorHandler.ThrowOnFailure(debuggerInternal.DataTipManager.ShowDataTipAt(point, 100, rect2, (ulong) (long) handle, str, dataTipIdentityArray));
})), "VS/Diagnostics/Debugger/IEnumerableVisualizer/FileAndForget/ShowDataTip", (string) null, (Func<Exception, bool>) (ex => !(ex is OperationCanceledException)));
}
private void DataGridCell_MouseLeave(object sender, MouseEventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread(nameof (DataGridCell_MouseLeave));
this.m_hoverCancellationTokenSource.Cancel();
this.m_hoverCancellationTokenSource = new CancellationTokenSource();
}
private void OnUnderlyingDataChanged(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread(nameof (OnUnderlyingDataChanged));
this.RefreshTableSelection(this.comboBoxTable.SelectedItem, true);
if (((FrameworkElement) ExtensionMethods.FindDescendant<SearchControl>((DependencyObject) this.searchControlContainer)).DataContext is IVsUIDataSource dataContext)
dataContext.SetValue(SearchControlDataSource.PropertyNames.SearchText, (IVsUIObject) BuiltInPropertyValue.Create(string.Empty));
VsTaskLibraryHelper.FileAndForget(this.m_viewModel.UINormalPriorityJTF.RunAsync((Func<Task>) (async () => await this.m_viewModel.StartFetchingRemainingRowsAsync(this.m_viewModel.CancellableAsyncOperationToken))), this.m_viewModel.VisualizerTelemetryPrefix + TelemetryConstants.FetchingRemainingRowsFileAndForgetEvent, (string) null, (Func<Exception, bool>) null);
}
private void ComboBoxTable_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread(nameof (ComboBoxTable_SelectionChanged));
this.RefreshTableSelection(this.comboBoxTable.SelectedItem);
}
private void RefreshTableSelection(object comboBoxSelectedItem, bool shouldRefreshInitialModels = false)
{
if (!this.comboBoxTable.IsEnabled)
return;
if (!(comboBoxSelectedItem is TableViewModel table))
return;
try
{
this.UpdateColumns(table);
if (shouldRefreshInitialModels)
table.LoadInitialModels();
this.dataGridView.UnselectAll();
if (this.dataGridView.Items.Count > 0)
{
this.dataGridView.SelectCellByIndex(0, 0, false);
this.dataGridView.ScrollIntoView(this.dataGridView.Items[0]);
this.m_viewModel.RefreshSelectedRowsStatusBarText();
}
this.PerformFilterOperation(this.m_viewModel.LastSearchQuery?.SearchString);
}
catch (Exception ex)
{
table.LoadRows();
if (this.m_viewModel == null)
return;
TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry((TabularDataViewModel) this.m_viewModel, ex, nameof (RefreshTableSelection));
}
}
private void UpdateColumns(TableViewModel table)
{
this.dataGridView.ResetSortOrder();
this.dataGridView.Columns.Clear();
this.emptyDataGridView.Columns.Clear();
if (table == null)
return;
table.LoadRows();
SortableDataGrid sortableDataGrid = table.IsEmpty ? this.emptyDataGridView : this.dataGridView;
Style resource1 = sortableDataGrid.FindResource((object) "CellTextBlockStyle") as Style;
Style resource2 = sortableDataGrid.FindResource((object) "EditableCellTextBoxStyle") as Style;
int index = 0;
foreach (ColumnInfo column in (IEnumerable<ColumnInfo>) table.Columns)
{
IEnumerableColumnHeaderViewModel columnHeaderViewModel = new IEnumerableColumnHeaderViewModel(column, table.Index, this.m_viewModel);
ObservableCollection<DataGridColumn> columns = sortableDataGrid.Columns;
SortableDataGridColumn sortableDataGridColumn = new SortableDataGridColumn();
sortableDataGridColumn.Binding = (BindingBase) new Binding(RowViewModel.IndexToBindingPath(index))
{
Mode = BindingMode.TwoWay,
ValidatesOnNotifyDataErrors = true,
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
};
sortableDataGridColumn.Header = (object) columnHeaderViewModel;
sortableDataGridColumn.IsReadOnly = column.ReadOnly || table.IsColumnLocked(column);
sortableDataGridColumn.ElementStyle = resource1;
sortableDataGridColumn.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto);
sortableDataGridColumn.MaxWidth = 200.0;
sortableDataGridColumn.EditingElementStyle = resource2;
sortableDataGridColumn.Visibility = columnHeaderViewModel.IsVisible ? Visibility.Visible : Visibility.Collapsed;
sortableDataGridColumn.CanSort = this.m_viewModel.IsSortingEnabled;
sortableDataGridColumn.IsSorted = false;
columns.Add((DataGridColumn) sortableDataGridColumn);
++index;
}
}
private void OnAsyncUserOperationStateChanged(object sender, AsyncOperationStatus _)
{
try
{
foreach (SortableDataGridColumn sortableDataGridColumn in this.dataGridView.Columns.Select<DataGridColumn, SortableDataGridColumn>((Func<DataGridColumn, SortableDataGridColumn>) (c => c as SortableDataGridColumn)))
sortableDataGridColumn.CanSort = this.m_viewModel.IsSortingEnabled;
}
catch (Exception ex)
{
if (!(sender is TabularDataViewModel viewModel))
return;
TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (OnAsyncUserOperationStateChanged));
}
}
private void ColumnsUpdateAttempted(object sender, ContextMenuCommandArgs args)
{
try
{
if (args.WasSuccessful)
{
this.UpdateColumns(this.m_viewModel.SelectedTable);
}
else
{
string caption = string.Format((IFormatProvider) CultureInfo.InvariantCulture, Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_UpdateErrorCaption);
int num = (int) MessageBox.Show(args.ErrorMessage, caption, MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
catch (Exception ex)
{
(this.comboBoxTable.SelectedItem as TableViewModel).LoadRows();
if (!(sender is TabularDataViewModel viewModel))
return;
TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (ColumnsUpdateAttempted));
}
}
private void ShowErrorMessageBox(object sender, string message)
{
string caption = string.Format((IFormatProvider) CultureInfo.InvariantCulture, Microsoft.VisualStudio.Debugger.TabularDataUIShared.Resources.G_FailedSort);
int num = (int) MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
private void DataGridView_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
try
{
if (!(sender is DataGrid dataGrid))
return;
this.m_viewModel.SelectedRows = dataGrid.SelectedCells.Select<DataGridCellInfo, RowViewModel>((Func<DataGridCellInfo, RowViewModel>) (cell => (RowViewModel) cell.Item)).Distinct<RowViewModel>();
this.m_viewModel.RefreshSelectedRowsStatusBarText();
}
catch (Exception ex)
{
if (!((sender is SortableDataGrid sortableDataGrid ? sortableDataGrid.DataContext : (object) null) is TabularDataViewModel dataContext))
return;
TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(dataContext, ex, nameof (DataGridView_SelectedCellsChanged));
}
}
public bool PromptToKeepChanges(string message)
{
string updateErrorCaption = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_UpdateErrorCaption;
return MessageBox.Show(message, updateErrorCaption, MessageBoxButton.OKCancel, MessageBoxImage.Exclamation) == MessageBoxResult.OK;
}
public IVsSearchTask CreateSearch(
uint dwCookie,
IVsSearchQuery pSearchQuery,
IVsSearchCallback pSearchCallback)
{
return (IVsSearchTask) new SortableDataGridSearchTask((TabularDataViewModel) this.m_viewModel, dwCookie, pSearchQuery, pSearchCallback);
}
public void ClearSearch()
{
if (this.m_viewModel.IsCancellableAsyncOperationInProgress && this.m_viewModel.AsyncOperationsTracker.LastUserAsyncOperation == AsyncOperationsTracker.AsyncOperationType.FILTERING)
this.m_viewModel.CancelButtonCommand.Execute((object) this);
this.m_viewModel.LastSearchQuery = (IVsSearchQuery) null;
this.PerformFilterOperation((string) null);
}
public void ProvideSearchSettings(IVsUIDataSource pSearchSettings)
{
SearchSettingsDataSource settingsDataSource = pSearchSettings as SearchSettingsDataSource;
settingsDataSource.SearchStartType = (VSSEARCHSTARTTYPE) 2;
settingsDataSource.SearchProgressType = (VSSEARCHPROGRESSTYPE) 2;
settingsDataSource.ControlMaxWidth = uint.MaxValue;
string gFilter;
string str1 = gFilter = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_Filter;
settingsDataSource.SearchStartTooltip = gFilter;
string str2;
string str3 = str2 = str1;
settingsDataSource.SearchTooltip = str2;
settingsDataSource.SearchWatermark = str3;
settingsDataSource.SearchClearTooltip = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_ClearFilter;
settingsDataSource.SearchStopTooltip = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_StopFilter;
settingsDataSource.UseDefaultThemeColors = false;
}
public bool OnNavigationKeyDown(uint dwNavigationKey, uint dwModifiers) => false;
public bool SearchEnabled => this.m_viewModel.IsSearchControlEnabled;
public Guid Category => this.m_searchCategory;
public IVsEnumWindowSearchFilters SearchFiltersEnum => (IVsEnumWindowSearchFilters) null;
public IVsEnumWindowSearchOptions SearchOptionsEnum => (IVsEnumWindowSearchOptions) null;
public void Dispose()
{
if (this.m_viewModel == null)
throw new InvalidOperationException("The IEnumerable Visualizer's view model should never be null.");
this.comboBoxTable.SelectionChanged -= new SelectionChangedEventHandler(this.ComboBoxTable_SelectionChanged);
this.DataContextChanged -= new DependencyPropertyChangedEventHandler(this.OnDataContextChanged);
this.dataGridView.SelectedCellsChanged -= new SelectedCellsChangedEventHandler(this.DataGridView_SelectedCellsChanged);
this.m_viewModel.ColumnsUpdateAttempted -= new EventHandler<ContextMenuCommandArgs>(this.ColumnsUpdateAttempted);
this.m_viewModel.ContinuePausedOperation -= new EventHandler<PausedOperationInfo>(this.ResumeCancellableAsyncOperation);
this.m_viewModel.AsyncUserOperationStateChanged -= new EventHandler<AsyncOperationStatus>(this.OnAsyncUserOperationStateChanged);
this.m_viewModel.ShowErrorMessage -= new EventHandler<string>(this.ShowErrorMessageBox);
this.m_viewModel.SearchStarted -= new EventHandler<SearchStartedEventArgs>(this.OnSearchStarted);
this.m_viewModel.UnderlyingDataChanged -= new EventHandler(this.OnUnderlyingDataChanged);
this.m_viewModel.WindowClosing();
IEnumerableCopilotSessionManager.Instance.ClearAllActiveSessions();
foreach (BaseTableViewModel table in (Collection<BaseTableViewModel>) this.m_viewModel.Tables)
{
table.TableRows?.Clear();
table.ClearCache();
}
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
public void InitializeComponent()
{
if (this._contentLoaded)
return;
this._contentLoaded = true;
Application.LoadComponent((object) this, new Uri("/IEnumerableVisualizer.UI;V18.0.0.0;component/ienumerablevisualizercontrol.xaml", UriKind.Relative));
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Delegate _CreateDelegate(Type delegateType, string handler)
{
return Delegate.CreateDelegate(delegateType, (object) this, handler);
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
[EditorBrowsable(EditorBrowsableState.Never)]
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
void IComponentConnector.Connect(int connectionId, object target)
{
switch (connectionId)
{
case 2:
this.searchControlContainer = (Border) target;
break;
case 3:
this.labelTable = (Label) target;
break;
case 4:
this.comboBoxTable = (TableComboBox) target;
break;
case 5:
this.exportButton = (ExportButton) target;
break;
case 6:
this.emptyDataGridView = (SortableDataGrid) target;
break;
case 7:
this.dataGridView = (SortableDataGrid) target;
break;
case 8:
this.progressBar = (ProgressBar) target;
break;
default:
this._contentLoaded = true;
break;
}
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
[EditorBrowsable(EditorBrowsableState.Never)]
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
void IStyleConnector.Connect(int connectionId, object target)
{
if (connectionId != 1)
return;
((Style) target).Setters.Add((SetterBase) new EventSetter()
{
Event = UIElement.MouseEnterEvent,
Handler = (Delegate) new MouseEventHandler(this.DataGridCell_MouseEnter)
});
((Style) target).Setters.Add((SetterBase) new EventSetter()
{
Event = UIElement.MouseLeaveEvent,
Handler = (Delegate) new MouseEventHandler(this.DataGridCell_MouseLeave)
});
}
}
@@ -1,72 +0,0 @@
// Decompiled with JetBrains decompiler
// Type: Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders.IEnumerableVisualizerProvider
// Assembly: Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: 98A3FADF-73D8-4308-9C72-EE68F73E4141
// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders.dll
using Microsoft.Internal.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Debugger.IEnumerableVisualizer;
using Microsoft.VisualStudio.Debugger.TabularDataUIShared.WPF;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers;
using Microsoft.VisualStudio.Extensibility.VSSdkCompatibility;
using Microsoft.VisualStudio.RpcContracts.DebuggerVisualizers;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
#nullable enable
namespace Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders;
[VisualStudioContribution]
internal class IEnumerableVisualizerProvider : DebuggerVisualizerProvider
{
public IEnumerableVisualizerProvider(
VisualStudioExtensibility extensibility,
TraceSource traceSource)
{
}
public virtual DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration
{
get
{
return new DebuggerVisualizerProviderConfiguration("%IEnumerableVisualizer.DisplayName%", "System.Collections.Generic.IEnumerable`1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
{
VisualizerObjectSourceType = new VisualizerObjectSourceType(typeof (IEnumerableVisualizerSource)),
Style = (VisualizerStyle) 1
};
}
}
public virtual async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget,CancellationToken cancellationToken)
{
IEnumerableViewModel viewModel = new IEnumerableViewModel(visualizerTarget.ObjectSource, visualizerTarget.OriginalVisualizedExpression, VsTaskLibraryHelper.WithPriority(ThreadHelper.JoinableTaskFactory, (VsTaskRunContext) 6));
visualizerTarget.VisualizedExpressionChanged += new Func<string, Task>(((TabularDataViewModel) viewModel).VisualizerTarget_VisualizedExpressionChangedAsync);
visualizerTarget.Changed += new Func<VisualizerTargetStateNotification, Task>(((TabularDataViewModel) viewModel).VisualizerTarget_StateChangedAsync);
await viewModel.InitializeAsync();
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(new CancellationToken());
IVsDebugger debugger = await ServiceExtensions.GetServiceAsync<IVsDebugger, IVsDebugger>(AsyncServiceProvider.GlobalProvider, cancellationToken);
bool flag = (await ServiceExtensions.GetServiceAsync<SVsFeatureFlags, IVsFeatureFlags>(AsyncServiceProvider.GlobalProvider, cancellationToken)).IsFeatureEnabled("Debugger.EnableVisualizerDataTips", true);
IEnumerableVisualizerControl visualizerControl1 = new IEnumerableVisualizerControl();
visualizerControl1.DataContext = (object) viewModel;
visualizerControl1.Debugger = debugger;
visualizerControl1.AreDataTipsEnabled = flag;
IEnumerableVisualizerControl visualizerControl2 = visualizerControl1;
viewModel.ViewHelper = (IViewHelper) visualizerControl2;
IRemoteUserControl visualizerAsync = (IRemoteUserControl) new WpfControlWrapper((FrameworkElement) visualizerControl2);
viewModel = (IEnumerableViewModel) null;
debugger = (IVsDebugger) null;
return visualizerAsync;
}
protected virtual Task InitializeAsync(CancellationToken cancellationToken)
{
return ((ExtensionPart) this).InitializeAsync(cancellationToken);
}
}
@@ -1,66 +0,0 @@
// Decompiled with JetBrains decompiler
// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizerSource
// Assembly: IEnumerableVisualizer.DebuggeeSide, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: CFD2027C-E4DB-4399-B4DA-641D6C32082D
// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\net2.0\IEnumerableVisualizer.DebuggeeSide.dll
using Microsoft.VisualStudio.DebuggerVisualizers;
using System;
using System.Collections.Generic;
using System.IO;
namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer;
public class IEnumerableVisualizerSource : VisualizerObjectSource
{
private static readonly Dictionary<object, IEnumerableObjectHandler> DataObjectHandlerDictionary = new Dictionary<object, IEnumerableObjectHandler>();
private static IEnumerableObjectHandler CurrentObjectHandler = (IEnumerableObjectHandler)null;
public static object GetObjectAt(int tableIndex, int rowIndex, int columnIndex)
{
return CurrentObjectHandler?.GetObjectAt(tableIndex, rowIndex, columnIndex) ?? (object)null;
}
public virtual void TransferData(object obj, Stream fromVisualizer, Stream toVisualizer)
{
IEnumerableObjectHandler dataObjectHandler;
if (DataObjectHandlerDictionary.ContainsKey(obj))
{
dataObjectHandler = DataObjectHandlerDictionary[obj];
dataObjectHandler.Refresh();
}
else
{
dataObjectHandler = CreateDataObjectHandler(obj);
DataObjectHandlerDictionary[obj] = dataObjectHandler;
}
CurrentObjectHandler = dataObjectHandler;
fromVisualizer.Seek(0L, SeekOrigin.Begin);
IDeserializableObject deserializableObject = VisualizerObjectSource.GetDeserializableObject(fromVisualizer);
Type type = (Type)null;
if (!deserializableObject.IsBinaryFormat)
{
string stringPropertyValue = deserializableObject.GetJsonStringPropertyValue("TypeName");
if (!Utils.SupportedCommands.TryGetValue(stringPropertyValue, out type))
throw new ArgumentException($"Unknown Command: {stringPropertyValue}. Cannot send command to debuggee-side visualizer.");
}
ICommunicatorCommand communicatorCommand = (ICommunicatorCommand)deserializableObject.ToObject(type);
if (communicatorCommand.TypeName.Equals("GetTableListCommand", StringComparison.InvariantCulture))
dataObjectHandler.InitializeDataSource();
SendResult(communicatorCommand.HandleCommand(dataObjectHandler), toVisualizer);
}
private IEnumerableObjectHandler CreateDataObjectHandler(object obj)
{
return new IEnumerableObjectHandler(obj);
}
public virtual object CreateReplacementObject(object obj, Stream serializedObject) => throw new NotSupportedException();
public virtual void GetData(object obj, Stream dataStream) => throw new NotSupportedException();
private void SendResult(CommunicatorResult result, Stream toVisualizer)
{
VisualizerObjectSource.Serialize(toVisualizer, (object)result);
}
}
@@ -1,18 +0,0 @@
// Decompiled with JetBrains decompiler
// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.LatestDataAvailable
// Assembly: IEnumerableVisualizer.UI, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: 8D605BCB-B575-4FCD-B6C8-446C06150F34
// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\IEnumerableVisualizer.UI.dll
#nullable disable
namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer;
public class LatestDataAvailable
{
public int AvailableDataCount { get; }
public LatestDataAvailable(int availableDataCount)
{
this.AvailableDataCount = availableDataCount;
}
}
+2 -16
View File
@@ -1,22 +1,8 @@
using System; using System.Threading.Tasks;
using System.Numerics;
using System.Threading.Tasks;
namespace NamedPipes; namespace NamedPipes;
[Serializable]
public struct Frame(Vector3 translation, Quaternion orientation)
{
public Vector3 Translation => translation;
public Quaternion Orientation => orientation;
}
[Serializable]
public class DebugObject
{
public string? Type { get; set; }
public byte[]? Data { get; set; }
}
public interface IDebugVisualizer public interface IDebugVisualizer
{ {
Task<bool> SetDebugObjectAsync(DebugObject debugObject); Task<bool> SetVisualizerModelAsync(VisualizerModel visualizerModel);
Task<bool> SetMessageAsync(string message); Task<bool> SetMessageAsync(string message);
} }
+6 -6
View File
@@ -1,8 +1,8 @@
using System; using PipeMethodCalls;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using PipeMethodCalls;
namespace NamedPipes; namespace NamedPipes;
@@ -30,7 +30,7 @@ public class NamedPipeClient (string pipeName,string? serverLocation = null, st
try try
{ {
await PipeClient.ConnectAsync().ConfigureAwait(true); await PipeClient.ConnectAsync().ConfigureAwait(true);
Logger("NamedPipeClient.StartServerAsync succeded."); Logger("NamedPipeClient.StartServerAsync succeeded.");
return; return;
} }
catch(Exception e) catch(Exception e)
@@ -40,11 +40,11 @@ public class NamedPipeClient (string pipeName,string? serverLocation = null, st
} }
} }
public async Task<bool> SetDebugObjectAsync(DebugObject debugObject) public async Task<bool> SetVisualizerModelAsync(VisualizerModel visualizerModel)
{ {
await StartServerAsync().ConfigureAwait(true); await StartServerAsync().ConfigureAwait(true);
var result = await PipeClient.InvokeAsync(server => server.SetDebugObjectAsync(debugObject)); var result = await PipeClient.InvokeAsync(server => server.SetVisualizerModelAsync(visualizerModel));
Logger("NamedPipeClient.SetDebugObjectAsync: result = " + result); Logger("NamedPipeClient.SetVisualizerModelAsync: result = " + result);
return result; return result;
} }
+13 -11
View File
@@ -1,9 +1,11 @@
using System; using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using PipeMethodCalls; using PipeMethodCalls;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace NamedPipes; namespace NamedPipes;
public partial class NamedPipesServer(string pipeName,Action<string>? logger = null) : ObservableObject,IDebugVisualizer, IDisposable public partial class NamedPipesServer(string pipeName,Action<string>? logger = null) : ObservableObject,IDebugVisualizer, IDisposable
{ {
@@ -29,27 +31,27 @@ public partial class NamedPipesServer(string pipeName,Action<string>? logger = n
Logger("Waiting for client connection..."); Logger("Waiting for client connection...");
await PipeServer.WaitForConnectionAsync().ConfigureAwait(true); await PipeServer.WaitForConnectionAsync().ConfigureAwait(true);
} }
public async Task RunAsync() public async Task RunAsync(CancellationToken cancellationToken)
{ {
while(true) while(!cancellationToken.IsCancellationRequested)
{ {
await WaitForConnectionAsync().ConfigureAwait(true); await WaitForConnectionAsync().ConfigureAwait(true);
Logger("Client connected."); Logger("Client connected.");
while (IsConnected) while (IsConnected)
{ {
await Task.Delay(100).ConfigureAwait(true); await Task.Delay(100, cancellationToken).ConfigureAwait(true);
} }
Logger("Client disconnected."); Logger("Client disconnected.");
} }
} }
[ObservableProperty] [ObservableProperty]
public partial DebugObject? DebugObject { get; private set; } public partial VisualizerModel? VisualizerModel { get; private set; }
public async Task<bool> SetDebugObjectAsync(DebugObject debugObject) public async Task<bool> SetVisualizerModelAsync(VisualizerModel visualizerModel)
{ {
await Task.Delay(1); await Task.Delay(1);
DebugObject = debugObject; VisualizerModel = visualizerModel;
Logger("Received DebugObject of type: " + DebugObject.Type); Logger("NamedPipesServer, received object of type: " + visualizerModel.GetType());
return true; return true;
} }
+3 -3
View File
@@ -7,10 +7,10 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MessagePack" Version="3.1.4" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="PipeMethodCalls" Version="4.0.3" /> <PackageReference Include="PipeMethodCalls" Version="4.0.3" />
<PackageReference Include="System.Numerics.Vectors" Version="4.6.1" /> <PackageReference Include="System.Numerics.Vectors" Version="4.6.1" />
<PackageReference Include="System.Text.Json" Version="10.0.6" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+22 -26
View File
@@ -1,38 +1,34 @@
using System; using PipeMethodCalls;
using MessagePack; using System;
using PipeMethodCalls; using System.IO;
using System.Text.Json;
namespace NamedPipes namespace NamedPipes;
/// <summary>
/// Serializes pipe method call information with System.Text.Json.
/// </summary>
public class PipeSerializer : IPipeSerializer
{ {
public class PipeSerializer : IPipeSerializer private JsonSerializerOptions options = new()
{ {
public object? Deserialize(byte[] data, Type type) IncludeFields = true
};
public object Deserialize(byte[] data, Type type)
{ {
if (data.Length == 0) return null;
//Log.Trace("PipeSerializer.Deserialize: type = " + type + " , data.length = " + data.Length); var obj = JsonSerializer.Deserialize(data, type, options) ?? throw new InvalidDataException($"Can not deserialize to type: {type} ");
var obj = MessagePackSerializer.Deserialize(type, data, MessagePack.Resolvers.ContractlessStandardResolver.Options);
//if (obj is DebugObject debugObject)
//{
// var length = debugObject.Data?.Length ?? 0;
// var debugObjectType = debugObject.Type ?? "null";
// Log.Trace("PipeSerializer.Deserialize: debugObject.Type = " + debugObjectType + " , debugObject.Data.Length = " + length);
//}
return obj; return obj;
} }
public byte[] Serialize(object o) public byte[] Serialize(object o)
{ {
if (o.GetType().FullName == "System.Threading.Tasks.VoidTaskResult") return [];
//if (o is DebugObject debugObject)
//{
// var length = debugObject.Data?.Length ?? 0;
// Log.Trace("PipeSerializer.Deserialize: debugObject.Type = " + debugObject.Type + " , debugObject.Data.Length = " + length);
//}
var bytearray = MessagePackSerializer.Serialize(o.GetType(), o, MessagePack.Resolvers.ContractlessStandardResolver.Options); using var memoryStream = new MemoryStream();
//var xxx = Deserialize(bytearray, o.GetType()); using var utf8JsonWriter = new Utf8JsonWriter(memoryStream);
//Log.Trace("PipeSerializer.Serialize: type = " + o.GetType() + " , bytearray.length = " + bytearray.Length); JsonSerializer.Serialize(utf8JsonWriter, o, options);
return bytearray; var data = memoryStream.ToArray();
} return data;
} }
} }
+31
View File
@@ -0,0 +1,31 @@
using System;
using System.Numerics;
using System.Runtime.Serialization;
using Frame = System.ValueTuple<System.Numerics.Vector3, System.Numerics.Quaternion, float>;
namespace NamedPipes;
[DataContract]
[Serializable]
public class VisualizerModel
{
public static Type FrameType => typeof(Frame);
[DataMember]
public string? Content { get; set; }
[DataMember]
public Vector3? Point { get; set; }
[DataMember]
public Quaternion? Orientation { get; set; }
[DataMember]
public Vector3[]? PointArray { get; set; }
[DataMember]
public Frame? Frame { get; set; }
[DataMember]
public Frame[]? FrameArray { get; set; }
}
+11
View File
@@ -0,0 +1,11 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="NrxDebugVisualizer.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<SimpleTheme />
<DockSimpleTheme />
</Application.Styles>
</Application>
+34
View File
@@ -0,0 +1,34 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Controls.Utilities;
using NrxDebugVisualizer.ViewModels;
using NrxDebugVisualizer.Views;
namespace NrxDebugVisualizer;
public partial class App : Application
{
public static Settings? Settings { get; set; }
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
#if DEBUG
this.AttachDeveloperTools();
#endif
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
Settings = Settings.Load(desktop.MainWindow);
}
base.OnFrameworkInitializationCompleted();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

+43
View File
@@ -0,0 +1,43 @@
using NamedPipes;
using NrxDebugVisualizer.Scenes;
using Num.Roto.Visualization.Math.Geometry;
using Num.Roto.Visualization.Math.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
namespace NrxDebugVisualizer.Models;
internal sealed class PipeServer
{
private string PipeName { get; }
private NamedPipesServer? NamedPipesServer { get; set; }
private Action<VisualizerModel> UpdateAction { get; init; }
public PipeServer(string pipeName, Action<VisualizerModel> updateAction,CancellationToken cancellationToken)
{
PipeName = pipeName;
UpdateAction = updateAction;
StartPipeServer(cancellationToken);
}
private void NamedPipesServer_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(NamedPipesServer.VisualizerModel)) return;
var visualizerObject = NamedPipesServer?.VisualizerModel;
if (visualizerObject is null) return;
UpdateAction(visualizerObject);
}
#region NamedPipesServer
private async void StartPipeServer(CancellationToken cancellationToken)
{
NamedPipesServer = new NamedPipesServer(PipeName, logger: (s) => Log.Info(s,0));
NamedPipesServer.PropertyChanged += NamedPipesServer_PropertyChanged;
await NamedPipesServer.RunAsync(cancellationToken).ConfigureAwait(false);
}
#endregion
}
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="12.0.1" />
<PackageReference Include="Avalonia.Desktop" Version="12.0.1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.1" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="12.0.1" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.14">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Num.Roto.Visualization\Avalonia\Controls\Controls.csproj" />
<ProjectReference Include="..\..\Num.Roto.Visualization\Math\Num.Roto.Visualization.Math.csproj" />
<ProjectReference Include="..\..\Num.Roto.Visualization\SceneGraph\Num.Roto.Visualization.SceneGraph.csproj" />
<ProjectReference Include="..\NamedPipes\NamedPipes.csproj" />
</ItemGroup>
</Project>
+21
View File
@@ -0,0 +1,21 @@
using Avalonia;
using System;
namespace NrxDebugVisualizer;
internal static class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
private static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
@@ -0,0 +1,89 @@
using Num.Roto.Visualization.Math.Geometry;
using Num.Roto.Visualization.SceneGraph.Geometry;
using Num.Roto.Visualization.SceneGraph.Material;
using Num.Roto.Visualization.SceneGraph.Scene;
using Num.Roto.Visualization.SceneGraph.Scene.Nodes;
using Num.Roto.Visualization.Types;
using System.Numerics;
namespace NrxDebugVisualizer.Scenes;
internal sealed class DebugVisualizerScene : Num.Roto.Visualization.SceneGraph.Scenes.SceneBase
{
private static uint Counter;
private static uint Uid => Counter++;
private SceneNode Points { get; }
private SceneNode Frames { get; }
internal DebugVisualizerScene() : base("NrxDebuggerVisualizerScene")
{
Points = new SceneNode(nameof(Points), Objects);
Frames = new SceneNode(nameof(Frames), Objects);
}
public void AddPointGeometry(Vector3 position, float pointSize)
{
var point = new Points([position], "Point");
_ = new SceneGeometry("Point" + $".{Uid}", point, new Material(Colors4.White, null), Points)
{
PointSize = pointSize,
ComputeFragColor = false
};
}
public void AddPointListGeometry(Vector3[] positions, float pointSize)
{
var points = new Points(positions, "Points");
var material = new Material(Colors4.White, null)
{
EmissiveColor = Colors4.White,
VertexColorBlendFactor = 1
};
_ = new SceneGeometry("Points" + $".{Uid}", points, material, Points)
{
PointSize = pointSize,
ComputeFragColor = false
};
}
public void AddFrameGeometry(Frame frame, float length)
{
AddFrameListGeometry([frame], length);
}
public void AddFrameListGeometry(Frame[] frames, float length)
{
var material = new Material(Colors4.White, null)
{
EmissiveColor = Colors4.White,
VertexColorBlendFactor = 1
};
var name = frames.Length > 1 ? "FrameList" : "Frame";
var firstFrame = frames[0];
var frameContainer = new SceneNode(name + $".{Uid}", Frames)
{
Frame = firstFrame
};
var inverse = firstFrame.Inverse();
for (var i = 0; i < frames.Length; ++i)
{
frames[i] = inverse * frames[i];
}
var frameGeometry = new SceneGeometry("Geometry", Crosses.DefaultCross(0.5f), material, frameContainer)
{
InstanceFrames = [.. frames],
Flags = 0,
LineWidth = 1
};
if (frames.Length > 1)
{
var size = 2 * (frames.Length - 1);
var path = new Vector3[size];
path[0] = frames[0].Translation;
for (var i = 1; i < frames.Length - 1; ++i)
{
path[2 * i] = path[2 * i - 1] = frames[i].Translation;
}
path[^1] = frames[^1].Translation;
_ = new SceneGeometry("Path", new Lines(path), material, frameGeometry);
}
}
}
@@ -0,0 +1,127 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using CommunityToolkit.Mvvm.Input;
using Controls.ViewModels;
using NamedPipes;
using NrxDebugVisualizer.Models;
using NrxDebugVisualizer.Scenes;
using Num.Roto.Visualization.Math.Geometry;
using Num.Roto.Visualization.Math.Utilities;
using Num.Roto.Visualization.Types;
using Num.Roto.Visualization.VulkanLib.SceneInteraction;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
using KontractFrame = System.ValueTuple<System.Numerics.Vector3, System.Numerics.Quaternion, float>;
namespace NrxDebugVisualizer.ViewModels;
internal sealed partial class MainWindowViewModel : ViewModelBase
{
public ProportionalDockControlViewModel ProportionalDockControlViewModel { get; } = new();
public VulkanSceneControlViewModel VulkanSceneControlViewModel => ProportionalDockControlViewModel.VulkanSceneControlViewModel;
public SceneTreeViewViewModel SceneTreeViewViewModel => ProportionalDockControlViewModel.SceneTreeViewViewModel?? throw new InvalidOperationException("SceneTreeViewViewModel is not available");
public SceneInteraction SceneInteraction => ProportionalDockControlViewModel.SceneInteraction;
public DebugVisualizerScene DebugVisualizerScene => ProportionalDockControlViewModel.Scene as DebugVisualizerScene ?? throw new InvalidOperationException("Scene is not a DebugVisualizerScene");
private PipeServer PipeServer {get;init;}
public MainWindowViewModel()
{
ProportionalDockControlViewModel.Scene = new DebugVisualizerScene();
ProportionalDockControlViewModel.Scene.SelectedCamera.DefaultFrame = CameraView.Frame(ViewPosition);
PipeServer = new PipeServer(nameof(NrxDebugVisualizer), UpdateScene, cancellationToken: CancellationToken.None);
}
#region Commands
[RelayCommand]
public static void ShowLogViewer()
{
Log.LogViewerEnabled = true;
}
[RelayCommand]
private static void Exit(object? parameter)
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopApp)
{
desktopApp.Shutdown();
}
}
[RelayCommand]
internal void ResetCamera()
{
ProportionalDockControlViewModel.VulkanSceneControlViewModel.ResetCamera(CameraView.Frame(ViewPosition));
}
[RelayCommand]
internal void FitToWindow()
{
ProportionalDockControlViewModel.Scene?.FitToWindow(0.95f);
ProportionalDockControlViewModel.VulkanSceneControlViewModel.SceneInteraction.Update();
}
#endregion
#region Camera View
public static IEnumerable<CameraView.Position> ViewPositions => Enum.GetValues<CameraView.Position>();
private CameraView.Position _viewPosition = CameraView.Position.Custom1;
public CameraView.Position ViewPosition
{
get => _viewPosition;
set
{
if (_viewPosition == value) return;
_viewPosition = value;
OnPropertyChanged();
if (_viewPosition == CameraView.Position.User) return;
ProportionalDockControlViewModel.Scene?.SelectedCamera.DefaultFrame = CameraView.Frame(_viewPosition);
FitToWindow();
}
}
#endregion
#region Pipe Server
private void UpdateScene(VisualizerModel visualizerModel)
{
SetPoint(visualizerModel.Point);
SetPointList(visualizerModel.PointArray);
SetFrame(visualizerModel.Frame);
SetFrameList(visualizerModel.FrameArray);
SceneInteraction.Update();
}
private static Frame ToFrame(KontractFrame kontractFrame)
{
var (translation, orientation, scale) = kontractFrame;
return new Frame(translation, scale, orientation);
}
private static Frame[] ToFrameArray(IEnumerable<KontractFrame> frameList)
{
return [.. frameList.Select(ToFrame)];
}
private void SetPoint(Vector3? point)
{
if (point is null) return;
DebugVisualizerScene.AddPointGeometry(point.Value, 5f);
}
private void SetPointList(Vector3[]? pointList)
{
if (pointList == null || pointList.Length < 1) return;
DebugVisualizerScene.AddPointListGeometry(pointList, 5);
}
private void SetFrame(KontractFrame? kontractFrame)
{
if (kontractFrame is null) return;
DebugVisualizerScene.AddFrameGeometry(ToFrame(kontractFrame.Value), 1);
}
private void SetFrameList(KontractFrame[]? frameArray)
{
if (frameArray == null || frameArray.Length < 1) return;
DebugVisualizerScene.AddFrameListGeometry(ToFrameArray(frameArray), 1);
}
#endregion
}
+50
View File
@@ -0,0 +1,50 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:NrxDebugVisualizer.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="NrxDebugVisualizer.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
xmlns:viewModels="clr-namespace:NrxDebugVisualizer.ViewModels"
xmlns:controls="clr-namespace:Controls.Views;assembly=Controls"
Icon="/Assets/avalonia-logo.ico"
Title="NrxDebugVisualizer"
Width="1200"
Height="800"
MinWidth="800"
MinHeight="600">
<Design.DataContext>
<viewModels:MainWindowViewModel />
</Design.DataContext>
<DockPanel LastChildFill="True" Background="LightGray" >
<Menu DockPanel.Dock="Top" FontSize="12">
<MenuItem Header="_File">
<MenuItem Header="_Exit" Command="{Binding ExitCommand}" />
</MenuItem>
<MenuItem Header="_Options" VerticalAlignment="Center">
<MenuItem Header="_ShowLogViewer" Command="{Binding ShowLogViewerCommand}" />
</MenuItem>
<MenuItem Header="_Camera">
<MenuItem Header="_Reset" Command="{Binding ResetCameraCommand }" />
<MenuItem Header="_Fit" Command="{Binding FitToWindowCommand}" />
<DropDownButton Content="View">
<Button.Flyout>
<Flyout Placement="Right">
<ListBox ItemsSource="{Binding ViewPositions}" SelectedItem="{Binding ViewPosition}" SelectionMode="Single" />
</Flyout>
</Button.Flyout>
</DropDownButton>
</MenuItem>
</Menu>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Background="DarkGray" >
<Label Content="{Binding ProportionalDockControlViewModel.VulkanSceneControlViewModel.Fps,FallbackValue=Unset}" IsHitTestVisible="False" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" />
<Label Content="{Binding ProportionalDockControlViewModel.VulkanSceneControlViewModel.ObjectInfo,FallbackValue=Unset}" IsHitTestVisible="False" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" />
</StackPanel>
<controls:ProportionalDockControl DataContext="{Binding ProportionalDockControlViewModel}"/>
</DockPanel>
</Window>
@@ -0,0 +1,40 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using NrxDebugVisualizer.ViewModels;
using Num.Roto.Visualization.Types;
using System.Data;
using System.Globalization;
using System.Threading;
namespace NrxDebugVisualizer.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
InitializeComponent();
Closing += OnClosing;
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
if (DataContext is not MainWindowViewModel viewModel) throw new DataException("wrong viewModel type!");
if (App.Settings?.CameraFrame is not { } cameraFrame) return;
var scene = viewModel.ProportionalDockControlViewModel.Scene;
var camera = scene?.SelectedCamera;
camera?.Frame = cameraFrame;
viewModel.ViewPosition = CameraView.Position.User;
}
private async void OnClosing(object? sender, System.ComponentModel.CancelEventArgs e)
{
if (DataContext is not MainWindowViewModel viewModel) throw new DataException("wrong viewModel type!");
var scene = viewModel.ProportionalDockControlViewModel.Scene;
var camera = scene?.SelectedCamera;
App.Settings?.MainWindow = this;
App.Settings?.CameraFrame = camera?.Frame;
App.Settings?.Save();
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="NrxDebugVisualizer.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
@@ -14,7 +14,7 @@ namespace NrxDebugVisualizer
{ {
Metadata = new( Metadata = new(
id: "Vector3Visualizer.3c0400c3-acec-4b09-b561-c11fac7b10a7", id: "Vector3Visualizer.3c0400c3-acec-4b09-b561-c11fac7b10a7",
version: this.ExtensionAssemblyVersion, version: ExtensionAssemblyVersion,
publisherName: "Nrx Visualizer", publisherName: "Nrx Visualizer",
displayName: "Vector3Visualizer", displayName: "Vector3Visualizer",
description: "Visualizer for MailAdress"), description: "Visualizer for MailAdress"),
@@ -17,8 +17,6 @@
<Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\NrxVisualizerObjectSource.dll" Link="netstandard2.0\NrxVisualizerObjectSource.dll" CopyToOutputDirectory="PreserveNewest" /> <Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\NrxVisualizerObjectSource.dll" Link="netstandard2.0\NrxVisualizerObjectSource.dll" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\NamedPipes.dll" Link="netstandard2.0\NamedPipes.dll" CopyToOutputDirectory="PreserveNewest" /> <Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\NamedPipes.dll" Link="netstandard2.0\NamedPipes.dll" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\PipeMethodCalls.dll" Link="netstandard2.0\PipeMethodCalls.dll" CopyToOutputDirectory="PreserveNewest" /> <Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\PipeMethodCalls.dll" Link="netstandard2.0\PipeMethodCalls.dll" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\MessagePack.dll" Link="netstandard2.0\MessagePack.dll" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\MessagePack.Annotations.dll" Link="netstandard2.0\MessagePack.Annotations.dll" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\CommunityToolkit.Mvvm.dll" Link="netstandard2.0\CommunityToolkit.Mvvm.dll" CopyToOutputDirectory="PreserveNewest" /> <Content Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net8.0-windows8.0\CommunityToolkit.Mvvm.dll" Link="netstandard2.0\CommunityToolkit.Mvvm.dll" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -2,7 +2,6 @@
using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers; using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers;
using Microsoft.VisualStudio.RpcContracts.RemoteUI; using Microsoft.VisualStudio.RpcContracts.RemoteUI;
using NamedPipes; using NamedPipes;
using NrxVisualizerObjectSource;
using System; using System;
using System.Numerics; using System.Numerics;
using System.Threading; using System.Threading;
@@ -27,17 +26,18 @@ internal sealed class NrxDebuggerVisualizerProvider : DebuggerVisualizerProvider
}; };
public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken) public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
{ {
Vector3Model? model = await visualizerTarget.ObjectSource.RequestDataAsync<Vector3Model?>(jsonSerializer: null, cancellationToken); VisualizerModel? model = await visualizerTarget.ObjectSource.RequestDataAsync<VisualizerModel?>(jsonSerializer: null, cancellationToken);
SendToVisualizer(model); await SendToVisualizerAsync(model);
//throw new InvalidOperationException("This is a test exception to demonstrate the visualizer. Please remove this line and implement the visualizer logic."); //throw new InvalidOperationException("This is a test exception to demonstrate the visualizer. Please remove this line and implement the visualizer logic.");
var control = new Vector3VisualizerUserControl(dataContext: model); var control = new Vector3VisualizerUserControl(dataContext: model);
return await Task.FromResult<IRemoteUserControl>(control); return await Task.FromResult<IRemoteUserControl>(control);
} }
private static NamedPipeClient? PipeClient { get; set; } private static NamedPipeClient? PipeClient { get; set; }
private static void SendToVisualizer(Vector3Model? vector3Model) private static async Task SendToVisualizerAsync(VisualizerModel? visualizerModel)
{ {
if (vector3Model is null) return; if (visualizerModel is null) return;
PipeClient ??= new NamedPipeClient("testPipe", serverName: @"TestServer", logger: (m) => Console.WriteLine(m)); PipeClient ??= new NamedPipeClient("testPipe", serverName: @"TestServer", logger: (m) => Console.WriteLine(m));
_ = PipeClient.SetMessageAsync($"Hello from {nameof(CreateVisualizerAsync)},vector3Model = {vector3Model.Vector3},{vector3Model.Quaternion}"); await PipeClient.SetVisualizerModelAsync(visualizerModel);
await PipeClient.SetMessageAsync($"Hello from {nameof(CreateVisualizerAsync)},visualizerModel.Content = {visualizerModel.Content}");
} }
} }
@@ -1,14 +1,14 @@
using Microsoft.VisualStudio.Extensibility.UI; using Microsoft.VisualStudio.Extensibility.UI;
using NrxVisualizerObjectSource; using NamedPipes;
namespace NrxDebugVisualizer; namespace NrxDebugVisualizer;
internal sealed class Vector3VisualizerUserControl : RemoteUserControl internal sealed class Vector3VisualizerUserControl : RemoteUserControl
{ {
public Vector3VisualizerUserControl(Vector3Model? dataContext) : base(dataContext) public Vector3VisualizerUserControl(VisualizerModel? dataContext) : base(dataContext)
{ {
Vector3Model = (Vector3Model?)DataContext; Vector3Model = (VisualizerModel?)DataContext;
} }
public Vector3Model? Vector3Model { get; } public VisualizerModel? Vector3Model { get; }
} }
@@ -1,16 +1,5 @@
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid> <Grid>
<Grid.ColumnDefinitions> <TextBox TextWrapping="Wrap" AcceptsReturn="True" Text="{Binding Content}" Grid.Column="1" Grid.Row="0" />
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="Vector3" Grid.Column="0" Grid.Row="0" />
<TextBlock Text="{Binding Vector3}" Grid.Column="1" Grid.Row="0" />
<TextBlock Text="Quaternion" Grid.Column="0" Grid.Row="1" />
<TextBlock Text="{Binding Quaternion}" Grid.Column="1" Grid.Row="1" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
@@ -1,43 +1,95 @@
using Microsoft.VisualStudio.DebuggerVisualizers; using Microsoft.VisualStudio.DebuggerVisualizers;
using NamedPipes;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Frame = System.ValueTuple<System.Numerics.Vector3, System.Numerics.Quaternion, float>;
namespace NrxVisualizerObjectSource; namespace NrxVisualizerObjectSource;
public class NrxVisualizerObjectSource : VisualizerObjectSource public class NrxVisualizerObjectSource : VisualizerObjectSource
{ {
public NrxVisualizerObjectSource() public NrxVisualizerObjectSource()
{ {
Debug.WriteLine("new Vector3ObjectSource"); Debug.WriteLine($"new {nameof(NrxVisualizerObjectSource)}");
} }
public override void GetData(object target, Stream outgoingData) public override void GetData(object target, Stream outgoingData)
{ {
Debug.WriteLine("GetData: objectType is: " + target.GetType().FullName); var fullname = target.GetType().FullName;
Vector3Model vector3Model = new(); Trace.WriteLine("GetData: objectType is: " + fullname);
switch (target) VisualizerModel visualizerModel = new();
switch (fullname)
{ {
case Vector3 vector3: case "System.Numerics.Vector3":
vector3Model.Vector3 = vector3; visualizerModel.Point = (Vector3)target;
visualizerModel.Content = target.ToString();
break; break;
case IEnumerable<Vector3> vector3List: case "System.Numerics.Quaternion":
vector3Model.Vector3 = vector3List.Last(); visualizerModel.Orientation = (Quaternion)target;
visualizerModel.Content = target.ToString();
break; break;
case Quaternion quaternion: case "Num.Roto.Visualization.Math.Geometry.Frame":
vector3Model.Quaternion = quaternion; var frame = (Frame)TypeDescriptor.GetConverter(target).ConvertTo(target, VisualizerModel.FrameType);
visualizerModel.Frame = frame;
visualizerModel.Content = frame.ToString();
break; break;
} }
SerializeAsJson(outgoingData, vector3Model); if(target is IEnumerable<Vector3> enumerableVector3)
{
Trace.WriteLine($"enumerable type is {typeof(Vector3)}");
if (enumerableVector3 is not null)
{
List<string> stringList = [];
List<Vector3> pointList = [];
foreach (var item in enumerableVector3)
{
pointList.Add(item);
stringList.Add(TypeDescriptor.GetConverter(item).ConvertToString(item));
}
visualizerModel.PointArray = [.. pointList];
visualizerModel.Content = string.Join(Environment.NewLine, stringList);
}
}
else if (target is IEnumerable<object> enumerable)
{
var objectType = enumerable.First().GetType().FullName;
Trace.WriteLine($"IEnumerable<object> objectType is {objectType}");
switch (objectType)
{
case "Num.Roto.Visualization.Math.Geometry.Frame":
{
List<Frame> frameList = [];
foreach (var item in enumerable)
{
var frame = (Frame)TypeDescriptor.GetConverter(item).ConvertTo(item, VisualizerModel.FrameType);
frameList.Add(frame);
}
visualizerModel.FrameArray = [.. frameList];
visualizerModel.Content = string.Join(Environment.NewLine, frameList);
Trace.WriteLine($"I visualizerModel.Content is {visualizerModel.Content}");
break;
}
default:
visualizerModel.Content = $"unsupported enumerable of {objectType}";
break;
}
}
Trace.WriteLine($"visualizerModel.Content = {visualizerModel.Content}");
SerializeAsJson(outgoingData, visualizerModel);
} }
public override object CreateReplacementObject(object target, Stream incomingData) public override object CreateReplacementObject(object target, Stream incomingData)
{ {
Debug.WriteLine("CreateReplacementObject: objectType is: " + target.GetType().FullName); throw new NotImplementedException();
return base.CreateReplacementObject(target, incomingData); //Debug.WriteLine("CreateReplacementObject: objectType is: " + target.GetType().FullName);
//return base.CreateReplacementObject(target, incomingData);
} }
public override void TransferData(object target, Stream incomingData, Stream outgoingData) public override void TransferData(object target, Stream incomingData, Stream outgoingData)
{ {
Debug.WriteLine("TransferData: objectType is: " + target.GetType().FullName); throw new NotImplementedException();
base.TransferData(target, incomingData, outgoingData); //Debug.WriteLine("TransferData: objectType is: " + target.GetType().FullName);
//base.TransferData(target, incomingData, outgoingData);
} }
} }
-13
View File
@@ -1,13 +0,0 @@
using System.Numerics;
using System.Runtime.Serialization;
namespace NrxVisualizerObjectSource;
[DataContract]
public class Vector3Model
{
[DataMember]
public Vector3? Vector3 { get; set; }
[DataMember]
public Quaternion? Quaternion { get; set;}
}
+16 -4
View File
@@ -1,12 +1,24 @@
using System.Text;
using NamedPipes; using NamedPipes;
var count = 0; var count = 0;
var message = "Hello World!".ToCharArray(); var message = "Hello World!".ToCharArray();
VisualizerModel visualizerModel = new()
{
Content = "Hello from TestClient",
Point = new System.Numerics.Vector3(1, 2, 3),
//Orientation = System.Numerics.Quaternion.Identity,
//PointArray = [new System.Numerics.Vector3(4, 5, 6), new System.Numerics.Vector3(7, 8, 9)],
//Frame = (new System.Numerics.Vector3(10, 11, 12), System.Numerics.Quaternion.Identity, 1.0f),
//FrameArray = [(new System.Numerics.Vector3(13, 14, 15), System.Numerics.Quaternion.Identity, 2.0f), (new System.Numerics.Vector3(16, 17, 18), System.Numerics.Quaternion.Identity, 3.0f)]
};
while (true) while (true)
{ {
var pipeClient = new NamedPipeClient("testPipe",@".\","TestServer",logger:Console.WriteLine); var serverName = "NrxDebugVisualizer";
var pipeClient = new NamedPipeClient(serverName, @".\", serverName, logger:Console.WriteLine);
Thread.Sleep(1000); Thread.Sleep(1000);
pipeClient.SetMessageAsync($"Hello from TestClient " + count++).GetAwaiter().GetResult(); visualizerModel.Point = new System.Numerics.Vector3(count, count * 2, count * 3);
pipeClient.SetDebugObjectAsync(new DebugObject{Type = typeof(string).FullName,Data = Encoding.GetEncoding("UTF-8").GetBytes(message)}).GetAwaiter().GetResult(); count++;
await pipeClient.SetVisualizerModelAsync(visualizerModel);
//await pipeClient.SetMessageAsync($"Hello from TestClient " + count++);
pipeClient.Dispose(); pipeClient.Dispose();
} }
+3 -2
View File
@@ -1,5 +1,6 @@
using System; using System;
var pipeServer = new NamedPipes.NamedPipesServer("testPipe",logger:Console.WriteLine); using System.Threading;
await pipeServer.RunAsync().ConfigureAwait(true); var pipeServer = new NamedPipes.NamedPipesServer("NrxDebugVisualizer", logger:Console.WriteLine);
await pipeServer.RunAsync(CancellationToken.None).ConfigureAwait(true);
@@ -0,0 +1,8 @@
{
"profiles": {
"TestServer": {
"commandName": "Project",
"workingDirectory": "C:\\Users\\matth\\Documents\\Visual Studio 2026\\Visualizers"
}
}
}
+13 -1
View File
@@ -8,5 +8,17 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\NamedPipes\NamedPipes.csproj" /> <ProjectReference Include="..\NamedPipes\NamedPipes.csproj" />
</ItemGroup> </ItemGroup>
<Target Name="CopyToVs2026" Condition="Exists('$(OutputPath)\$(MSBuildProjectName).exe')" AfterTargets="AfterBuild" >
<Message Importance="High" Text="CopyToVs2026"></Message>
<ItemGroup>
<FilesToCopy Include="$(OutputPath)\$(MSBuildProjectName).exe;
$(OutputPath)\$(MSBuildProjectName).dll;
$(OutputPath)\$(MSBuildProjectName).runtimeconfig.json;
$(OutputPath)\PipeMethodCalls.dll;
$(OutputPath)\NamedPipes.dll;
$(OutputPath)\CommunityToolkit.Mvvm.dll;
"/>
</ItemGroup>
<Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(USERPROFILE)\Documents\Visual Studio 2026\Visualizers" SkipUnchangedFiles="True" />
</Target>
</Project> </Project>
-12
View File
@@ -1,12 +0,0 @@
using System.Numerics;
var a = new Vector3(1,2,3);
var typeName = a.GetType().AssemblyQualifiedName;
var b = new Vector3(5, 6, 7);
Vector3[] v3Array = [a, b];
List<Vector3> v3List = [.. v3Array];
var quaternion = new Quaternion(1,2,3,4);
return;
@@ -1,10 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -1,3 +0,0 @@
<Solution>
<Project Path="Vector3VisualizerTest.csproj" />
</Solution>
-14
View File
@@ -1,14 +0,0 @@
<Solution>
<Folder Name="/NamedPipes/">
<Project Path="NamedPipes/NamedPipes.csproj" Id="8d7b1151-8b57-4411-a361-47ed9c504a22" />
</Folder>
<Folder Name="/Test/">
<Project Path="TestClient/TestClient.csproj" />
<Project Path="TestServer/TestServer.csproj" Id="c81154ef-d854-4f1e-9d14-2cf674885291" />
<Project Path="Vector3VisualizerTest/Vector3VisualizerTest.csproj" />
</Folder>
<Project Path="NrxDebuggerVisualizerProvider/NrxDebugVisualizer.csproj">
<Deploy />
</Project>
<Project Path="NrxVisualizerObjectSource/NrxVisualizerObjectSource.csproj" />
</Solution>