Reputation: 36708
I am writing a PowerShell cmdlet in C#. I know one may use a formatting file to specify default output properties of a cmdlet (Writing a Windows PowerShell Formatting File) but I recently came across a more streamlined technique (i.e. just a couple lines of code; no separate formatting file needed) to do this in a scripted cmdlet (with thanks to Kirk Munro's Define default properties for custom objects, where I copied this code sample from):
$myObject = New-Object PSObject
$myObject | Add-Member NoteProperty Name ‘My Object’
$myObject | Add-Member NoteProperty Property1 1
$myObject | Add-Member NoteProperty Property2 2
$myObject | Add-Member NoteProperty Property3 3
$myObject | Add-Member NoteProperty Property4 4
$myObject | Add-Member NoteProperty Property5 5
$defaultProperties = @('Name','Property2','Property4')
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultProperties)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
$myObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers
$myObject
The last line above displays the object, showing just the specified default properties.
I have been searching for an equivalent way to do the same steps in my C# cmdlet but to no avail: I see no obvious access to PSStandardMembers
on the PSCmdlet
base class nor do I find any posts discussing the matter. Is this possible and, if so, how?
2015.03.26 Update With thanks to @Jaykul for pointing the way, for completeness I am attaching the actual C# code to do the task. I wrote this method to be reusable so along with the object you wish to "filter" you also pass in the property names of interest.
PSObject SetDefaultProperties(object obj, IEnumerable<string> defaultProperties)
{
var psObject = new PSObject(obj);
psObject.Members.Add(
new PSMemberSet("PSStandardMembers", new PSMemberInfo[]
{
new PSPropertySet("DefaultDisplayPropertySet", defaultProperties)
}));
return psObject;
}
Here is how one might use it in a cmdlet--instead of sending the full result object to WriteObject
, funnel it through the new method.
string[] DefaultProperties = {"Name", "Property2", "Property4" };
base.WriteObject(SetDefaultProperties(myObject, DefaultProperties));
Upvotes: 3
Views: 2220
Reputation: 1360
Michael, for a binary module, I agree with Joel that it would be a better design to provide a type and/or format ps1xml file with your module to define how it is rendered in PowerShell. But I really don't like that the management of that information (the type and format data) is as far removed from the definition of custom classes or objects that you might be returning from your commands (which is why, especially in the case of PowerShell commands, a solution like what I posted before makes sense -- note, however, that if you're ok with PowerShell 3.0 as a minimum requirement you should use Update-TypeData -DefaultDisplayPropertySet instead, since this is much easier than the workaround I created for that blog post before PowerShell 2.0 was even released). Having to manage ps1xml files makes maintenance of the type/format information a little more onerous than it could be, and it would be nice if this was more streamlined by having the definition of that configuration right next to the type for which it is defined. You can do this using my technique in PowerShell functions (although that technique was broken for PS 2.0 but I did find a workaround at one point that I could dig up), or by using Update-TypeData -DefaultDisplayPropertySet, but for types that are defined in c# classes inside of a binary module, I also recommend sticking with creation of ps1xml files. By using ps1xml files that are loaded via configuration data in your module's manifest (psd1) file, you are guaranteeing that those extensions will be loaded with your module and, very importantly, that they will be unloaded when your module is unloaded as well.
Revisiting this, now you've got me thinking that I should create a mechanism that allows me to define type/format information in the binary module itself, right alongside the types that are being defined, perhaps using an attribute or something similar, such that it produced the type/format ps1xml files that you would distribute as part of the package containing your binary module. Hmmmmm.....
Update about the applied solution:
Regarding Jaykul's solution (and the version you posted), that works, but you can (and should) also just call Update-TypeData -DefaultDisplayPropertySet prop1,prop2,.... That's something you can invoke easily from your binary cmdlet code, and I believe (but haven't tested myself) that it can be overridden properly using ps1xml files or other Update-TypeData calls.
Upvotes: 2
Reputation: 15824
You have to wrap the object in a PSObject, and then use the methods of that wrapper to set those properties.
using System.Management.Automation;
public class Whatever {
public string One { get { return "This is one"; } }
public string Two { get { return "This is two"; } }
public string Three { get { return "This is three"; } }
public string Four { get { return "This is four"; } }
public static PSObject Get() {
var w = new Whatever();
var pso = new PSObject(w);
var display = new PSPropertySet("DefaultDisplayPropertySet",new []{"One","Two"});
var mi = new PSMemberSet("PSStandardMembers", new[]{display});
pso.Members.Add(mi);
return pso;
}
}
To be very clear and explicit: I am answering your question, but I do not recommend this approach! I don't like this even in PowerShell scripts, although it's sometimes expedient, but I emphatically advise against doing this in compiled code.
By doing this you may be breaking some of the expectations of the ETS system. This is not a "more streamlined technique" -- it's an end-run around the system, and I believe it will override any PSTypes file the end user might create, thus subtly breaking their ability to control formatting (which is a core feature of PowerShell).
When you're creating a simple PowerShell script, that's probably excusable (since it's easy enough for someone to tweak), but when you're distributing a compiled module, you should just make the PSTypes XML file and take care of formatting the correct, extensible way so that it is modifiable by your users and their sysadmins.
Upvotes: 3