Skip to content

Commit c5f7673

Browse files
committed
Refactor and enhance object formatting in OCGV
Enhanced object formatting and display logic in the `Out-ConsoleGridView` cmdlet: - Added `GetFormatDataForObjects` to retrieve format data for PowerShell objects. - Updated `EndProcessing` to include format data in `ApplicationData`. - Improved `DataTable` construction to use format data and added dynamic column formatting. - Introduced `IsIdentifierProperty` to handle identifier-specific formatting. - Added `FormatString` and `FormatValue` to `DataTableColumn` for flexible value formatting. - Enhanced `IValue` interface with `OriginalValue` for raw data access. Refactored `TypeGetter` to simplify format data retrieval and object-to-table conversion. Updated `launchSettings.json` to include new profiles and simplify commands. Performed general code cleanup, removed legacy methods, and improved documentation for better maintainability.
1 parent b8dbaa6 commit c5f7673

File tree

7 files changed

+295
-163
lines changed

7 files changed

+295
-163
lines changed

src/Microsoft.PowerShell.ConsoleGuiTools/OutConsoleGridviewCmdletCommand.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,12 @@ protected override void EndProcessing()
157157
// Return if no objects
158158
if (_psObjects.Count == 0) return;
159159

160+
var formatData = GetFormatDataForObjects(_psObjects);
161+
160162
var applicationData = new ApplicationData
161163
{
162164
PSObjects = _psObjects.Cast<object>().ToList(),
165+
FormatData = formatData.ToList(),
163166
Title = Title ?? "Out-ConsoleGridView",
164167
OutputMode = OutputMode,
165168
Filter = Filter,
@@ -187,4 +190,38 @@ public void Dispose()
187190
_outConsoleGridView.Dispose();
188191
GC.SuppressFinalize(this);
189192
}
193+
194+
/// <summary>
195+
/// Gets the format data (property information) for the PowerShell objects using PowerShell's formatting system.
196+
/// </summary>
197+
/// <param name="psObjects">The list of PowerShell objects to get format data for.</param>
198+
/// <returns>A collection of PSPropertyInfo representing the properties to display.</returns>
199+
private IEnumerable<PSPropertyInfo> GetFormatDataForObjects(List<PSObject> psObjects)
200+
{
201+
if (psObjects.Count == 0)
202+
{
203+
return Array.Empty<PSPropertyInfo>();
204+
}
205+
206+
var firstObject = psObjects[0];
207+
208+
// Try to get the DefaultDisplayPropertySet from PSStandardMembers
209+
var standardMembers = firstObject.Members["PSStandardMembers"]?.Value as PSMemberSet;
210+
var defaultDisplayPropertySet = standardMembers?.Members["DefaultDisplayPropertySet"]?.Value as PSPropertySet;
211+
212+
if (defaultDisplayPropertySet?.ReferencedPropertyNames != null &&
213+
defaultDisplayPropertySet.ReferencedPropertyNames.Count > 0)
214+
{
215+
// Return only the properties in the DefaultDisplayPropertySet
216+
return defaultDisplayPropertySet.ReferencedPropertyNames
217+
.Select(name => firstObject.Properties[name])
218+
.Where(prop => prop != null)
219+
.Cast<PSPropertyInfo>();
220+
}
221+
222+
// Fall back to all visible properties (excluding PS* internal properties)
223+
return firstObject.Properties
224+
.Where(p => !p.Name.StartsWith("PS", StringComparison.OrdinalIgnoreCase))
225+
.ToList();
226+
}
190227
}

src/Microsoft.PowerShell.ConsoleGuiTools/OutGridViewWindow.cs

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,53 @@ public OutGridViewWindow(ApplicationData applicationData)
5454
: MARGIN_LEFT
5555
};
5656

57-
// Convert PSObjects to DataTable
58-
if (_applicationData.PSObjects is { Count: > 0 })
57+
// Convert PSObjects to DataTable using the provided format data
58+
if (_applicationData.PSObjects is { Count: > 0 } && _applicationData.FormatData is { Count: > 0 })
5959
{
60-
var typeGetter = new TypeGetter();
6160
var psObjects = _applicationData.PSObjects.Cast<PSObject>().ToList();
62-
_dataTable = TypeGetter.CastObjectsToTableView(psObjects);
61+
62+
// Create columns from the format data with format strings based on property type AND name
63+
var dataTableColumns = _applicationData.FormatData
64+
.Select(prop =>
65+
{
66+
var column = new DataTableColumn(prop.Name, $"$_.{prop.Name}");
67+
68+
// Set format string based on property type and name
69+
var propType = prop.TypeNameOfValue;
70+
var propName = prop.Name;
71+
72+
column.FormatString = propType switch
73+
{
74+
"System.DateTime" => "G", // General date/time
75+
"System.Decimal" => "N2", // Decimal with 2 decimal places
76+
"System.Double" or "System.Single" => "N2", // Floating point with 2 decimals
77+
78+
// For integers, check if it's an identifier or a quantity
79+
"System.Int32" or "System.Int64" or "System.Int16" or "System.Byte"
80+
when IsIdentifierProperty(propName) => null, // No formatting for IDs
81+
82+
"System.Int32" or "System.Int64" or "System.Int16" or "System.Byte"
83+
=> "N0", // Quantities get thousand separators
84+
85+
_ => null
86+
};
87+
88+
return column;
89+
})
90+
.ToList();
91+
92+
// Convert each object to a row
93+
var dataTableRows = new List<DataTableRow>();
94+
for (var i = 0; i < psObjects.Count; i++)
95+
{
96+
var dataTableRow = TypeGetter.CastObjectToDataTableRow(psObjects[i], _applicationData.FormatData, dataTableColumns, i);
97+
dataTableRows.Add(dataTableRow);
98+
}
99+
100+
// Set the column types based on the actual data
101+
SetTypesOnDataColumns(dataTableRows, dataTableColumns);
102+
103+
_dataTable = new DataTable(dataTableColumns, dataTableRows);
63104
}
64105
else
65106
{
@@ -81,6 +122,50 @@ public OutGridViewWindow(ApplicationData applicationData)
81122
_listView?.SetFocus();
82123
}
83124

125+
/// <summary>
126+
/// Determines if a property name represents an identifier rather than a quantity.
127+
/// Identifiers should not have thousand separators.
128+
/// </summary>
129+
/// <param name="propertyName">The name of the property.</param>
130+
/// <returns>True if the property is likely an identifier; otherwise false.</returns>
131+
private static bool IsIdentifierProperty(string propertyName)
132+
{
133+
// Common identifier property names
134+
return propertyName.Equals("Id", StringComparison.OrdinalIgnoreCase) ||
135+
propertyName.EndsWith("Id", StringComparison.OrdinalIgnoreCase) ||
136+
propertyName.Equals("PID", StringComparison.OrdinalIgnoreCase) ||
137+
propertyName.Equals("ProcessId", StringComparison.OrdinalIgnoreCase) ||
138+
propertyName.Equals("SessionId", StringComparison.OrdinalIgnoreCase) ||
139+
propertyName.Equals("SI", StringComparison.OrdinalIgnoreCase) ||
140+
propertyName.Equals("ParentProcessId", StringComparison.OrdinalIgnoreCase) ||
141+
propertyName.Equals("ThreadId", StringComparison.OrdinalIgnoreCase);
142+
}
143+
144+
/// <summary>
145+
/// Sets the data type on each column based on the values in the data rows.
146+
/// If all values in a column can be parsed as decimal, the column type is set to decimal; otherwise, it's set to
147+
/// string.
148+
/// </summary>
149+
/// <param name="dataTableRows">The list of data table rows to analyze.</param>
150+
/// <param name="dataTableColumns">The list of data table columns to update with type information.</param>
151+
private static void SetTypesOnDataColumns(List<DataTableRow> dataTableRows, List<DataTableColumn> dataTableColumns)
152+
{
153+
var dataRows = dataTableRows.Select(x => x.Values);
154+
155+
foreach (var dataColumn in dataTableColumns)
156+
dataColumn.StringType = typeof(decimal).FullName;
157+
158+
// If every value in a column could be a decimal, assume that it is supposed to be a decimal
159+
foreach (var dataRow in dataRows)
160+
{
161+
foreach (var dataColumn in dataTableColumns)
162+
{
163+
if (dataRow[dataColumn.ToString()] is not DecimalValue)
164+
dataColumn.StringType = typeof(string).FullName;
165+
}
166+
}
167+
}
168+
84169
/// <summary>
85170
/// Gets a value indicating whether the user cancelled the operation.
86171
/// </summary>

src/Microsoft.PowerShell.ConsoleGuiTools/Properties/launchSettings.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"OCGV": {
1515
"commandName": "Executable",
1616
"executablePath": "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
17-
"commandLineArgs": "-NoProfile -NoLogo -NoExit -Command \"& { Import-Module '$(TargetPath)'; Get-Process | Select-Object ProcessName, Id, Handles, NPM, PM, WS, CPU | Out-ConsoleGridView}\"",
17+
"commandLineArgs": "-NoProfile -NoLogo -NoExit -Command \"& { Import-Module '$(TargetPath)'; Get-Process | Out-ConsoleGridView}\"",
1818
"workingDirectory": "$(TargetDir)"
1919
},
2020
"OCGV -MinUi": {
@@ -28,6 +28,12 @@
2828
"executablePath": "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
2929
"commandLineArgs": "-NoProfile -NoLogo -NoExit -Command \"& { Import-Module '$(TargetPath)'; Get-Process | Show-ObjectTree }\"",
3030
"workingDirectory": "$(TargetDir)"
31+
},
32+
"OCGV Select-Object": {
33+
"commandName": "Executable",
34+
"executablePath": "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
35+
"commandLineArgs": "-NoProfile -NoLogo -NoExit -Command \"& { Import-Module '$(TargetPath)'; Get-Process | Select-Object ProcessName, Id, Handles, NPM, PM, WS, CPU | Out-ConsoleGridView}\"",
36+
"workingDirectory": "$(TargetDir)"
3137
}
3238
}
3339
}

0 commit comments

Comments
 (0)