Compare commits

...

14 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
38 changed files with 572 additions and 1387 deletions
+3
View File
@@ -33,6 +33,9 @@ bld/
[Ll]og/
[Ll]ogs/
# Jetbrains Rider
.idea/
# Visual Studio 2015/2017 cache/options directory
.vs/
# 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>
+20 -2
View File
@@ -1,15 +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>
<Project Path="../Num.Roto.Visualization/Math/Num.Roto.Visualization.Math.csproj" />
<Project Path="NrxDebuggerVisualizerProvider/NrxDebugVisualizer.csproj">
<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;
}
}
+1 -3
View File
@@ -1,6 +1,4 @@
using System;
using System.Numerics;
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace NamedPipes;
public interface IDebugVisualizer
+2 -4
View File
@@ -1,6 +1,4 @@
using PipeMethodCalls;
using PipeMethodCalls.NetJson;
using System;
using System.Diagnostics;
using System.IO;
@@ -11,7 +9,7 @@ namespace NamedPipes;
public class NamedPipeClient (string pipeName,string? serverLocation = null, string? serverName = null,Action<string>? logger = null): IDebugVisualizer, IDisposable
{
private bool IsConnected => PipeClient.State == PipeState.Connected;
private PipeClient<IDebugVisualizer> PipeClient { get; } = new(new NetJsonPipeSerializer(), pipeName);
private PipeClient<IDebugVisualizer> PipeClient { get; } = new(new PipeSerializer(), pipeName);
private string ServerLocation { get; } = serverLocation ?? Environment.GetEnvironmentVariable("USERPROFILE") + @"\Documents\Visual Studio 2026\Visualizers\Server\NrxVisualizer\";
private string ServerName { get; } = serverName ?? "Num.Roto.Nrx.VisualizerServer";
private Action<string> Logger { get;} = logger ??((m) => Trace.WriteLine(m));
@@ -32,7 +30,7 @@ public class NamedPipeClient (string pipeName,string? serverLocation = null, st
try
{
await PipeClient.ConnectAsync().ConfigureAwait(true);
Logger("NamedPipeClient.StartServerAsync succeded.");
Logger("NamedPipeClient.StartServerAsync succeeded.");
return;
}
catch(Exception e)
+5 -5
View File
@@ -1,9 +1,9 @@
using CommunityToolkit.Mvvm.ComponentModel;
using PipeMethodCalls;
using PipeMethodCalls.NetJson;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace NamedPipes;
@@ -25,21 +25,21 @@ public partial class NamedPipesServer(string pipeName,Action<string>? logger = n
if (PipeServer == null || PipeServer.State != PipeState.Connected)
{
PipeServer?.Dispose();
PipeServer = new PipeServer<IDebugVisualizer>(new NetJsonPipeSerializer(), PipeName, () => this);
PipeServer = new PipeServer<IDebugVisualizer>(new PipeSerializer(), PipeName, () => this);
PipeServer.SetLogger((m) => Trace.WriteLine(m));
}
Logger("Waiting for client connection...");
await PipeServer.WaitForConnectionAsync().ConfigureAwait(true);
}
public async Task RunAsync()
public async Task RunAsync(CancellationToken cancellationToken)
{
while(true)
while(!cancellationToken.IsCancellationRequested)
{
await WaitForConnectionAsync().ConfigureAwait(true);
Logger("Client connected.");
while (IsConnected)
{
await Task.Delay(100).ConfigureAwait(true);
await Task.Delay(100, cancellationToken).ConfigureAwait(true);
}
Logger("Client disconnected.");
}
+4 -3
View File
@@ -7,9 +7,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="PipeMethodCalls.NetJson" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.6" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="PipeMethodCalls" Version="4.0.3" />
<PackageReference Include="System.Numerics.Vectors" Version="4.6.1" />
<PackageReference Include="System.Text.Json" Version="10.0.6" />
</ItemGroup>
</Project>
+34
View File
@@ -0,0 +1,34 @@
using PipeMethodCalls;
using System;
using System.IO;
using System.Text.Json;
namespace NamedPipes;
/// <summary>
/// Serializes pipe method call information with System.Text.Json.
/// </summary>
public class PipeSerializer : IPipeSerializer
{
private JsonSerializerOptions options = new()
{
IncludeFields = true
};
public object Deserialize(byte[] data, Type type)
{
var obj = JsonSerializer.Deserialize(data, type, options) ?? throw new InvalidDataException($"Can not deserialize to type: {type} ");
return obj;
}
public byte[] Serialize(object o)
{
using var memoryStream = new MemoryStream();
using var utf8JsonWriter = new Utf8JsonWriter(memoryStream);
JsonSerializer.Serialize(utf8JsonWriter, o, options);
var data = memoryStream.ToArray();
return data;
}
}
+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>
@@ -17,7 +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\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.NetJson.dll" Link="netstandard2.0\PipeMethodCalls.NetJson.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>
@@ -27,17 +27,17 @@ internal sealed class NrxDebuggerVisualizerProvider : DebuggerVisualizerProvider
public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken 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.");
var control = new Vector3VisualizerUserControl(dataContext: model);
return await Task.FromResult<IRemoteUserControl>(control);
}
private static NamedPipeClient? PipeClient { get; set; }
private static void SendToVisualizer(VisualizerModel? visualizerModel)
private static async Task SendToVisualizerAsync(VisualizerModel? visualizerModel)
{
if (visualizerModel is null) return;
PipeClient ??= new NamedPipeClient("testPipe", serverName: @"TestServer", logger: (m) => Console.WriteLine(m));
_ = PipeClient.SetVisualizerModelAsync(visualizerModel);
_ = PipeClient.SetMessageAsync($"Hello from {nameof(CreateVisualizerAsync)},visualizerModel.Content = {visualizerModel.Content}");
await PipeClient.SetVisualizerModelAsync(visualizerModel);
await PipeClient.SetMessageAsync($"Hello from {nameof(CreateVisualizerAsync)},visualizerModel.Content = {visualizerModel.Content}");
}
}
+7 -5
View File
@@ -1,8 +1,8 @@
using System.Text;
using NamedPipes;
var count = 0;
var message = "Hello World!".ToCharArray();
VisualizerModel visualizerModel = new VisualizerModel
VisualizerModel visualizerModel = new()
{
Content = "Hello from TestClient",
Point = new System.Numerics.Vector3(1, 2, 3),
@@ -13,10 +13,12 @@ VisualizerModel visualizerModel = new VisualizerModel
};
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);
visualizerModel.Point = new System.Numerics.Vector3(count, count * 2, count * 3);
count++;
await pipeClient.SetVisualizerModelAsync(visualizerModel);
pipeClient.SetMessageAsync($"Hello from TestClient " + count++).GetAwaiter().GetResult();
//pipeClient.SetDebugObjectAsync(new DebugObject{Type = typeof(string).FullName,Data = Encoding.GetEncoding("UTF-8").GetBytes(message)}).GetAwaiter().GetResult();
//await pipeClient.SetMessageAsync($"Hello from TestClient " + count++);
pipeClient.Dispose();
}
+3 -2
View File
@@ -1,5 +1,6 @@
using System;
var pipeServer = new NamedPipes.NamedPipesServer("testPipe",logger:Console.WriteLine);
await pipeServer.RunAsync().ConfigureAwait(true);
using System.Threading;
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>
<ProjectReference Include="..\NamedPipes\NamedPipes.csproj" />
</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>
-21
View File
@@ -1,21 +0,0 @@
using System.Numerics;
using Num.Roto.Visualization.Math.Geometry;
#region points
var point0 = new Vector3(1,2,3);
var point1 = new Vector3(5, 6, 7);
Vector3[] pointArray = [point0, point1];
List<Vector3> pointList = [.. pointArray];
#endregion
#region frames
var orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ,1);
var frame = new Frame(point0,1, orientation);
Frame[] frameArray = [frame, frame with { Translation = point1}];
var frameList = frameArray.ToList();
#endregion
//var typeName = point0.GetType().AssemblyQualifiedName;
return;
@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Num.Roto.Visualization\Math\Num.Roto.Visualization.Math.csproj" />
</ItemGroup>
</Project>
@@ -1,4 +0,0 @@
<Solution>
<Project Path="../../Num.Roto.Visualization/Math/Num.Roto.Visualization.Math.csproj" />
<Project Path="Vector3VisualizerTest.csproj" />
</Solution>