diff --git a/assets/files.wxs b/assets/files.wxs index c887be8dd61..5869628e0cd 100644 --- a/assets/files.wxs +++ b/assets/files.wxs @@ -3065,6 +3065,15 @@ + + + + + + + + + @@ -3907,6 +3916,9 @@ + + + diff --git a/build.psm1 b/build.psm1 index a94942fbd2a..782af5e5ba6 100644 --- a/build.psm1 +++ b/build.psm1 @@ -2210,7 +2210,7 @@ function Start-CrossGen { $crossGenRequiredAssemblies = @("mscorlib.dll", "System.Private.CoreLib.dll") $crossGenRequiredAssemblies += if ($Environment.IsWindows) { - "clrjit.dll" + "clrjit.dll" } elseif ($Environment.IsLinux) { "libclrjit.so" } elseif ($Environment.IsMacOS) { @@ -2269,6 +2269,7 @@ function Start-CrossGen { "Microsoft.WSMan.Management.dll", "Microsoft.WSMan.Runtime.dll", "Microsoft.PowerShell.Commands.Diagnostics.dll", + "Microsoft.PowerShell.GraphicalHost.dll", "Microsoft.Management.Infrastructure.CimCmdlets.dll" ) } diff --git a/src/Microsoft.Management.UI.Internal/CommonHelper.cs b/src/Microsoft.Management.UI.Internal/CommonHelper.cs new file mode 100644 index 00000000000..5455899beec --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/CommonHelper.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Windows; + +// Specifies the location in which theme dictionaries are stored for types in an assembly. +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] + +namespace Microsoft.Management.UI +{ + /// + /// Utilities in common in this assembly + /// + internal static class CommonHelper + { + /// + /// Restore the values from the settings to the actual window position, size and state. + /// + /// the window we are setting position and size of + /// the value for top from the user settings + /// the value for left from the user settings + /// the value for width from the user settings + /// the value for height from the user settings + /// the with used if is not valid + /// the height used if is not valid + /// true if the window is maximized in the user setting + internal static void SetStartingPositionAndSize(Window target, double userSettingTop, double userSettingLeft, double userSettingWidth, double userSettingHeight, double defaultWidth, double defaultHeight, bool userSettingMaximized) + { + bool leftInvalid = userSettingLeft < System.Windows.SystemParameters.VirtualScreenLeft || + userSettingWidth > System.Windows.SystemParameters.VirtualScreenLeft + + System.Windows.SystemParameters.VirtualScreenWidth; + + bool topInvalid = userSettingTop < System.Windows.SystemParameters.VirtualScreenTop || + userSettingTop > System.Windows.SystemParameters.VirtualScreenTop + + System.Windows.SystemParameters.VirtualScreenHeight; + + bool widthInvalid = userSettingWidth < 0 || + userSettingWidth > System.Windows.SystemParameters.VirtualScreenWidth; + + bool heightInvalid = userSettingHeight < 0 || + userSettingHeight > System.Windows.SystemParameters.VirtualScreenHeight; + + if (leftInvalid || topInvalid) + { + target.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen; + } + else + { + target.Left = userSettingLeft; + target.Top = userSettingTop; + } + + // If any saved coordinate is invalid, we set the window to the default position + if (widthInvalid || heightInvalid) + { + target.Width = defaultWidth; + target.Height = defaultHeight; + } + else + { + target.Width = userSettingWidth; + target.Height = userSettingHeight; + } + + if (userSettingMaximized) + { + target.WindowState = WindowState.Maximized; + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs new file mode 100644 index 00000000000..e459c6b3cdd --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs @@ -0,0 +1,994 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Management.Automation; +using System.Text; +using System.Windows.Documents; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Builds a help paragraph for a cmdlet + /// + internal class HelpParagraphBuilder : ParagraphBuilder + { + /// + /// Indentation size + /// + internal const int IndentSize = 4; + + /// + /// new line separators + /// + private static readonly string[] Separators = new[] { "\r\n", "\n" }; + + /// + /// Object with the cmdelt + /// + private readonly PSObject psObj; + + /// + /// Initializes a new instance of the HelpParagraphBuilder class + /// + /// paragraph being built + /// object with help information + internal HelpParagraphBuilder(Paragraph paragraph, PSObject psObj) + : base(paragraph) + { + this.psObj = psObj; + this.AddTextToParagraphBuilder(); + } + + /// + /// Enum for category of Help. + /// + private enum HelpCategory + { + Default, + DscResource, + Class + } + + /// + /// Gets the string value of a property or null if it could not be retrieved + /// + /// object with the property + /// property name + /// the string value of a property or null if it could not be retrieved + internal static string GetPropertyString(PSObject psObj, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + object value = GetPropertyObject(psObj, propertyName); + + if (value == null) + { + return null; + } + + return value.ToString(); + } + + /// + /// Adds the help text to the paragraph + /// + internal void AddTextToParagraphBuilder() + { + this.ResetAllText(); + + string strCategory = HelpParagraphBuilder.GetProperty(this.psObj, "Category").Value.ToString(); + + HelpCategory category = HelpCategory.Default; + + if (string.Compare(strCategory, "DscResource", StringComparison.OrdinalIgnoreCase) == 0) + { + category = HelpCategory.DscResource; + } + else if (string.Compare(strCategory, "Class", StringComparison.OrdinalIgnoreCase) == 0) + { + category = HelpCategory.Class; + } + + if (HelpParagraphBuilder.GetProperty(this.psObj, "Syntax") == null) + { + if (category == HelpCategory.Default) + { + // if there is no syntax, this is not the standard help + // it might be an about page + this.AddText(this.psObj.ToString(), false); + return; + } + } + + switch (category) + { + case HelpCategory.Class: + this.AddDescription(HelpWindowSettings.Default.HelpSynopsysDisplayed, HelpWindowResources.SynopsisTitle, "Introduction"); + this.AddMembers(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.PropertiesTitle); + this.AddMembers(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.MethodsTitle); + break; + case HelpCategory.DscResource: + this.AddStringSection(HelpWindowSettings.Default.HelpSynopsysDisplayed, "Synopsis", HelpWindowResources.SynopsisTitle); + this.AddDescription(HelpWindowSettings.Default.HelpDescriptionDisplayed, HelpWindowResources.DescriptionTitle, "Description"); + this.AddParameters(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.PropertiesTitle, "Properties", HelpCategory.DscResource); + break; + default: + this.AddStringSection(HelpWindowSettings.Default.HelpSynopsysDisplayed, "Synopsis", HelpWindowResources.SynopsisTitle); + this.AddDescription(HelpWindowSettings.Default.HelpDescriptionDisplayed, HelpWindowResources.DescriptionTitle, "Description"); + this.AddParameters(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.ParametersTitle, "Parameters", HelpCategory.Default); + this.AddSyntax(HelpWindowSettings.Default.HelpSyntaxDisplayed, HelpWindowResources.SyntaxTitle); + break; + } + + this.AddInputOrOutputEntries(HelpWindowSettings.Default.HelpInputsDisplayed, HelpWindowResources.InputsTitle, "inputTypes", "inputType"); + this.AddInputOrOutputEntries(HelpWindowSettings.Default.HelpOutputsDisplayed, HelpWindowResources.OutputsTitle, "returnValues", "returnValue"); + this.AddNotes(HelpWindowSettings.Default.HelpNotesDisplayed, HelpWindowResources.NotesTitle); + this.AddExamples(HelpWindowSettings.Default.HelpExamplesDisplayed, HelpWindowResources.ExamplesTitle); + this.AddNavigationLink(HelpWindowSettings.Default.HelpRelatedLinksDisplayed, HelpWindowResources.RelatedLinksTitle); + this.AddStringSection(HelpWindowSettings.Default.HelpRemarksDisplayed, "Remarks", HelpWindowResources.RemarksTitle); + } + + /// + /// Gets the object property or null if it could not be retrieved + /// + /// object with the property + /// property name + /// the object property or null if it could not be retrieved + private static PSPropertyInfo GetProperty(PSObject psObj, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + return psObj.Properties[propertyName]; + } + + /// + /// Gets a PSObject and then a value from it or null if the value could not be retrieved + /// + /// PSObject that contains another PSObject as a property + /// property name that contains the PSObject + /// property name in thye inner PSObject + /// the string from the inner psObject property or null if it could not be retrieved + private static string GetInnerPSObjectPropertyString(PSObject psObj, string psObjectName, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + PSObject innerPsObj = GetPropertyObject(psObj, psObjectName) as PSObject; + + if (innerPsObj == null) + { + return null; + } + + object value = GetPropertyObject(innerPsObj, propertyName); + + if (value == null) + { + return null; + } + + return value.ToString(); + } + + /// + /// Gets the value of a property or null if the value could not be retrieved + /// + /// object with the property + /// property name + /// the value of a property or null if the value could not be retrieved + private static object GetPropertyObject(PSObject psObj, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + PSPropertyInfo property = HelpParagraphBuilder.GetProperty(psObj, propertyName); + if (property == null) + { + return null; + } + + object value = null; + try + { + value = property.Value; + } + catch (ExtendedTypeSystemException) + { + // ignore this exception + } + + return value; + } + + /// + /// Gets the text from a property of type PSObject[] where the first object has a text property + /// + /// objhect to get text from + /// property with PSObject[] containing text + /// the text from a property of type PSObject[] where the first object has a text property + private static string GetTextFromArray(PSObject psObj, string propertyText) + { + PSObject[] introductionObjects = HelpParagraphBuilder.GetPropertyObject(psObj, propertyText) as PSObject[]; + if (introductionObjects != null && introductionObjects.Length > 0) + { + return GetPropertyString(introductionObjects[0], "text"); + } + + return null; + } + + /// + /// Returns the largest size of a group of strings + /// + /// strings to evaluate the largest size from + /// the largest size of a group of strings + private static int LargestSize(params string[] strs) + { + int returnValue = 0; + + foreach (string str in strs) + { + if (str != null && str.Length > returnValue) + { + returnValue = str.Length; + } + } + + return returnValue; + } + + /// + /// Splits the string adding indentation before each line + /// + /// string to add indentation to + /// the string indented + private static string AddIndent(string str) + { + return HelpParagraphBuilder.AddIndent(str, 1); + } + + /// + /// Splits the string adding indentation before each line + /// + /// string to add indentation to + /// number of indentations + /// the string indented + private static string AddIndent(string str, int numberOfIdents) + { + StringBuilder indent = new StringBuilder(); + indent.Append(' ', numberOfIdents * HelpParagraphBuilder.IndentSize); + return HelpParagraphBuilder.AddIndent(str, indent.ToString()); + } + + /// + /// Splits the string adding indentation before each line + /// + /// string to add indentation to + /// indentation string + /// the string indented + private static string AddIndent(string str, string indentString) + { + if (str == null) + { + return string.Empty; + } + + string[] lines = str.Split(Separators, StringSplitOptions.None); + + StringBuilder returnValue = new StringBuilder(); + foreach (string line in lines) + { + // Indentation is not localized + returnValue.AppendFormat("{0}{1}\r\n", indentString, line); + } + + if (returnValue.Length > 2) + { + // remove the last \r\n + returnValue.Remove(returnValue.Length - 2, 2); + } + + return returnValue.ToString(); + } + + /// + /// Get the object array value of a property + /// + /// object containing the property + /// property with the array value + /// the object array value of a property + private static object[] GetPropertyObjectArray(PSObject obj, string propertyName) + { + object innerObject; + if ((innerObject = HelpParagraphBuilder.GetPropertyObject(obj, propertyName)) == null) + { + return null; + } + + if (innerObject is PSObject) + { + return new[] { innerObject }; + } + + object[] innerObjectArray = innerObject as object[]; + return innerObjectArray; + } + + /// + /// Adds a section that contains only a string + /// + /// true if it should add the segment + /// name of the section to add + /// title of the section + private void AddStringSection(bool setting, string sectionName, string sectionTitle) + { + string propertyValue; + if (!setting || (propertyValue = HelpParagraphBuilder.GetPropertyString(this.psObj, sectionName)) == null) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + this.AddText(HelpParagraphBuilder.AddIndent(propertyValue), false); + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help syntax segment + /// + /// true if it should add the segment + /// title of the section + private void AddSyntax(bool setting, string sectionTitle) + { + PSObject syntaxObject; + if (!setting || (syntaxObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "Syntax") as PSObject) == null) + { + return; + } + + object[] syntaxItemsObj = HelpParagraphBuilder.GetPropertyObjectArray(syntaxObject, "syntaxItem"); + if (syntaxItemsObj == null || syntaxItemsObj.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object syntaxItemObj in syntaxItemsObj) + { + PSObject syntaxItem = syntaxItemObj as PSObject; + if (syntaxItem == null) + { + continue; + } + + string commandName = GetPropertyString(syntaxItem, "name"); + + object[] parameterObjs = HelpParagraphBuilder.GetPropertyObjectArray(syntaxItem, "parameter"); + if (commandName == null || parameterObjs == null || parameterObjs.Length == 0) + { + continue; + } + + string commandStart = string.Format(CultureInfo.CurrentCulture, "{0} ", commandName); + this.AddText(HelpParagraphBuilder.AddIndent(commandStart), false); + + foreach (object parameterObj in parameterObjs) + { + PSObject parameter = parameterObj as PSObject; + if (parameter == null) + { + continue; + } + + string parameterValue = GetPropertyString(parameter, "parameterValue"); + string position = GetPropertyString(parameter, "position"); + string required = GetPropertyString(parameter, "required"); + string parameterName = GetPropertyString(parameter, "name"); + if (position == null || required == null || parameterName == null) + { + continue; + } + + string parameterType = parameterValue == null ? string.Empty : string.Format(CultureInfo.CurrentCulture, "<{0}>", parameterValue); + + string parameterOptionalOpenBrace, parameterOptionalCloseBrace; + + if (string.Equals(required, "true", StringComparison.OrdinalIgnoreCase)) + { + parameterOptionalOpenBrace = string.Empty; + parameterOptionalCloseBrace = string.Empty; + } + else + { + parameterOptionalOpenBrace = "["; + parameterOptionalCloseBrace = "]"; + } + + string parameterNameOptionalOpenBrace, parameterNameOptionalCloseBrace; + + if (string.Equals(position, "named", StringComparison.OrdinalIgnoreCase)) + { + parameterNameOptionalOpenBrace = parameterNameOptionalCloseBrace = string.Empty; + } + else + { + parameterNameOptionalOpenBrace = "["; + parameterNameOptionalCloseBrace = "]"; + } + + string paramterPrefix = string.Format( + CultureInfo.CurrentCulture, + "{0}{1}-", + parameterOptionalOpenBrace, + parameterNameOptionalOpenBrace); + + this.AddText(paramterPrefix, false); + this.AddText(parameterName, true); + + string paramterSuffix = string.Format( + CultureInfo.CurrentCulture, + "{0} {1}{2} ", + parameterNameOptionalCloseBrace, + parameterType, + parameterOptionalCloseBrace); + this.AddText(paramterSuffix, false); + } + + string commonParametersText = string.Format( + CultureInfo.CurrentCulture, + "[<{0}>]\r\n\r\n", + HelpWindowResources.CommonParameters); + + this.AddText(commonParametersText, false); + } + + this.AddText("\r\n", false); + } + + /// + /// Adds the help description segment + /// + /// true if it should add the segment + /// title of the section + /// propertyName that has description + private void AddDescription(bool setting, string sectionTitle, string propertyName) + { + PSObject[] descriptionObjects; + if (!setting || + (descriptionObjects = HelpParagraphBuilder.GetPropertyObject(this.psObj, propertyName) as PSObject[]) == null || + descriptionObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (PSObject description in descriptionObjects) + { + string descriptionText = GetPropertyString(description, "text"); + this.AddText(HelpParagraphBuilder.AddIndent(descriptionText), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help examples segment + /// + /// true if it should add the segment + /// title of the section + private void AddExamples(bool setting, string sectionTitle) + { + if (!setting) + { + return; + } + + PSObject exampleRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "Examples") as PSObject; + if (exampleRootObject == null) + { + return; + } + + object[] exampleObjects = HelpParagraphBuilder.GetPropertyObjectArray(exampleRootObject, "example"); + if (exampleObjects == null || exampleObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object exampleObj in exampleObjects) + { + PSObject example = exampleObj as PSObject; + if (example == null) + { + continue; + } + + string introductionText = null; + introductionText = GetTextFromArray(example, "introduction"); + + string codeText = GetPropertyString(example, "code"); + string title = GetPropertyString(example, "title"); + + if (codeText == null) + { + continue; + } + + if (title != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(title), false); + this.AddText("\r\n", false); + } + + string codeLine = string.Format( + CultureInfo.CurrentCulture, + "{0}{1}\r\n\r\n", + introductionText, + codeText); + + this.AddText(HelpParagraphBuilder.AddIndent(codeLine), false); + + PSObject[] remarks = HelpParagraphBuilder.GetPropertyObject(example, "remarks") as PSObject[]; + if (remarks == null) + { + continue; + } + + foreach (PSObject remark in remarks) + { + string remarkText = GetPropertyString(remark, "text"); + if (remarkText == null) + { + continue; + } + + this.AddText(remarkText, false); + this.AddText("\r\n", false); + } + } + + this.AddText("\r\n\r\n", false); + } + + private void AddMembers(bool setting, string sectionTitle) + { + if (!setting || string.IsNullOrEmpty(sectionTitle)) + { + return; + } + + PSObject memberRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "Members") as PSObject; + if (memberRootObject == null) + { + return; + } + + object[] memberObjects = HelpParagraphBuilder.GetPropertyObjectArray(memberRootObject, "member"); + + if (memberObjects == null) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object memberObj in memberObjects) + { + string description = null; + string memberText = null; + + PSObject member = memberObj as PSObject; + if (member == null) + { + continue; + } + + string name = GetPropertyString(member, "title"); + string type = GetPropertyString(member, "type"); + string propertyType = null; + + if (string.Compare("field", type, StringComparison.OrdinalIgnoreCase) == 0) + { + PSObject fieldData = HelpParagraphBuilder.GetPropertyObject(member, "fieldData") as PSObject; + + if (fieldData != null) + { + PSObject propertyTypeObject = HelpParagraphBuilder.GetPropertyObject(fieldData, "type") as PSObject; + if (propertyTypeObject != null) + { + propertyType = GetPropertyString(propertyTypeObject, "name"); + description = GetPropertyString(propertyTypeObject, "description"); + } + + memberText = string.Format(CultureInfo.CurrentCulture, " [{0}] {1}\r\n", propertyType, name); + } + } + else if (string.Compare("method", type, StringComparison.OrdinalIgnoreCase) == 0) + { + FormatMethodData(member, name, out memberText, out description); + } + + if (!string.IsNullOrEmpty(memberText)) + { + this.AddText(HelpParagraphBuilder.AddIndent(string.Empty), false); + this.AddText(memberText, true); + + if (description != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(description, 2), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n", false); + } + } + } + + private static void FormatMethodData(PSObject member, string name, out string memberText, out string description) + { + memberText = null; + description = null; + + if (member == null || string.IsNullOrEmpty(name)) + { + return; + } + + string returnType = null; + StringBuilder parameterText = new StringBuilder(); + + // Get method return type + PSObject returnTypeObject = HelpParagraphBuilder.GetPropertyObject(member, "returnValue") as PSObject; + if (returnTypeObject != null) + { + PSObject returnTypeData = HelpParagraphBuilder.GetPropertyObject(returnTypeObject, "type") as PSObject; + if (returnTypeData != null) + { + returnType = GetPropertyString(returnTypeData, "name"); + } + } + + // Get method description. + PSObject[] methodDescriptions = HelpParagraphBuilder.GetPropertyObject(member, "introduction") as PSObject[]; + if (methodDescriptions != null) + { + foreach (var methodDescription in methodDescriptions) + { + description = GetPropertyString(methodDescription, "Text"); + + // If we get an text we do not need to iterate more. + if (!string.IsNullOrEmpty(description)) + { + break; + } + } + } + + // Get method parameters. + PSObject parametersObject = HelpParagraphBuilder.GetPropertyObject(member, "parameters") as PSObject; + if (parametersObject != null) + { + PSObject[] paramObject = HelpParagraphBuilder.GetPropertyObject(parametersObject, "parameter") as PSObject[]; + + if (paramObject != null) + { + foreach (var param in paramObject) + { + string parameterName = GetPropertyString(param, "name"); + string parameterType = null; + + PSObject parameterTypeData = HelpParagraphBuilder.GetPropertyObject(param, "type") as PSObject; + + if (parameterTypeData != null) + { + parameterType = GetPropertyString(parameterTypeData, "name"); + + // If there is no type for the paramter, we expect it is System.Object + if (string.IsNullOrEmpty(parameterType)) + { + parameterType = "object"; + } + } + + string paramString = string.Format(CultureInfo.CurrentCulture, "[{0}] ${1},", parameterType, parameterName); + + parameterText.Append(paramString); + } + + if (string.Compare(parameterText[parameterText.Length - 1].ToString(), ",", StringComparison.OrdinalIgnoreCase) == 0) + { + parameterText = parameterText.Remove(parameterText.Length - 1, 1); + } + } + } + + memberText = string.Format(CultureInfo.CurrentCulture, " [{0}] {1}({2})\r\n", returnType, name, parameterText); + } + + /// + /// Adds the help parameters segment + /// + /// true if it should add the segment + /// title of the section + /// name of the property which has properties + /// category of help + private void AddParameters(bool setting, string sectionTitle, string paramPropertyName, HelpCategory helpCategory) + { + if (!setting) + { + return; + } + + PSObject parameterRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, paramPropertyName) as PSObject; + if (parameterRootObject == null) + { + return; + } + + object[] parameterObjects = null; + + // Root object for Class has members not parameters. + if (helpCategory != HelpCategory.Class) + { + parameterObjects = HelpParagraphBuilder.GetPropertyObjectArray(parameterRootObject, "parameter"); + } + + if (parameterObjects == null || parameterObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object parameterObj in parameterObjects) + { + PSObject parameter = parameterObj as PSObject; + if (parameter == null) + { + continue; + } + + string parameterValue = GetPropertyString(parameter, "parameterValue"); + string name = GetPropertyString(parameter, "name"); + string description = GetTextFromArray(parameter, "description"); + string required = GetPropertyString(parameter, "required"); + string position = GetPropertyString(parameter, "position"); + string pipelineinput = GetPropertyString(parameter, "pipelineInput"); + string defaultValue = GetPropertyString(parameter, "defaultValue"); + string acceptWildcard = GetPropertyString(parameter, "globbing"); + + if (string.IsNullOrEmpty(name)) + { + continue; + } + + if (helpCategory == HelpCategory.DscResource) + { + this.AddText(HelpParagraphBuilder.AddIndent(string.Empty), false); + } + else + { + this.AddText(HelpParagraphBuilder.AddIndent("-"), false); + } + + this.AddText(name, true); + string parameterText = string.Format( + CultureInfo.CurrentCulture, + " <{0}>\r\n", + parameterValue); + + this.AddText(parameterText, false); + + if (description != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(description, 2), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n", false); + + int largestSize = HelpParagraphBuilder.LargestSize( + HelpWindowResources.ParameterRequired, + HelpWindowResources.ParameterPosition, + HelpWindowResources.ParameterDefaultValue, + HelpWindowResources.ParameterPipelineInput, + HelpWindowResources.ParameterAcceptWildcard); + + // justification of parameter values is not localized + string formatString = string.Format( + CultureInfo.CurrentCulture, + "{{0,-{0}}}{{1}}", + largestSize + 2); + + string tableLine; + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterRequired, + required); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + // these are not applicable for Dsc Resource help + if (helpCategory != HelpCategory.DscResource) + { + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterPosition, + position); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterDefaultValue, + defaultValue); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterPipelineInput, + pipelineinput); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterAcceptWildcard, + acceptWildcard); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + } + + this.AddText("\r\n\r\n", false); + } + + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help navigation links segment + /// + /// true if it should add the segment + /// title of the section + private void AddNavigationLink(bool setting, string sectionTitle) + { + if (!setting) + { + return; + } + + PSObject linkRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "RelatedLinks") as PSObject; + if (linkRootObject == null) + { + return; + } + + PSObject[] linkObjects; + + if ((linkObjects = HelpParagraphBuilder.GetPropertyObject(linkRootObject, "navigationLink") as PSObject[]) == null || + linkObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (PSObject linkObject in linkObjects) + { + string text = GetPropertyString(linkObject, "linkText"); + string uri = GetPropertyString(linkObject, "uri"); + + string linkLine = string.IsNullOrEmpty(uri) ? text : string.Format( + CultureInfo.CurrentCulture, + HelpWindowResources.LinkTextFormat, + text, + uri); + + this.AddText(HelpParagraphBuilder.AddIndent(linkLine), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help input or output segment + /// + /// true if it should add the segment + /// title of the section + /// property with the outter object + /// property with the inner object + private void AddInputOrOutputEntries(bool setting, string sectionTitle, string inputOrOutputProperty, string inputOrOutputInnerProperty) + { + if (!setting) + { + return; + } + + PSObject rootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, inputOrOutputProperty) as PSObject; + if (rootObject == null) + { + return; + } + + object[] inputOrOutputObjs; + inputOrOutputObjs = HelpParagraphBuilder.GetPropertyObjectArray(rootObject, inputOrOutputInnerProperty); + + if (inputOrOutputObjs == null || inputOrOutputObjs.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object inputOrOutputObj in inputOrOutputObjs) + { + PSObject inputOrOutput = inputOrOutputObj as PSObject; + if (inputOrOutput == null) + { + continue; + } + + string type = HelpParagraphBuilder.GetInnerPSObjectPropertyString(inputOrOutput, "type", "name"); + string description = GetTextFromArray(inputOrOutput, "description"); + + this.AddText(HelpParagraphBuilder.AddIndent(type), false); + this.AddText("\r\n", false); + if (description != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(description), false); + this.AddText("\r\n", false); + } + } + + this.AddText("\r\n", false); + } + + /// + /// Adds the help notes segment + /// + /// true if it should add the segment + /// title of the section + private void AddNotes(bool setting, string sectionTitle) + { + if (!setting) + { + return; + } + + PSObject rootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "alertSet") as PSObject; + if (rootObject == null) + { + return; + } + + string note = GetTextFromArray(rootObject, "alert"); + + if (note == null) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + this.AddText(HelpParagraphBuilder.AddIndent(note), false); + this.AddText("\r\n\r\n", false); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs new file mode 100644 index 00000000000..2c5a018f679 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs @@ -0,0 +1,283 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Management.Automation; +using System.Windows.Documents; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// ViewModel for the Help Dialog used to: + /// build the help document + /// search the help document + /// offer text for labels + /// + internal class HelpViewModel : INotifyPropertyChanged + { + /// + /// The builder for the help FlowDocument Paragraph used in a RichEditText control + /// + private readonly HelpParagraphBuilder helpBuilder; + + /// + /// Searcher for selecting current matches in paragraph text + /// + private readonly ParagraphSearcher searcher; + + /// + /// Title of the help window + /// + private readonly string helpTitle; + + /// + /// the zoom bound to the zoom slider value + /// + private double zoom = 100; + + /// + /// Text to be found. This is bound to the find TextBox + /// + private string findText; + + /// + /// text for the number of matches found + /// + private string matchesLabel; + + /// + /// Initializes a new instance of the HelpViewModel class + /// + /// object containing help + /// paragraph in which help text is built/searched + internal HelpViewModel(PSObject psObj, Paragraph documentParagraph) + { + Debug.Assert(psObj != null, "ensured by caller"); + Debug.Assert(documentParagraph != null, "ensured by caller"); + + this.helpBuilder = new HelpParagraphBuilder(documentParagraph, psObj); + this.helpBuilder.BuildParagraph(); + this.searcher = new ParagraphSearcher(); + this.helpBuilder.PropertyChanged += new PropertyChangedEventHandler(this.HelpBuilder_PropertyChanged); + this.helpTitle = string.Format( + CultureInfo.CurrentCulture, + HelpWindowResources.HelpTitleFormat, + HelpParagraphBuilder.GetPropertyString(psObj, "name")); + } + + #region INotifyPropertyChanged Members + /// + /// Used to notify of property changes + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Gets or sets the Zoom bound to the zoom slider value + /// + public double Zoom + { + get + { + return this.zoom; + } + + set + { + this.zoom = value; + this.OnNotifyPropertyChanged("Zoom"); + this.OnNotifyPropertyChanged("ZoomLabel"); + this.OnNotifyPropertyChanged("ZoomLevel"); + } + } + + /// + /// Gets the value bound to the RichTextEdit zoom, which is calculated based on the zoom + /// + public double ZoomLevel + { + get + { + return this.zoom / 100.0; + } + } + + /// + /// Gets the label to be displayed for the zoom + /// + public string ZoomLabel + { + get + { + return string.Format(CultureInfo.CurrentCulture, HelpWindowResources.ZoomLabelTextFormat, this.zoom); + } + } + + /// + /// Gets or sets the text to be found + /// + public string FindText + { + get + { + return this.findText; + } + + set + { + this.findText = value; + this.Search(); + this.SetMatchesLabel(); + } + } + + /// + /// Gets the title of the window + /// + public string HelpTitle + { + get + { + return this.helpTitle; + } + } + + /// + /// Gets or sets the label for current matches + /// + public string MatchesLabel + { + get + { + return this.matchesLabel; + } + + set + { + this.matchesLabel = value; + this.OnNotifyPropertyChanged("MatchesLabel"); + } + } + + /// + /// Gets a value indicating whether there are matches to go to + /// + public bool CanGoToNextOrPrevious + { + get + { + return this.HelpBuilder.HighlightCount != 0; + } + } + + /// + /// Gets the searcher for selecting current matches in paragraph text + /// + internal ParagraphSearcher Searcher + { + get { return this.searcher; } + } + + /// + /// Gets the paragraph builder used to write help content + /// + internal HelpParagraphBuilder HelpBuilder + { + get { return this.helpBuilder; } + } + + /// + /// Highlights all matches to this.findText + /// Called when findText changes or whenever the search has to be refreshed + /// + internal void Search() + { + this.HelpBuilder.HighlightAllInstancesOf(this.findText, HelpWindowSettings.Default.HelpSearchMatchCase, HelpWindowSettings.Default.HelpSearchWholeWord); + this.searcher.ResetSearch(); + } + + /// + /// Increases Zoom if not above maximum + /// + internal void ZoomIn() + { + if (this.Zoom + HelpWindow.ZoomInterval <= HelpWindow.MaximumZoom) + { + this.Zoom += HelpWindow.ZoomInterval; + } + } + + /// + /// Decreases Zoom if not below minimum + /// + internal void ZoomOut() + { + if (this.Zoom - HelpWindow.ZoomInterval >= HelpWindow.MinimumZoom) + { + this.Zoom -= HelpWindow.ZoomInterval; + } + } + + /// + /// Called to update the matches label + /// + /// event sender + /// event arguments + private void HelpBuilder_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "HighlightCount") + { + this.SetMatchesLabel(); + this.OnNotifyPropertyChanged("CanGoToNextOrPrevious"); + } + } + + /// + /// Sets the current matches label + /// + private void SetMatchesLabel() + { + if (this.findText == null || this.findText.Trim().Length == 0) + { + this.MatchesLabel = string.Empty; + } + else + { + if (this.HelpBuilder.HighlightCount == 0) + { + this.MatchesLabel = HelpWindowResources.NoMatches; + } + else + { + if (this.HelpBuilder.HighlightCount == 1) + { + this.MatchesLabel = HelpWindowResources.OneMatch; + } + else + { + this.MatchesLabel = string.Format( + CultureInfo.CurrentCulture, + HelpWindowResources.SomeMatchesFormat, + this.HelpBuilder.HighlightCount); + } + } + } + } + + /// + /// Called internally to notify when a proiperty changed + /// + /// property name + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml new file mode 100644 index 00000000000..b30ddfdbbfe --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml.cs new file mode 100644 index 00000000000..38f1f317d20 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml.cs @@ -0,0 +1,318 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Globalization; +using System.Management.Automation; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Input; + +using Microsoft.Management.UI.Internal; + +namespace Microsoft.Management.UI +{ + /// + /// A window displaying help content and allowing search + /// + public partial class HelpWindow : Window + { + /// + /// Minimum zoom in the slider + /// + public static double MinimumZoom + { + get + { + return 20; + } + } + + /// + /// Maximum zoom in the slider + /// + public static double MaximumZoom + { + get + { + return 300; + } + } + + /// + /// Zoom interval + /// + public static double ZoomInterval + { + get + { + return 10; + } + } + + /// + /// The ViewModel for the dialog + /// + private readonly HelpViewModel viewModel; + + /// + /// Initializes a new instance of the HelpWindow class + /// + /// the object with help information + public HelpWindow(PSObject helpObject) + { + InitializeComponent(); + this.viewModel = new HelpViewModel(helpObject, this.DocumentParagraph); + CommonHelper.SetStartingPositionAndSize( + this, + HelpWindowSettings.Default.HelpWindowTop, + HelpWindowSettings.Default.HelpWindowLeft, + HelpWindowSettings.Default.HelpWindowWidth, + HelpWindowSettings.Default.HelpWindowHeight, + double.Parse((string)HelpWindowSettings.Default.Properties["HelpWindowWidth"].DefaultValue, CultureInfo.InvariantCulture.NumberFormat), + double.Parse((string)HelpWindowSettings.Default.Properties["HelpWindowHeight"].DefaultValue, CultureInfo.InvariantCulture.NumberFormat), + HelpWindowSettings.Default.HelpWindowMaximized); + + this.ReadZoomUserSetting(); + + this.viewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.ViewModel_PropertyChanged); + this.DataContext = this.viewModel; + + this.Loaded += new RoutedEventHandler(this.HelpDialog_Loaded); + this.Closed += new System.EventHandler(this.HelpDialog_Closed); + } + + /// + /// Handles the mouse wheel to zoom in/out + /// + /// event arguments + protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) + { + if (Keyboard.Modifiers != ModifierKeys.Control) + { + return; + } + + if (e.Delta > 0) + { + this.viewModel.ZoomIn(); + e.Handled = true; + } + else + { + this.viewModel.ZoomOut(); + e.Handled = true; + } + } + + /// + /// Handles key down to fix the Page/Douwn going to end of help issue + /// And to implement some additional shortcuts like Ctrl+F and ZoomIn/ZoomOut + /// + /// event arguments + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + if (Keyboard.Modifiers == ModifierKeys.None) + { + if (e.Key == Key.PageUp) + { + this.Scroll.PageUp(); + e.Handled = true; + return; + } + + if (e.Key == Key.PageDown) + { + this.Scroll.PageDown(); + e.Handled = true; + return; + } + } + + if (Keyboard.Modifiers == ModifierKeys.Control) + { + this.HandleZoomInAndZoomOut(e); + if (e.Handled) + { + return; + } + + if (e.Key == Key.F) + { + this.Find.Focus(); + e.Handled = true; + return; + } + } + + if (Keyboard.Modifiers == (ModifierKeys.Control | ModifierKeys.Shift)) + { + this.HandleZoomInAndZoomOut(e); + } + } + + /// + /// Reads the zoom part of the user settings + /// + private void ReadZoomUserSetting() + { + if (HelpWindowSettings.Default.HelpZoom < HelpWindow.MinimumZoom || HelpWindowSettings.Default.HelpZoom > HelpWindow.MaximumZoom) + { + HelpWindowSettings.Default.HelpZoom = 100; + } + + this.viewModel.Zoom = HelpWindowSettings.Default.HelpZoom; + } + + /// + /// Handles Zoom in and Zoom out keys + /// + /// event arguments + private void HandleZoomInAndZoomOut(KeyEventArgs e) + { + if (e.Key == Key.OemPlus || e.Key == Key.Add) + { + this.viewModel.ZoomIn(); + e.Handled = true; + } + + if (e.Key == Key.OemMinus || e.Key == Key.Subtract) + { + this.viewModel.ZoomOut(); + e.Handled = true; + } + } + + /// + /// Listens to changes in the zoom in order to update the user settings + /// + /// event sender + /// event arguments + private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == "Zoom") + { + HelpWindowSettings.Default.HelpZoom = this.viewModel.Zoom; + } + } + + /// + /// Saves the user settings + /// + /// event sender + /// event arguments + private void HelpDialog_Closed(object sender, System.EventArgs e) + { + HelpWindowSettings.Default.Save(); + } + + /// + /// Updates the user setting with window state + /// + /// event sender + /// event arguments + private void HelpDialog_StateChanged(object sender, System.EventArgs e) + { + HelpWindowSettings.Default.HelpWindowMaximized = this.WindowState == WindowState.Maximized; + } + + /// + /// Sets the positions from user settings and start monitoring position changes + /// + /// event sender + /// event arguments + private void HelpDialog_Loaded(object sender, RoutedEventArgs e) + { + this.StateChanged += new System.EventHandler(this.HelpDialog_StateChanged); + this.LocationChanged += new System.EventHandler(this.HelpDialog_LocationChanged); + this.SizeChanged += new SizeChangedEventHandler(this.HelpDialog_SizeChanged); + } + + /// + /// Saves size changes in user settings + /// + /// event sender + /// event arguments + private void HelpDialog_SizeChanged(object sender, SizeChangedEventArgs e) + { + HelpWindowSettings.Default.HelpWindowWidth = this.Width; + HelpWindowSettings.Default.HelpWindowHeight = this.Height; + } + + /// + /// Saves position changes in user settings + /// + /// event sender + /// event arguments + private void HelpDialog_LocationChanged(object sender, System.EventArgs e) + { + HelpWindowSettings.Default.HelpWindowTop = this.Top; + HelpWindowSettings.Default.HelpWindowLeft = this.Left; + } + + /// + /// Called when the settings button is clicked + /// + /// event sender + /// event arguments + private void Settings_Click(object sender, RoutedEventArgs e) + { + SettingsDialog settings = new SettingsDialog(); + settings.Owner = this; + + settings.ShowDialog(); + + if (settings.DialogResult == true) + { + this.viewModel.HelpBuilder.AddTextToParagraphBuilder(); + this.viewModel.Search(); + } + } + + /// + /// Called when the Previous button is clicked + /// + /// event sender + /// event arguments + private void PreviousMatch_Click(object sender, RoutedEventArgs e) + { + this.MoveToNextMatch(false); + } + + /// + /// Called when the Next button is clicked + /// + /// event sender + /// event arguments + private void NextMatch_Click(object sender, RoutedEventArgs e) + { + this.MoveToNextMatch(true); + } + + /// + /// Moves to the previous or next match + /// + /// true for forward false for backwards + private void MoveToNextMatch(bool forward) + { + TextPointer caretPosition = this.HelpText.CaretPosition; + Run nextRun = this.viewModel.Searcher.MoveAndHighlightNextNextMatch(forward, caretPosition); + this.MoveToRun(nextRun); + } + + /// + /// Moves to the caret and brings the view to the + /// + /// run to move to + private void MoveToRun(Run run) + { + if (run == null) + { + return; + } + + run.BringIntoView(); + this.HelpText.CaretPosition = run.ElementEnd; + this.HelpText.Focus(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs new file mode 100644 index 00000000000..03f214dd60d --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.16598 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// namespace Microsoft.Management.UI.Internal { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class HelpWindowSettings : global::System.Configuration.ApplicationSettingsBase { + + private static HelpWindowSettings defaultInstance = ((HelpWindowSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new HelpWindowSettings()))); + + public static HelpWindowSettings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpRemarksDisplayed + { + get + { + return ((bool)(this["HelpRemarksDisplayed"])); + } + set + { + this["HelpRemarksDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpSyntaxDisplayed { + get { + return ((bool)(this["HelpSyntaxDisplayed"])); + } + set { + this["HelpSyntaxDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpExamplesDisplayed { + get { + return ((bool)(this["HelpExamplesDisplayed"])); + } + set { + this["HelpExamplesDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpSynopsysDisplayed { + get { + return ((bool)(this["HelpSynopsysDisplayed"])); + } + set { + this["HelpSynopsysDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpDescriptionDisplayed { + get { + return ((bool)(this["HelpDescriptionDisplayed"])); + } + set { + this["HelpDescriptionDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpParametersDisplayed { + get { + return ((bool)(this["HelpParametersDisplayed"])); + } + set { + this["HelpParametersDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpInputsDisplayed { + get { + return ((bool)(this["HelpInputsDisplayed"])); + } + set { + this["HelpInputsDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpOutputsDisplayed { + get { + return ((bool)(this["HelpOutputsDisplayed"])); + } + set { + this["HelpOutputsDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpNotesDisplayed { + get { + return ((bool)(this["HelpNotesDisplayed"])); + } + set { + this["HelpNotesDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpRelatedLinksDisplayed { + get { + return ((bool)(this["HelpRelatedLinksDisplayed"])); + } + set { + this["HelpRelatedLinksDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool HelpSearchMatchCase { + get { + return ((bool)(this["HelpSearchMatchCase"])); + } + set { + this["HelpSearchMatchCase"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool HelpSearchWholeWord { + get { + return ((bool)(this["HelpSearchWholeWord"])); + } + set { + this["HelpSearchWholeWord"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("400")] + public double HelpWindowHeight { + get { + return ((double)(this["HelpWindowHeight"])); + } + set { + this["HelpWindowHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("600")] + public double HelpWindowWidth { + get { + return ((double)(this["HelpWindowWidth"])); + } + set { + this["HelpWindowWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double HelpWindowTop { + get { + return ((double)(this["HelpWindowTop"])); + } + set { + this["HelpWindowTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double HelpWindowLeft { + get { + return ((double)(this["HelpWindowLeft"])); + } + set { + this["HelpWindowLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool HelpWindowMaximized { + get { + return ((bool)(this["HelpWindowMaximized"])); + } + set { + this["HelpWindowMaximized"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("100")] + public double HelpZoom { + get { + return ((double)(this["HelpZoom"])); + } + set { + this["HelpZoom"] = value; + } + } + } +//} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.settings b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.settings new file mode 100644 index 00000000000..4de65371758 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.settings @@ -0,0 +1,60 @@ + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + -1 + + + -1 + + + False + + + 100 + + + 500 + + + 700 + + + True + + + \ No newline at end of file diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphBuilder.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphBuilder.cs new file mode 100644 index 00000000000..eebc03afafe --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphBuilder.cs @@ -0,0 +1,377 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; +using System.Windows.Documents; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Builds a paragraph based on Text + Bold + Highlight information. + /// Bold are the segments of thexct that should be bold, and Highlight are + /// the segments of thext that should be highlighted (like search results). + /// + internal class ParagraphBuilder : INotifyPropertyChanged + { + /// + /// The text spans that should be bold + /// + private readonly List boldSpans; + + /// + /// The text spans that should be highlighted + /// + private readonly List highlightedSpans; + + /// + /// The text displayed + /// + private readonly StringBuilder textBuilder; + + /// + /// Paragraph built in BuildParagraph + /// + private readonly Paragraph paragraph; + + /// + /// Initializes a new instance of the ParagraphBuilder class + /// + /// paragraph we will be adding lines to in BuildParagraph + internal ParagraphBuilder(Paragraph paragraph) + { + if (paragraph == null) + { + throw new ArgumentNullException("paragraph"); + } + + this.paragraph = paragraph; + this.boldSpans = new List(); + this.highlightedSpans = new List(); + this.textBuilder = new StringBuilder(); + } + + #region INotifyPropertyChanged Members + /// + /// Used to notify of property changes + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Gets the number of highlights. + /// + internal int HighlightCount + { + get { return this.highlightedSpans.Count; } + } + + /// + /// Gets the paragraph built in BuildParagraph + /// + internal Paragraph Paragraph + { + get { return this.paragraph; } + } + + /// + /// Called after all the AddText calls have been made to build the paragraph + /// based on the current text. + /// This method goes over 3 collections simultaneouslly: + /// 1) characters in this.textBuilder + /// 2) spans in this.boldSpans + /// 3) spans in this.highlightedSpans + /// And adds the minimal number of Inlines to the paragraph so that all + /// characters that should be bold and/or highlighed are. + /// + internal void BuildParagraph() + { + this.paragraph.Inlines.Clear(); + + int currentBoldIndex = 0; + TextSpan? currentBoldSpan = this.boldSpans.Count == 0 ? (TextSpan?)null : this.boldSpans[0]; + int currentHighlightedIndex = 0; + TextSpan? currentHighlightedSpan = this.highlightedSpans.Count == 0 ? (TextSpan?)null : this.highlightedSpans[0]; + + bool currentBold = false; + bool currentHighlighted = false; + + StringBuilder sequence = new StringBuilder(); + int i = 0; + foreach (char c in this.textBuilder.ToString()) + { + bool newBold = false; + bool newHighlighted = false; + + ParagraphBuilder.MoveSpanToPosition(ref currentBoldIndex, ref currentBoldSpan, i, this.boldSpans); + newBold = currentBoldSpan == null ? false : currentBoldSpan.Value.Contains(i); + + ParagraphBuilder.MoveSpanToPosition(ref currentHighlightedIndex, ref currentHighlightedSpan, i, this.highlightedSpans); + newHighlighted = currentHighlightedSpan == null ? false : currentHighlightedSpan.Value.Contains(i); + + if (newBold != currentBold || newHighlighted != currentHighlighted) + { + ParagraphBuilder.AddInline(this.paragraph, currentBold, currentHighlighted, sequence); + } + + sequence.Append(c); + + currentHighlighted = newHighlighted; + currentBold = newBold; + i++; + } + + ParagraphBuilder.AddInline(this.paragraph, currentBold, currentHighlighted, sequence); + } + + /// + /// Highlights all ocurrences of . + /// This is called after all calls to AddText have been made + /// + /// search string + /// true if search should be case sensitive + /// true if we should search whole word only + internal void HighlightAllInstancesOf(string search, bool caseSensitive, bool wholeWord) + { + this.highlightedSpans.Clear(); + + if (search == null || search.Trim().Length == 0) + { + this.BuildParagraph(); + this.OnNotifyPropertyChanged("HighlightCount"); + return; + } + + string text = this.textBuilder.ToString(); + StringComparison comparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + int start = 0; + int match; + while ((match = text.IndexOf(search, start, comparison)) != -1) + { + // false loop + do + { + if (wholeWord) + { + if (match > 0 && char.IsLetterOrDigit(text[match - 1])) + { + break; + } + + if ((match + search.Length <= text.Length - 1) && char.IsLetterOrDigit(text[match + search.Length])) + { + break; + } + } + + this.AddHighlight(match, search.Length); + } + while (false); + + start = match + search.Length; + } + + this.BuildParagraph(); + this.OnNotifyPropertyChanged("HighlightCount"); + } + + /// + /// Adds text to the paragraph later build with BuildParagraph + /// + /// text to be added + /// true if the text should be bold + internal void AddText(string str, bool bold) + { + if (str == null) + { + throw new ArgumentNullException("str"); + } + + if (str.Length == 0) + { + return; + } + + if (bold) + { + this.boldSpans.Add(new TextSpan(this.textBuilder.Length, str.Length)); + } + + this.textBuilder.Append(str); + } + + /// + /// Called before a derived class starts adding text + /// to reset the current content + /// + internal void ResetAllText() + { + this.boldSpans.Clear(); + this.highlightedSpans.Clear(); + this.textBuilder.Clear(); + } + + /// + /// Adds an inline to based on the remaining parameters. + /// + /// paragraph to add Inline to + /// true if text should be added in bold + /// true if the text should be added with highlight + /// the text to add and clear + private static void AddInline(Paragraph currentParagraph, bool currentBold, bool currentHighlighted, StringBuilder sequence) + { + if (sequence.Length == 0) + { + return; + } + + Run run = new Run(sequence.ToString()); + if (currentHighlighted) + { + run.Background = ParagraphSearcher.HighlightBrush; + } + + Inline inline = currentBold ? (Inline)new Bold(run) : run; + currentParagraph.Inlines.Add(inline); + sequence.Clear(); + } + + /// + /// This is an auxiliar method in BuildParagraph to move the current bold or highlighed spans + /// according to the + /// The current bold and higlighed span should be ending ahead of the current position. + /// Moves and to the + /// propper span in according to the + /// This is an auxiliar method in BuildParagraph. + /// + /// current index within + /// current span within + /// caracter position. This comes from a position within this.textBuilder + /// the collection of spans. This is either this.boldSpans or this.highlightedSpans + private static void MoveSpanToPosition(ref int currentSpanIndex, ref TextSpan? currentSpan, int caracterPosition, List allSpans) + { + if (currentSpan == null || caracterPosition <= currentSpan.Value.End) + { + return; + } + + for (int newBoldIndex = currentSpanIndex + 1; newBoldIndex < allSpans.Count; newBoldIndex++) + { + TextSpan newBoldSpan = allSpans[newBoldIndex]; + if (caracterPosition <= newBoldSpan.End) + { + currentSpanIndex = newBoldIndex; + currentSpan = newBoldSpan; + return; + } + } + + // there is no span ending ahead of current position, so + // we set the current span to null to prevent unecessary comparisons against the currentSpan + currentSpan = null; + } + + /// + /// Adds one individual text highlight + /// This is called after all calls to AddText have been made + /// + /// highlight start + /// highlight length + private void AddHighlight(int start, int length) + { + if (start < 0) + { + throw new ArgumentOutOfRangeException("start"); + } + + if (start + length > this.textBuilder.Length) + { + throw new ArgumentOutOfRangeException("length"); + } + + this.highlightedSpans.Add(new TextSpan(start, length)); + } + + /// + /// Called internally to notify when a proiperty changed + /// + /// property name + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + /// + /// A text span used to mark bold and highlighed segments + /// + internal struct TextSpan + { + /// + /// Index of the first character in the span + /// + private readonly int start; + + /// + /// Index of the last character in the span + /// + private readonly int end; + + /// + /// Initializes a new instance of the TextSpan struct + /// + /// Index of the first character in the span + /// Index of the last character in the span + internal TextSpan(int start, int length) + { + if (start < 0) + { + throw new ArgumentOutOfRangeException("start"); + } + + if (length < 1) + { + throw new ArgumentOutOfRangeException("length"); + } + + this.start = start; + this.end = start + length - 1; + } + + /// + /// Gets the index of the first character in the span + /// + internal int Start + { + get { return this.start; } + } + + /// + /// Gets the index of the first character in the span + /// + internal int End + { + get + { + return this.end; + } + } + + /// + /// Returns true if the is between start and end (inclusive) + /// + /// position to verify if is in the span + /// true if the is between start and end (inclusive) + internal bool Contains(int position) + { + return (position >= this.start) && (position <= this.end); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphSearcher.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphSearcher.cs new file mode 100644 index 00000000000..055f05a98b2 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphSearcher.cs @@ -0,0 +1,241 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics; +using System.Windows.Documents; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Moves through search highlights built in a ParagraphBuilder + /// changing the color of the current highlight + /// + internal class ParagraphSearcher + { + /// + /// Highlight for all matches except the current + /// + internal static readonly Brush HighlightBrush = Brushes.Yellow; + + /// + /// Highlight for the current match + /// + private static readonly Brush CurrentHighlightBrush = Brushes.Cyan; + + /// + /// Current match being highlighted in search + /// + private Run currentHighlightedMatch; + + /// + /// Initializes a new instance of the ParagraphSearcher class + /// + internal ParagraphSearcher() + { + } + + /// + /// Move to the next highlight starting at the + /// + /// true for next false for previous + /// caret position + /// the next highlight starting at the + internal Run MoveAndHighlightNextNextMatch(bool forward, TextPointer caretPosition) + { + Debug.Assert(caretPosition != null, "a caret position is allways valid"); + Debug.Assert(caretPosition.Parent != null && caretPosition.Parent is Run, "a caret PArent is allways a valid Run"); + Run caretRun = (Run)caretPosition.Parent; + + Run currentRun; + + if (this.currentHighlightedMatch != null) + { + // restore the curent highlighted background to plain highlighted + this.currentHighlightedMatch.Background = ParagraphSearcher.HighlightBrush; + } + + // If the caret is in the end of a highlight we move to the adjacent run + // It has to be in the end because if there is a match at the begining of the file + // and the caret has not been touched (so it is in the beginning of the file too) + // we want to highlight this first match. + // Considering the caller allways set the caret to the end of the highlight + // The condition below works well for successive searchs + // We also need to move to the adjacent run if the caret is at the first run and we + // are moving backwards so that a search backwards when the first run is highlighted + // and the caret is at the beginning will wrap to the end + if ((!forward && IsFirstRun(caretRun)) || + ((caretPosition.GetOffsetToPosition(caretRun.ContentEnd) == 0) && ParagraphSearcher.Ishighlighted(caretRun))) + { + currentRun = ParagraphSearcher.GetNextRun(caretRun, forward); + } + else + { + currentRun = caretRun; + } + + currentRun = ParagraphSearcher.GetNextMatch(currentRun, forward); + + if (currentRun == null) + { + // if we could not find a next highlight wrap arround + currentRun = ParagraphSearcher.GetFirstOrLastRun(caretRun, forward); + currentRun = ParagraphSearcher.GetNextMatch(currentRun, forward); + } + + this.currentHighlightedMatch = currentRun; + if (this.currentHighlightedMatch != null) + { + // restore the curent highligthed background to current highlighted + this.currentHighlightedMatch.Background = ParagraphSearcher.CurrentHighlightBrush; + } + + return currentRun; + } + + /// + /// Resets the search for fresh calls to MoveAndHighlightNextNextMatch + /// + internal void ResetSearch() + { + this.currentHighlightedMatch = null; + } + + /// + /// Returns true if is highlighted + /// + /// run to check if is highlighted + /// true if is highlighted + private static bool Ishighlighted(Run run) + { + if (run == null) + { + return false; + } + + SolidColorBrush background = run.Background as SolidColorBrush; + if (background != null && background == ParagraphSearcher.HighlightBrush) + { + return true; + } + + return false; + } + + /// + /// Get the next or previous run according to + /// + /// the current run + /// true for next false for previous + /// the next or previous run according to + private static Run GetNextRun(Run currentRun, bool forward) + { + Bold parentBold = currentRun.Parent as Bold; + + Inline nextInline; + + if (forward) + { + nextInline = parentBold != null ? ((Inline)parentBold).NextInline : currentRun.NextInline; + } + else + { + nextInline = parentBold != null ? ((Inline)parentBold).PreviousInline : currentRun.PreviousInline; + } + + return GetRun(nextInline); + } + + /// + /// Gets the run of an inline. Inlines in a ParagrahBuilder are either a Run or a Bold + /// which contains a Run + /// + /// inline to get the run from + /// the run of the inline + private static Run GetRun(Inline inline) + { + Bold inlineBold = inline as Bold; + if (inlineBold != null) + { + return (Run)inlineBold.Inlines.FirstInline; + } + + return (Run)inline; + } + + /// + /// Gets the next highlighted run starting and including + /// according to the direction specified in + /// + /// the current run + /// true for next false for previous + /// + /// the next highlighted run starting and including + /// according to the direction specified in + /// + private static Run GetNextMatch(Run currentRun, bool forward) + { + while (currentRun != null) + { + if (ParagraphSearcher.Ishighlighted(currentRun)) + { + return currentRun; + } + + currentRun = ParagraphSearcher.GetNextRun(currentRun, forward); + } + + return currentRun; + } + + /// + /// Gets the run's paragraph + /// + /// run to get the paragraph from + /// the run's paragraph + private static Paragraph GetParagraph(Run run) + { + Bold parentBold = run.Parent as Bold; + Paragraph parentParagraph = (parentBold != null ? parentBold.Parent : run.Parent) as Paragraph; + Debug.Assert(parentParagraph != null, "the documents we are saerching are built with ParagraphBuilder, which builds the document like this"); + return parentParagraph; + } + + /// + /// Returns true if the run is the fiorst run of the paragraph + /// + /// run to check + /// true if the run is the fiorst run of the paragraph + private static bool IsFirstRun(Run run) + { + Paragraph paragraph = GetParagraph(run); + Run firstRun = ParagraphSearcher.GetRun(paragraph.Inlines.FirstInline); + return run == firstRun; + } + + /// + /// Gets the first or lasr run in the paragraph containing + /// + /// run containing the caret + /// true for first false for last + /// the first or last run in the paragraph containing + private static Run GetFirstOrLastRun(Run caretRun, bool forward) + { + Debug.Assert(caretRun != null, "a caret run is allways valid"); + + Paragraph paragraph = GetParagraph(caretRun); + + Inline firstOrLastInline; + if (forward) + { + firstOrLastInline = paragraph.Inlines.FirstInline; + } + else + { + firstOrLastInline = paragraph.Inlines.LastInline; + } + + return GetRun(firstOrLastInline); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml new file mode 100644 index 00000000000..53d23720c9e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs new file mode 100644 index 00000000000..663076af2e3 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI +{ + using System.Windows; + using Microsoft.Management.UI.Internal; + + /// + /// Dialog with settings for the help dialog + /// + public partial class SettingsDialog : Window + { + /// + /// Initializes a new instance of the SettingsDialog class + /// + public SettingsDialog() + { + InitializeComponent(); + this.Description.IsChecked = HelpWindowSettings.Default.HelpDescriptionDisplayed; + this.Examples.IsChecked = HelpWindowSettings.Default.HelpExamplesDisplayed; + this.Inputs.IsChecked = HelpWindowSettings.Default.HelpInputsDisplayed; + this.Notes.IsChecked = HelpWindowSettings.Default.HelpNotesDisplayed; + this.Outputs.IsChecked = HelpWindowSettings.Default.HelpOutputsDisplayed; + this.Parameters.IsChecked = HelpWindowSettings.Default.HelpParametersDisplayed; + this.RelatedLinks.IsChecked = HelpWindowSettings.Default.HelpRelatedLinksDisplayed; + this.Remarks.IsChecked = HelpWindowSettings.Default.HelpRemarksDisplayed; + this.Synopsys.IsChecked = HelpWindowSettings.Default.HelpSynopsysDisplayed; + this.Syntax.IsChecked = HelpWindowSettings.Default.HelpSyntaxDisplayed; + this.CaseSensitive.IsChecked = HelpWindowSettings.Default.HelpSearchMatchCase; + this.WholeWord.IsChecked = HelpWindowSettings.Default.HelpSearchWholeWord; + } + + /// + /// Called when the OK button has been clicked + /// + /// event sender + /// event arguments + private void OK_Click(object sender, RoutedEventArgs e) + { + HelpWindowSettings.Default.HelpDescriptionDisplayed = this.Description.IsChecked == true; + HelpWindowSettings.Default.HelpExamplesDisplayed = this.Examples.IsChecked == true; + HelpWindowSettings.Default.HelpInputsDisplayed = this.Inputs.IsChecked == true; + HelpWindowSettings.Default.HelpOutputsDisplayed = this.Outputs.IsChecked == true; + HelpWindowSettings.Default.HelpNotesDisplayed = this.Notes.IsChecked == true; + HelpWindowSettings.Default.HelpParametersDisplayed = this.Parameters.IsChecked == true; + HelpWindowSettings.Default.HelpRelatedLinksDisplayed = this.RelatedLinks.IsChecked == true; + HelpWindowSettings.Default.HelpRemarksDisplayed = this.Remarks.IsChecked == true; + HelpWindowSettings.Default.HelpSynopsysDisplayed = this.Synopsys.IsChecked == true; + HelpWindowSettings.Default.HelpSyntaxDisplayed = this.Syntax.IsChecked == true; + HelpWindowSettings.Default.HelpSearchMatchCase = this.CaseSensitive.IsChecked == true; + HelpWindowSettings.Default.HelpSearchWholeWord = this.WholeWord.IsChecked == true; + HelpWindowSettings.Default.Save(); + this.DialogResult = true; + this.Close(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationButton.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationButton.cs new file mode 100644 index 00000000000..dbfa42ed53c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationButton.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a control that is always visible in the automation tree. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + [Description("Provides a System.Windows.Controls.Button control that is always visible in the automation tree.")] + public class AutomationButton : Button + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public AutomationButton() + { + // This constructor intentionally left blank + } + + #endregion + + #region Overides + + /// + /// Returns the implementations for this control. + /// + /// The implementations for this control. + protected override AutomationPeer OnCreateAutomationPeer() + { + return new AutomationButtonAutomationPeer(this); + } + + #endregion + } + + /// + /// Provides an automation peer for AutomationButton. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + internal class AutomationButtonAutomationPeer : ButtonAutomationPeer + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public AutomationButtonAutomationPeer(Button owner) + : base(owner) + { + // This constructor intentionally left blank + } + + #endregion + + #region Overrides + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns false. + protected override bool IsControlElementCore() + { + return this.Owner.Visibility != Visibility.Hidden; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationImage.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationImage.cs new file mode 100644 index 00000000000..d542ee0654f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationImage.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a control that is always visible in the automation tree. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + [Description("Provides a System.Windows.Controls.Image control that is always visible in the automation tree.")] + public class AutomationImage : Image + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public AutomationImage() + { + // This constructor intentionally left blank + } + + #endregion + + #region Overides + + /// + /// Returns the implementations for this control. + /// + /// The implementations for this control. + protected override AutomationPeer OnCreateAutomationPeer() + { + return new AutomationImageAutomationPeer(this); + } + + #endregion + } + + /// + /// Provides an automation peer for AutomationImage. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + internal class AutomationImageAutomationPeer : ImageAutomationPeer + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public AutomationImageAutomationPeer(Image owner) + : base(owner) + { + // This constructor intentionally left blank + } + + #endregion + + #region Overrides + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns false. + protected override bool IsControlElementCore() + { + return false; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlock.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlock.cs new file mode 100644 index 00000000000..44fbd38dff4 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlock.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a control that is always visible in the automation tree. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + [Description("Provides a System.Windows.Controls.TextBlock control that is always visible in the automation tree.")] + public class AutomationTextBlock : TextBlock + { + #region Structors + + /// + /// Initializes a new instance of the class. + /// + public AutomationTextBlock() + { + // This constructor intentionally left blank + } + + #endregion + + #region Overides + + /// + /// Returns the implementations for this control. + /// + /// The implementations for this control. + protected override AutomationPeer OnCreateAutomationPeer() + { + return new AutomationTextBlockAutomationPeer(this); + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlockAutomationPeer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlockAutomationPeer.cs new file mode 100644 index 00000000000..79950b95b4e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlockAutomationPeer.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides an automation peer for AutomationTextBlock. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + internal class AutomationTextBlockAutomationPeer : TextBlockAutomationPeer + { + #region Structors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public AutomationTextBlockAutomationPeer(TextBlock owner) + : base(owner) + { + // This constructor intentionally left blank + } + + #endregion + + #region Overrides + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns true. + protected override bool IsControlElementCore() + { + return true; + } + + /// + /// Gets the class name. + /// + /// The class name. + protected override string GetClassNameCore() + { + return this.Owner.GetType().Name; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/BooleanBoxes.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/BooleanBoxes.cs new file mode 100644 index 00000000000..f75bad7e982 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/BooleanBoxes.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI.Internal +{ + /// + /// A class which returns the same boxed bool values. + /// + internal static class BooleanBoxes + { + private static object trueBox = true; + private static object falseBox = false; + + internal static object TrueBox + { + get + { + return trueBox; + } + } + + internal static object FalseBox + { + get + { + return falseBox; + } + } + + internal static object Box(bool value) + { + if (value) + { + return TrueBox; + } + else + { + return FalseBox; + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/CommandHelper.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CommandHelper.cs new file mode 100644 index 00000000000..7316b3e50e9 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CommandHelper.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Security; +using System.Text; +using System.Windows; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + internal static class CommandHelper + { + internal static void ExecuteCommand(ICommand command, object parameter, IInputElement target) + { + RoutedCommand command2 = command as RoutedCommand; + if (command2 != null) + { + if (command2.CanExecute(parameter, target)) + { + command2.Execute(parameter, target); + } + } + else if (command.CanExecute(parameter)) + { + command.Execute(parameter); + } + } + + internal static bool CanExecuteCommand(ICommand command, object parameter, IInputElement target) + { + if (command == null) + { + return false; + } + + RoutedCommand command2 = command as RoutedCommand; + + if (command2 != null) + { + return command2.CanExecute(parameter, target); + } + else + { + return command.CanExecute(parameter); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs new file mode 100644 index 00000000000..af9b02750b1 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The CustomTypeComparer is responsible for holding custom comparers + /// for different types, which are in turn used to perform comparison + /// operations instead of the default IComparable comparison. + /// with a custom comparer + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public static class CustomTypeComparer + { + private static Dictionary comparers = new Dictionary(); + + /// + /// The static constructor. + /// + static CustomTypeComparer() + { + comparers.Add(typeof(DateTime), new DateTimeApproximationComparer()); + } + + /// + /// Compares two objects and returns a value indicating + /// whether one is less than, equal to, or greater than the other. + /// + /// + /// The first object to compare. + /// + /// + /// The second object to compare. + /// + /// + /// A type implementing IComparable. + /// + /// + /// If value1 is less than value2, then a value less than zero is returned. + /// If value1 equals value2, than zero is returned. + /// If value1 is greater than value2, then a value greater than zero is returned. + /// + public static int Compare(T value1, T value2) where T : IComparable + { + IComparer comparer; + if (TryGetCustomComparer(out comparer) == false) + { + return value1.CompareTo(value2); + } + + return comparer.Compare(value1, value2); + } + + private static bool TryGetCustomComparer(out IComparer comparer) where T : IComparable + { + comparer = null; + + object uncastComparer = null; + if (comparers.TryGetValue(typeof(T), out uncastComparer) == false) + { + return false; + } + + Debug.Assert(uncastComparer is IComparer, "must be IComparer"); + comparer = (IComparer)uncastComparer; + + return true; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DataRoutedEventArgs.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DataRoutedEventArgs.cs new file mode 100644 index 00000000000..aefcb941741 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DataRoutedEventArgs.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Windows; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Routed event args which provide the ability to attach an + /// arbitrary peice of data. + /// + /// There are no restrictions on type T. + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class DataRoutedEventArgs : RoutedEventArgs + { + private T data; + + /// + /// Constructs a new instance of the DataRoutedEventArgs class. + /// + /// The data payload to be stored. + /// The routed event. + public DataRoutedEventArgs(T data, RoutedEvent routedEvent) + { + this.data = data; + this.RoutedEvent = routedEvent; + } + + /// + /// Gets a value containing the data being stored. + /// + public T Data + { + get { return this.data; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs new file mode 100644 index 00000000000..9090f5764af --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The DateTimeApproximationComparer is responsible for comparing two + /// DateTime objects at a level of precision determined by + /// the first object. The comparison either compares at the + /// date level or the date and time (down to Seconds precision). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class DateTimeApproximationComparer : IComparer + { + /// + /// Compares two objects and returns a value indicating + /// whether one is less than, equal to, or greater than the other. + /// + /// + /// The first object to compare. + /// + /// + /// The second object to compare. + /// + /// + /// If value1 is less than value2, then a value less than zero is returned. + /// If value1 equals value2, than zero is returned. + /// If value1 is greater than value2, then a value greater than zero is returned. + /// + public int Compare(DateTime value1, DateTime value2) + { + DateTime roundedX; + DateTime roundedY; + GetRoundedValues(value1, value2, out roundedX, out roundedY); + + return roundedX.CompareTo(roundedY); + } + + private static void GetRoundedValues(DateTime value1, DateTime value2, out DateTime roundedValue1, out DateTime roundedValue2) + { + roundedValue1 = value1; + roundedValue2 = value2; + + bool hasTimeComponent = HasTimeComponent(value1); + + int hour = hasTimeComponent ? value1.Hour : value2.Hour; + int minute = hasTimeComponent ? value1.Minute : value2.Minute; + int second = hasTimeComponent ? value1.Second : value2.Second; + + roundedValue1 = new DateTime(value1.Year, value1.Month, value1.Day, hour, minute, second); + roundedValue2 = new DateTime(value2.Year, value2.Month, value2.Day, value2.Hour, value2.Minute, value2.Second); + } + + private static bool HasTimeComponent(DateTime value) + { + bool hasNoTimeComponent = true + && value.Hour == 0 + && value.Minute == 0 + && value.Second == 0 + && value.Millisecond == 0; + + return !hasNoTimeComponent; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs new file mode 100644 index 00000000000..1eb1d456389 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#region StyleCop Suppression - generated code +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + + /// + /// A popup which child controls can signal to be dimissed. + /// + /// + /// If a control wants to dismiss the popup then they should execute the DismissPopupCommand on a target in the popup window. + /// + [Localizability(LocalizationCategory.None)] + partial class DismissiblePopup + { + // + // DismissPopup routed command + // + /// + /// A command which child controls can use to tell the popup to close. + /// + public static readonly RoutedCommand DismissPopupCommand = new RoutedCommand("DismissPopup",typeof(DismissiblePopup)); + + static private void DismissPopupCommand_CommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) sender; + obj.OnDismissPopupExecuted( e ); + } + + /// + /// Called when DismissPopup executes. + /// + /// + /// A command which child controls can use to tell the popup to close. + /// + protected virtual void OnDismissPopupExecuted(ExecutedRoutedEventArgs e) + { + OnDismissPopupExecutedImplementation(e); + } + + partial void OnDismissPopupExecutedImplementation(ExecutedRoutedEventArgs e); + + // + // CloseOnEscape dependency property + // + /// + /// Identifies the CloseOnEscape dependency property. + /// + public static readonly DependencyProperty CloseOnEscapeProperty = DependencyProperty.Register( "CloseOnEscape", typeof(bool), typeof(DismissiblePopup), new PropertyMetadata( BooleanBoxes.TrueBox, CloseOnEscapeProperty_PropertyChanged) ); + + /// + /// Gets or sets a value indicating whether the popup closes when ESC is pressed. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets a value indicating whether the popup closes when ESC is pressed.")] + [Localizability(LocalizationCategory.None)] + public bool CloseOnEscape + { + get + { + return (bool) GetValue(CloseOnEscapeProperty); + } + set + { + SetValue(CloseOnEscapeProperty,BooleanBoxes.Box(value)); + } + } + + static private void CloseOnEscapeProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnCloseOnEscapeChanged( new PropertyChangedEventArgs((bool)e.OldValue, (bool)e.NewValue) ); + } + + /// + /// Occurs when CloseOnEscape property changes. + /// + public event EventHandler> CloseOnEscapeChanged; + + /// + /// Called when CloseOnEscape property changes. + /// + protected virtual void OnCloseOnEscapeChanged(PropertyChangedEventArgs e) + { + OnCloseOnEscapeChangedImplementation(e); + RaisePropertyChangedEvent(CloseOnEscapeChanged, e); + } + + partial void OnCloseOnEscapeChangedImplementation(PropertyChangedEventArgs e); + + // + // FocusChildOnOpen dependency property + // + /// + /// Identifies the FocusChildOnOpen dependency property. + /// + public static readonly DependencyProperty FocusChildOnOpenProperty = DependencyProperty.Register( "FocusChildOnOpen", typeof(bool), typeof(DismissiblePopup), new PropertyMetadata( BooleanBoxes.TrueBox, FocusChildOnOpenProperty_PropertyChanged) ); + + /// + /// Gets or sets a value indicating whether focus should be set on the child when the popup opens. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets a value indicating whether focus should be set on the child when the popup opens.")] + [Localizability(LocalizationCategory.None)] + public bool FocusChildOnOpen + { + get + { + return (bool) GetValue(FocusChildOnOpenProperty); + } + set + { + SetValue(FocusChildOnOpenProperty,BooleanBoxes.Box(value)); + } + } + + static private void FocusChildOnOpenProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnFocusChildOnOpenChanged( new PropertyChangedEventArgs((bool)e.OldValue, (bool)e.NewValue) ); + } + + /// + /// Occurs when FocusChildOnOpen property changes. + /// + public event EventHandler> FocusChildOnOpenChanged; + + /// + /// Called when FocusChildOnOpen property changes. + /// + protected virtual void OnFocusChildOnOpenChanged(PropertyChangedEventArgs e) + { + OnFocusChildOnOpenChangedImplementation(e); + RaisePropertyChangedEvent(FocusChildOnOpenChanged, e); + } + + partial void OnFocusChildOnOpenChangedImplementation(PropertyChangedEventArgs e); + + // + // SetFocusOnClose dependency property + // + /// + /// Identifies the SetFocusOnClose dependency property. + /// + public static readonly DependencyProperty SetFocusOnCloseProperty = DependencyProperty.Register( "SetFocusOnClose", typeof(bool), typeof(DismissiblePopup), new PropertyMetadata( BooleanBoxes.FalseBox, SetFocusOnCloseProperty_PropertyChanged) ); + + /// + /// Indicates whether the focus returns to either a defined by the FocusOnCloseTarget dependency property UIElement or PlacementTarget or not. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Indicates whether the focus returns to either a defined by the FocusOnCloseTarget dependency property UIElement or PlacementTarget or not.")] + [Localizability(LocalizationCategory.None)] + public bool SetFocusOnClose + { + get + { + return (bool) GetValue(SetFocusOnCloseProperty); + } + set + { + SetValue(SetFocusOnCloseProperty,BooleanBoxes.Box(value)); + } + } + + static private void SetFocusOnCloseProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnSetFocusOnCloseChanged( new PropertyChangedEventArgs((bool)e.OldValue, (bool)e.NewValue) ); + } + + /// + /// Occurs when SetFocusOnClose property changes. + /// + public event EventHandler> SetFocusOnCloseChanged; + + /// + /// Called when SetFocusOnClose property changes. + /// + protected virtual void OnSetFocusOnCloseChanged(PropertyChangedEventArgs e) + { + OnSetFocusOnCloseChangedImplementation(e); + RaisePropertyChangedEvent(SetFocusOnCloseChanged, e); + } + + partial void OnSetFocusOnCloseChangedImplementation(PropertyChangedEventArgs e); + + // + // SetFocusOnCloseElement dependency property + // + /// + /// Identifies the SetFocusOnCloseElement dependency property. + /// + public static readonly DependencyProperty SetFocusOnCloseElementProperty = DependencyProperty.Register( "SetFocusOnCloseElement", typeof(UIElement), typeof(DismissiblePopup), new PropertyMetadata( null, SetFocusOnCloseElementProperty_PropertyChanged) ); + + /// + /// If the SetFocusOnClose property is set True and this property is set to a valid UIElement, focus returns to this UIElement after the DismissiblePopup is closed. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("If the SetFocusOnClose property is set True and this property is set to a valid UIElement, focus returns to this UIElement after the DismissiblePopup is closed.")] + [Localizability(LocalizationCategory.None)] + public UIElement SetFocusOnCloseElement + { + get + { + return (UIElement) GetValue(SetFocusOnCloseElementProperty); + } + set + { + SetValue(SetFocusOnCloseElementProperty,value); + } + } + + static private void SetFocusOnCloseElementProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnSetFocusOnCloseElementChanged( new PropertyChangedEventArgs((UIElement)e.OldValue, (UIElement)e.NewValue) ); + } + + /// + /// Occurs when SetFocusOnCloseElement property changes. + /// + public event EventHandler> SetFocusOnCloseElementChanged; + + /// + /// Called when SetFocusOnCloseElement property changes. + /// + protected virtual void OnSetFocusOnCloseElementChanged(PropertyChangedEventArgs e) + { + OnSetFocusOnCloseElementChangedImplementation(e); + RaisePropertyChangedEvent(SetFocusOnCloseElementChanged, e); + } + + partial void OnSetFocusOnCloseElementChangedImplementation(PropertyChangedEventArgs e); + + /// + /// Called when a property changes. + /// + private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) + { + if (eh != null) + { + eh(this,e); + } + } + + // + // Static constructor + // + + /// + /// Called when the type is initialized. + /// + static DismissiblePopup() + { + CommandManager.RegisterClassCommandBinding( typeof(DismissiblePopup), new CommandBinding( DismissiblePopup.DismissPopupCommand, DismissPopupCommand_CommandExecuted )); + StaticConstructorImplementation(); + } + + static partial void StaticConstructorImplementation(); + + } +} +#endregion diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs new file mode 100644 index 00000000000..fa08b212e4a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Partial class implementation for DismissiblePopup control. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public partial class DismissiblePopup : Popup + { + /// + /// Constructs an instance of DismissablePopup. + /// + public DismissiblePopup() : base() + { + // nothing + } + + private delegate void FocusChildDelegate(); + + /// + /// Responds to the condition in which the value of the IsOpen property changes from false to true. + /// + /// The event arguments. + protected override void OnOpened(EventArgs e) + { + base.OnOpened(e); + + if (this.FocusChildOnOpen) + { + this.Dispatcher.BeginInvoke( + System.Windows.Threading.DispatcherPriority.Loaded, + new FocusChildDelegate(this.FocusChild)); + } + + this.SetupAutomationIdBinding(); + } + + /// + /// Responds when the value of the IsOpen property changes from to true to false. + /// + /// The event arguments. + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + + if (this.SetFocusOnClose) + { + // Find a control to set focus on. + if (this.SetFocusOnCloseElement != null) + { + // The focus target is set explicitly. + this.SetFocus(this.SetFocusOnCloseElement); + } + else if (this.PlacementTarget != null) + { + // Use PlacementTarget as a first chance option. + this.SetFocus(this.PlacementTarget); + } + else + { + // Use parent UIObject when neither FocusOnCloseTarget nor PlacementTarget is set. + UIElement parent = this.Parent as UIElement; + if (parent != null) + { + this.SetFocus(parent); + } + } + } + } + + private void SetFocus(UIElement element) + { + if (element.Focusable) + { + element.Focus(); + } + else + { + element.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + } + } + + private void SetupAutomationIdBinding() + { + var popupRoot = this.FindPopupRoot(); + + var binding = new Binding(); + binding.Source = this; + binding.Path = new PropertyPath(AutomationProperties.AutomationIdProperty); + popupRoot.SetBinding(AutomationProperties.AutomationIdProperty, binding); + } + + private FrameworkElement FindPopupRoot() + { + DependencyObject element = this.Child; + + while (element.GetType().Name.Equals("PopupRoot", StringComparison.Ordinal) == false) + { + element = VisualTreeHelper.GetParent(element); + } + + Debug.Assert(element != null, "element not null"); + + return (FrameworkElement)element; + } + + /// + /// Provides class handling for the KeyDown routed event that occurs when the user presses a key while this control has focus. + /// + /// The event data. + protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e) + { + //// + // Close the popup if ESC is pressed + //// + if (e.Key == System.Windows.Input.Key.Escape && this.CloseOnEscape) + { + this.IsOpen = false; + } + else + { + base.OnKeyDown(e); + } + } + + partial void OnDismissPopupExecutedImplementation(ExecutedRoutedEventArgs e) + { + this.IsOpen = false; + } + + private void FocusChild() + { + if (this.Child != null) + { + this.Child.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs new file mode 100644 index 00000000000..1670e7bbc6e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a base automation peer for FrameworkElement controls. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class ExtendedFrameworkElementAutomationPeer : FrameworkElementAutomationPeer + { + #region Fields + + /// + /// Gets or sets the control type of the element that is associated with this automation peer. + /// + private AutomationControlType controlType = AutomationControlType.Custom; + + /// + /// Gets or sets a value that indicates whether the control should show in the logical tree. + /// + private bool isControlElement = true; + + #endregion + + #region Structors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public ExtendedFrameworkElementAutomationPeer(FrameworkElement owner) + : base(owner) + { + // This constructor intentionally left blank + } + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + /// The control type of the element that is associated with the automation peer. + public ExtendedFrameworkElementAutomationPeer(FrameworkElement owner, AutomationControlType controlType) + : this(owner) + { + this.controlType = controlType; + } + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + /// The control type of the element that is associated with the automation peer. + /// Whether the element should show in the logical tree. + public ExtendedFrameworkElementAutomationPeer(FrameworkElement owner, AutomationControlType controlType, bool isControlElement) + : this(owner, controlType) + { + this.isControlElement = isControlElement; + } + + #endregion + + #region Overrides + + /// + /// Gets the class name. + /// + /// The class name. + protected override string GetClassNameCore() + { + return this.Owner.GetType().Name; + } + + /// + /// Gets the control type of the element that is associated with the automation peer. + /// + /// Returns the control type of the element that is associated with the automation peer. + protected override AutomationControlType GetAutomationControlTypeCore() + { + return this.controlType; + } + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns true. + protected override bool IsControlElementCore() + { + return this.isControlElement; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IAsyncProgress.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IAsyncProgress.cs new file mode 100644 index 00000000000..4798e8e5c90 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IAsyncProgress.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Windows; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// An interface designed to provide updates about an asynchronous operation. + /// If the UI is data bound to the properties in this interface then INotifyPropertyChanged should + /// be implemented by the type implementing IAsyncProgress so the UI can get notification of the properties + /// being changed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public interface IAsyncProgress + { + /// + /// Gets a value indicating whether the async operation is currently running. + /// + bool OperationInProgress + { + get; + } + + /// + /// Gets a the error for the async operation. This field is only valid if + /// OperationInProgress is false. null indicates there was no error. + /// + Exception OperationError + { + get; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IStateDescriptorFactory.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IStateDescriptorFactory.cs new file mode 100644 index 00000000000..cecccc6a7ee --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IStateDescriptorFactory.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Defines an interface for a factory that creates + /// StateDescriptors. + /// + /// The type T used by the StateDescriptor. + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public interface IStateDescriptorFactory + { + /// + /// Creates a new StateDescriptor based upon custom + /// logic. + /// + /// A new StateDescriptor. + StateDescriptor Create(); + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs new file mode 100644 index 00000000000..caffb63b645 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; +using System.Windows; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Takes a value and returns the largest value which is a integral amount of the second value. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IntegralConverter : IMultiValueConverter + { + /// + /// Takes a value and returns the largest value which is a integral amount of the second value. + /// + /// + /// The first value is the source. The second is the factor. + /// + /// The parameter is not used. + /// The padding to subtract from the first value. + /// The parameter is not used. + /// + /// The integral value. + /// + public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (values == null) + { + throw new ArgumentNullException("values"); + } + + if (values.Length != 2) + { + throw new ArgumentException("Two values expected", "values"); + } + + if (values[0] == DependencyProperty.UnsetValue || + values[1] == DependencyProperty.UnsetValue) + { + return DependencyProperty.UnsetValue; + } + + var source = (double)values[0]; + var factor = (double)values[1]; + + double padding = 0; + + if (parameter != null) + { + padding = double.Parse((string)parameter, CultureInfo.InvariantCulture); + } + + var newSource = source - padding; + + if (newSource < factor) + { + return source; + } + + var remainder = newSource % factor; + var result = newSource - remainder; + + return result; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs new file mode 100644 index 00000000000..c8f5f8e29e7 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Takes a bool value and returns the inverse. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class InverseBooleanConverter : IValueConverter + { + /// + /// Converts a boolean value to be it's inverse. + /// + /// The source value. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The inverted boolean value. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + var boolValue = (bool)value; + + return !boolValue; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsEqualConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsEqualConverter.cs new file mode 100644 index 00000000000..d340839f5f9 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsEqualConverter.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Windows; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Takes two objects and determines whether they are equal. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsEqualConverter : IMultiValueConverter + { + /// + /// Takes two items and determines whether they are equal. + /// + /// + /// Two objects of any type. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// + /// True if-and-only-if the two objects are equal per Object.Equals(). + /// Null is equal only to null. + /// + public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (values == null) + { + throw new ArgumentNullException("values"); + } + + if (values.Length != 2) + { + throw new ArgumentException("Two values expected", "values"); + } + + object item1 = values[0]; + object item2 = values[1]; + + if (item1 == null) + { + return item2 == null; + } + + if (item2 == null) + { + return false; + } + + bool equal = item1.Equals(item2); + return equal; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs new file mode 100644 index 00000000000..21371fee76f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IsNotNullConverter is responsible for converting a value into + /// a boolean indicting whether the value is not null. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsNotNullConverter : IValueConverter + { + #region IValueConverter Members + + /// + /// Determines if value is not null. + /// + /// The object to check. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// Returns true if value is not null, false otherwise. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return value != null; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs new file mode 100644 index 00000000000..3de99caeb4f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Windows; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + internal enum LogicalDirection + { + None, + Left, + Right + } + + internal static class KeyboardHelp + { + /// + /// Gets the logical direction for a key, taking into account RTL settings. + /// + /// The element to get FlowDirection from. + /// The key pressed. + /// The logical direction. + public static LogicalDirection GetLogicalDirection(DependencyObject element, Key key) + { + Debug.Assert(element != null, "element not null"); + + bool rightToLeft = IsElementRightToLeft(element); + + switch (key) + { + case Key.Right: + if (rightToLeft) + { + return LogicalDirection.Left; + } + else + { + return LogicalDirection.Right; + } + + case Key.Left: + if (rightToLeft) + { + return LogicalDirection.Right; + } + else + { + return LogicalDirection.Left; + } + + default: + return LogicalDirection.None; + } + } + + /// + /// Gets the focus direction for a key, taking into account RTL settings. + /// + /// The element to get FlowDirection from. + /// The key pressed. + /// The focus direction. + public static FocusNavigationDirection GetNavigationDirection(DependencyObject element, Key key) + { + Debug.Assert(element != null, "element not null"); + Debug.Assert(IsFlowDirectionKey(key)); + + bool rightToLeft = IsElementRightToLeft(element); + + switch (key) + { + case Key.Right: + if (rightToLeft) + { + return FocusNavigationDirection.Left; + } + else + { + return FocusNavigationDirection.Right; + } + + case Key.Left: + if (rightToLeft) + { + return FocusNavigationDirection.Right; + } + else + { + return FocusNavigationDirection.Left; + } + + case Key.Down: + return FocusNavigationDirection.Down; + case Key.Up: + return FocusNavigationDirection.Up; + default: + Debug.Fail("Non-direction key specified"); + return FocusNavigationDirection.First; + } + } + + /// + /// Determines if the control key is pressed. + /// + /// True if a control is is pressed. + public static bool IsControlPressed() + { + if (ModifierKeys.Control == (Keyboard.Modifiers & ModifierKeys.Control)) + { + return true; + } + else + { + return false; + } + } + + /// + /// Determines if the key is a navigation key. + /// + /// The key pressed. + /// True if the key is a navigation key. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private static bool IsFlowDirectionKey(Key key) + { + switch (key) + { + case Key.Right: + case Key.Left: + case Key.Down: + case Key.Up: + return true; + default: + return false; + } + } + + private static bool IsElementRightToLeft(DependencyObject element) + { + FlowDirection flowDirection = FrameworkElement.GetFlowDirection(element); + bool rightToLeft = flowDirection == FlowDirection.RightToLeft; + return rightToLeft; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs new file mode 100644 index 00000000000..8b31f700a12 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs @@ -0,0 +1,487 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// +// This code was generated by a tool. DO NOT EDIT +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// + +#region StyleCop Suppression - generated code +using System; +using System.Collections; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + + /// + /// This control presents a dropdown listbox with associated organizing actions that can be performed on it. + /// + /// + /// + /// + /// If a custom template is provided for this control, then the template MUST provide the following template parts: + /// + /// PART_Picker - A required template part which must be of type PickerBase. This control provides basic functionality for Picker-like controls. + /// + /// + [TemplatePart(Name="PART_Picker", Type=typeof(PickerBase))] + [Localizability(LocalizationCategory.None)] + partial class ListOrganizer + { + // + // Fields + // + private PickerBase picker; + + // + // ItemDeleted RoutedEvent + // + /// + /// Identifies the ItemDeleted RoutedEvent. + /// + public static readonly RoutedEvent ItemDeletedEvent = EventManager.RegisterRoutedEvent("ItemDeleted",RoutingStrategy.Bubble,typeof(EventHandler>),typeof(ListOrganizer)); + + /// + /// Occurs when an item is deleted from the list. + /// + public event EventHandler> ItemDeleted + { + add + { + AddHandler(ItemDeletedEvent,value); + } + remove + { + RemoveHandler(ItemDeletedEvent,value); + } + } + + // + // ItemSelected RoutedEvent + // + /// + /// Identifies the ItemSelected RoutedEvent. + /// + public static readonly RoutedEvent ItemSelectedEvent = EventManager.RegisterRoutedEvent("ItemSelected",RoutingStrategy.Bubble,typeof(EventHandler>),typeof(ListOrganizer)); + + /// + /// Occurs when an item is selected in the list. + /// + public event EventHandler> ItemSelected + { + add + { + AddHandler(ItemSelectedEvent,value); + } + remove + { + RemoveHandler(ItemSelectedEvent,value); + } + } + + // + // DeleteItem routed command + // + /// + /// Informs the ListOrganizer that it should delete the item passed. + /// + public static readonly RoutedCommand DeleteItemCommand = new RoutedCommand("DeleteItem",typeof(ListOrganizer)); + + static private void DeleteItemCommand_CommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) sender; + obj.OnDeleteItemExecuted( e ); + } + + /// + /// Called when DeleteItem executes. + /// + /// + /// Informs the ListOrganizer that it should delete the item passed. + /// + protected virtual void OnDeleteItemExecuted(ExecutedRoutedEventArgs e) + { + OnDeleteItemExecutedImplementation(e); + } + + partial void OnDeleteItemExecutedImplementation(ExecutedRoutedEventArgs e); + + // + // SelectItem routed command + // + /// + /// Informs the ListOrganizer that it should select the item passed. + /// + public static readonly RoutedCommand SelectItemCommand = new RoutedCommand("SelectItem",typeof(ListOrganizer)); + + static private void SelectItemCommand_CommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) sender; + obj.OnSelectItemExecuted( e ); + } + + /// + /// Called when SelectItem executes. + /// + /// + /// Informs the ListOrganizer that it should select the item passed. + /// + protected virtual void OnSelectItemExecuted(ExecutedRoutedEventArgs e) + { + OnSelectItemExecutedImplementation(e); + } + + partial void OnSelectItemExecutedImplementation(ExecutedRoutedEventArgs e); + + // + // DropDownButtonTemplate dependency property + // + /// + /// Identifies the DropDownButtonTemplate dependency property. + /// + public static readonly DependencyProperty DropDownButtonTemplateProperty = DependencyProperty.Register( "DropDownButtonTemplate", typeof(ControlTemplate), typeof(ListOrganizer), new PropertyMetadata( null, DropDownButtonTemplateProperty_PropertyChanged) ); + + /// + /// Gets or sets a value that controls the visual tree of the DropDown button. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets a value that controls the visual tree of the DropDown button.")] + [Localizability(LocalizationCategory.None)] + public ControlTemplate DropDownButtonTemplate + { + get + { + return (ControlTemplate) GetValue(DropDownButtonTemplateProperty); + } + set + { + SetValue(DropDownButtonTemplateProperty,value); + } + } + + static private void DropDownButtonTemplateProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) o; + obj.OnDropDownButtonTemplateChanged( new PropertyChangedEventArgs((ControlTemplate)e.OldValue, (ControlTemplate)e.NewValue) ); + } + + /// + /// Occurs when DropDownButtonTemplate property changes. + /// + public event EventHandler> DropDownButtonTemplateChanged; + + /// + /// Called when DropDownButtonTemplate property changes. + /// + protected virtual void OnDropDownButtonTemplateChanged(PropertyChangedEventArgs e) + { + OnDropDownButtonTemplateChangedImplementation(e); + RaisePropertyChangedEvent(DropDownButtonTemplateChanged, e); + } + + partial void OnDropDownButtonTemplateChangedImplementation(PropertyChangedEventArgs e); + + // + // DropDownStyle dependency property + // + /// + /// Identifies the DropDownStyle dependency property. + /// + public static readonly DependencyProperty DropDownStyleProperty = DependencyProperty.Register( "DropDownStyle", typeof(Style), typeof(ListOrganizer), new PropertyMetadata( null, DropDownStyleProperty_PropertyChanged) ); + + /// + /// Gets or sets the style of the drop-down. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets the style of the drop-down.")] + [Localizability(LocalizationCategory.None)] + public Style DropDownStyle + { + get + { + return (Style) GetValue(DropDownStyleProperty); + } + set + { + SetValue(DropDownStyleProperty,value); + } + } + + static private void DropDownStyleProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) o; + obj.OnDropDownStyleChanged( new PropertyChangedEventArgs + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml.cs new file mode 100644 index 00000000000..2283d9dce75 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for AllModulesControl.xaml + /// + public partial class AllModulesControl : UserControl + { + #region Construction and Destructor + + /// + /// Initializes a new instance of the AllModulesControl class + /// + public AllModulesControl() + { + InitializeComponent(); + + this.Loaded += (obj, args) => + { + this.ModulesCombo.Focus(); + }; + } + + #endregion + /// + /// Gets current control of the ShowModuleControl + /// + internal ShowModuleControl CurrentShowModuleControl + { + get { return this.ShowModuleControl; } + } + + private void RefreshButton_Click(object sender, System.Windows.RoutedEventArgs e) + { + AllModulesViewModel viewModel = this.DataContext as AllModulesViewModel; + if (viewModel == null) + { + return; + } + + viewModel.OnRefresh(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml new file mode 100644 index 00000000000..77a60961484 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml.cs new file mode 100644 index 00000000000..9c1a9fc5c4f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for CmdletControl.xaml + /// + public partial class CmdletControl : UserControl + { + /// + /// Field used for the CurrentCommandViewModel parameter. + /// + private CommandViewModel currentCommandViewModel; + + #region Construction and Destructor + /// + /// Initializes a new instance of the CmdletControl class + /// + public CmdletControl() + { + InitializeComponent(); + this.NotImportedControl.ImportModuleButton.Click += new RoutedEventHandler(ImportModuleButton_Click); + this.ParameterSetTabControl.DataContextChanged += new DependencyPropertyChangedEventHandler(this.ParameterSetTabControl_DataContextChanged); + this.KeyDown += new System.Windows.Input.KeyEventHandler(this.CmdletControl_KeyDown); + this.helpButton.innerButton.Click += new RoutedEventHandler(this.HelpButton_Click); + } + #endregion + + #region Properties + /// + /// Gets the owner of the ViewModel. + /// + private CommandViewModel CurrentCommandViewModel + { + get { return this.currentCommandViewModel; } + } + #endregion + + #region Private Events + + /// + /// DataContextChanged event. + /// + /// Event sender + /// Event args + private void ParameterSetTabControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (this.DataContext == null) + { + return; + } + + CommandViewModel viewModel = (CommandViewModel)this.DataContext; + this.currentCommandViewModel = viewModel; + + if (viewModel.ParameterSets.Count == 0) + { + return; + } + + this.ParameterSetTabControl.SelectedItem = viewModel.ParameterSets[0]; + } + + /// + /// Key down event for user press F1 button. + /// + /// Event sender + /// Event args + private void CmdletControl_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) + { + if (e.Key == System.Windows.Input.Key.F1) + { + this.CurrentCommandViewModel.OpenHelpWindow(); + } + } + + /// + /// Help button event. + /// + /// Event sender + /// Event args + private void HelpButton_Click(object sender, RoutedEventArgs e) + { + this.CurrentCommandViewModel.OpenHelpWindow(); + } + + /// + /// Import Module Button event + /// + /// Event sender + /// Event args + private void ImportModuleButton_Click(object sender, RoutedEventArgs e) + { + this.CurrentCommandViewModel.OnImportModule(); + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml new file mode 100644 index 00000000000..18cc35d0fec --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs new file mode 100644 index 00000000000..1d0fb21d7b6 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Button with images to represent enabled and disabled states + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Required by XAML")] + public partial class ImageButton : ImageButtonBase + { + /// + /// Initializes a new instance of the ImageButton class. + /// + public ImageButton() + { + InitializeComponent(); + this.Loaded += new System.Windows.RoutedEventHandler(this.ImageButton_Loaded); + } + + /// + /// Copies the automation id and name from the parent control to the inner button + /// + /// event sender + /// event arguments + private void ImageButton_Loaded(object sender, System.Windows.RoutedEventArgs e) + { + object thisAutomationId = this.GetValue(AutomationProperties.AutomationIdProperty); + if (thisAutomationId != null) + { + this.innerButton.SetValue(AutomationProperties.AutomationIdProperty, thisAutomationId); + } + + object thisAutomationName = this.GetValue(AutomationProperties.NameProperty); + if (thisAutomationName != null) + { + this.innerButton.SetValue(AutomationProperties.NameProperty, thisAutomationName); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonBase.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonBase.cs new file mode 100644 index 00000000000..f4eadf03537 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonBase.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Implements the ImageButtonBase base class to the ImageButton and ImageToggleButton. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Required by XAML")] + public class ImageButtonBase : Grid + { + /// + /// Command associated with this button + /// + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register("Command", typeof(RoutedUICommand), typeof(ImageButton)); + + /// + /// Image to be used for the enabled state + /// + public static readonly DependencyProperty EnabledImageSourceProperty = + DependencyProperty.Register("EnabledImageSource", typeof(ImageSource), typeof(ImageButton)); + + /// + /// Image to be used for the disabled state + /// + public static readonly DependencyProperty DisabledImageSourceProperty = + DependencyProperty.Register("DisabledImageSource", typeof(ImageSource), typeof(ImageButton)); + + /// + /// Gets or sets the image to be used for the enabled state + /// + public ImageSource EnabledImageSource + { + get { return (ImageSource)GetValue(ImageButton.EnabledImageSourceProperty); } + set { SetValue(ImageButton.EnabledImageSourceProperty, value); } + } + + /// + /// Gets or sets the image to be used for the disabled state + /// + public ImageSource DisabledImageSource + { + get { return (ImageSource)GetValue(ImageButton.DisabledImageSourceProperty); } + set { SetValue(ImageButton.DisabledImageSourceProperty, value); } + } + + /// + /// Gets or sets the command associated with this button + /// + public RoutedUICommand Command + { + get { return (RoutedUICommand)GetValue(ImageButton.CommandProperty); } + set { SetValue(ImageButton.CommandProperty, value); } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonCommon.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonCommon.xaml new file mode 100644 index 00000000000..f8ce60e8418 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonCommon.xaml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonToolTipConverter.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonToolTipConverter.cs new file mode 100644 index 00000000000..ef082c72881 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonToolTipConverter.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Controls; +using System.Windows.Data; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Converts a an ImageButtonBase to its corresponding ToolTip + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Needed for XAML")] + public class ImageButtonToolTipConverter : IValueConverter + { + // This class is meant to be used like this in XAML: + // + // ... + // + // + // + // ... + // + #region IValueConverter Members + + /// + /// Converts a an ImageButtonBase to its corresponding ToolTip by checking if it has a tooltip property + /// or a command with tooltip text + /// + /// The ImageButtonBase we are trying to Convert. + /// is not used. + /// is not used. + /// is not used. + /// The resulting object obtained from retrieving the property value in (or property values if contains dots) out of . + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + ImageButtonBase imageButtonBase = value as ImageButtonBase; + if (imageButtonBase == null) + { + return null; + } + + object toolTipObj = imageButtonBase.GetValue(Button.ToolTipProperty); + if (toolTipObj != null) + { + return toolTipObj.ToString(); + } + + if (imageButtonBase.Command != null && !string.IsNullOrEmpty(imageButtonBase.Command.Text)) + { + return imageButtonBase.Command.Text.Replace("_", string.Empty); + } + + return null; + } + + /// + /// This method is not supported. + /// + /// is not used. + /// is not used. + /// is not used. + /// is not used. + /// No value is returned. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml new file mode 100644 index 00000000000..347331124f3 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs new file mode 100644 index 00000000000..8fd4201c6ea --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Automation; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Toggle button with images to represent enabled and disabled states + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Required by XAML")] + public partial class ImageToggleButton : ImageButtonBase + { + /// + /// Value indicating the button is checked + /// + public static readonly DependencyProperty IsCheckedProperty = + DependencyProperty.Register("IsChecked", typeof(bool), typeof(ImageToggleButton)); + + /// + /// Initializes a new instance of the ImageToggleButton class. + /// + public ImageToggleButton() + { + InitializeComponent(); + this.Loaded += new System.Windows.RoutedEventHandler(this.ImageButton_Loaded); + } + + /// + /// Gets or sets a value indicating whether the button is checked + /// + public bool IsChecked + { + get { return (bool)GetValue(ImageToggleButton.IsCheckedProperty); } + set { SetValue(ImageToggleButton.IsCheckedProperty, value); } + } + + /// + /// Copies the automation id and name from the parent control to the inner button + /// + /// event sender + /// event arguments + private void ImageButton_Loaded(object sender, System.Windows.RoutedEventArgs e) + { + object thisAutomationId = this.GetValue(AutomationProperties.AutomationIdProperty); + if (thisAutomationId != null) + { + this.toggleInnerButton.SetValue(AutomationProperties.AutomationIdProperty, thisAutomationId); + } + + object thisAutomationName = this.GetValue(AutomationProperties.NameProperty); + if (thisAutomationName != null) + { + this.toggleInnerButton.SetValue(AutomationProperties.NameProperty, thisAutomationName); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml new file mode 100644 index 00000000000..9070f9bc78a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml.cs new file mode 100644 index 00000000000..0aafd9184d7 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Globalization; +using System.Management.Automation; +using System.Text; +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for MultipleSelectionControl.xaml + /// + public partial class MultipleSelectionControl : UserControl + { + /// + /// Initializes a new instance of the MultipleSelectionControl class + /// + public MultipleSelectionControl() + { + InitializeComponent(); + } + + /// + /// Show more items in new dialog + /// + /// event sender + /// event arguments + private void ButtonBrowse_Click(object sender, RoutedEventArgs e) + { + MultipleSelectionDialog multipleSelectionDialog = new MultipleSelectionDialog(); + multipleSelectionDialog.Title = this.multipleValueButton.ToolTip.ToString(); + multipleSelectionDialog.listboxParameter.ItemsSource = comboxParameter.ItemsSource; + multipleSelectionDialog.ShowDialog(); + + if (multipleSelectionDialog.DialogResult != true) + { + return; + } + + StringBuilder newComboText = new StringBuilder(); + + foreach (object selectedItem in multipleSelectionDialog.listboxParameter.SelectedItems) + { + newComboText.AppendFormat(CultureInfo.InvariantCulture, "{0},", selectedItem.ToString()); + } + + if (newComboText.Length > 1) + { + newComboText.Remove(newComboText.Length - 1, 1); + } + + comboxParameter.Text = newComboText.ToString(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml new file mode 100644 index 00000000000..8743d95e74a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs new file mode 100644 index 00000000000..24eab391100 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for NotImportedCmdletControl.xaml + /// + public partial class NotImportedCmdletControl : UserControl + { + #region Construction and Destructor + + /// + /// Initializes a new instance of the NotImportedCmdletControl class + /// + public NotImportedCmdletControl() + { + InitializeComponent(); + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml new file mode 100644 index 00000000000..5487cb59e2f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml.cs new file mode 100644 index 00000000000..e13d1e589ca --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml.cs @@ -0,0 +1,406 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Management.Automation; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls; +using System.Windows.Data; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for ParameterSetControl.xaml + /// + public partial class ParameterSetControl : UserControl + { + /// + /// First focusable element in the generated UI. + /// + private UIElement firstFocusableElement; + + /// + /// Field used for the CurrentParameterSetViewModel parameter. + /// + private ParameterSetViewModel currentParameterSetViewModel; + + #region Construction and Destructor + /// + /// Initializes a new instance of the ParameterSetControl class + /// + public ParameterSetControl() + { + InitializeComponent(); + this.DataContextChanged += new DependencyPropertyChangedEventHandler(this.ParameterSetControl_DataContextChanged); + } + #endregion + + #region Public Methods + + /// + /// Focuses the first focusable element in this control. + /// + public void FocusFirstElement() + { + if (this.firstFocusableElement != null) + { + this.firstFocusableElement.Focus(); + } + } + + #endregion + + #region Private Property + /// + /// Gets current ParameterSetViewModel. + /// + private ParameterSetViewModel CurrentParameterSetViewModel + { + get { return this.currentParameterSetViewModel; } + } + + #endregion + + /// + /// Creates a CheckBox for switch parameters + /// + /// DataContext object + /// Row number + /// a CheckBox for switch parameters + private static CheckBox CreateCheckBox(ParameterViewModel parameterViewModel, int rowNumber) + { + CheckBox checkBox = new CheckBox(); + + checkBox.SetBinding(Label.ContentProperty, new Binding("NameCheckLabel")); + checkBox.DataContext = parameterViewModel; + checkBox.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; + checkBox.SetValue(Grid.ColumnProperty, 0); + checkBox.SetValue(Grid.ColumnSpanProperty, 2); + checkBox.SetValue(Grid.RowProperty, rowNumber); + checkBox.IsThreeState = false; + checkBox.Margin = new Thickness(8, rowNumber == 0 ? 7 : 5, 0, 5); + checkBox.SetBinding(CheckBox.ToolTipProperty, new Binding("ToolTip")); + checkBox.SetBinding(AutomationProperties.HelpTextProperty, new Binding("ToolTip")); + Binding valueBinding = new Binding("Value"); + checkBox.SetBinding(CheckBox.IsCheckedProperty, valueBinding); + + //// Add AutomationProperties.AutomationId for Ui Automation test. + checkBox.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + string.Format(CultureInfo.CurrentCulture, "chk{0}", parameterViewModel.Name)); + + checkBox.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + return checkBox; + } + + /// + /// Creates a ComboBox control for input type field + /// + /// DataContext object + /// Row number + /// Control data source + /// Return a ComboBox control + private static ComboBox CreateComboBoxControl(ParameterViewModel parameterViewModel, int rowNumber, IEnumerable itemsSource) + { + ComboBox comboBox = new ComboBox(); + + comboBox.DataContext = parameterViewModel; + comboBox.SetValue(Grid.ColumnProperty, 1); + comboBox.SetValue(Grid.RowProperty, rowNumber); + comboBox.Margin = new Thickness(2); + comboBox.SetBinding(TextBox.ToolTipProperty, new Binding("ToolTip")); + comboBox.ItemsSource = itemsSource; + + Binding selectedItemBinding = new Binding("Value"); + comboBox.SetBinding(ComboBox.SelectedItemProperty, selectedItemBinding); + + string automationId = string.Format( + CultureInfo.CurrentCulture, + "combox{0}", + parameterViewModel.Name); + + //// Add AutomationProperties.AutomationId for Ui Automation test. + comboBox.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + automationId); + + comboBox.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + return comboBox; + } + + /// + /// Creates a MultiSelectCombo control for input type field + /// + /// DataContext object + /// Row number + /// Control data source + /// Return a MultiSelectCombo control + private static MultipleSelectionControl CreateMultiSelectComboControl(ParameterViewModel parameterViewModel, int rowNumber, IEnumerable itemsSource) + { + MultipleSelectionControl multiControls = new MultipleSelectionControl(); + + multiControls.DataContext = parameterViewModel; + multiControls.SetValue(Grid.ColumnProperty, 1); + multiControls.SetValue(Grid.RowProperty, rowNumber); + multiControls.Margin = new Thickness(2); + multiControls.comboxParameter.ItemsSource = itemsSource; + multiControls.SetBinding(TextBox.ToolTipProperty, new Binding("ToolTip")); + + Binding valueBinding = new Binding("Value"); + valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; + multiControls.comboxParameter.SetBinding(ComboBox.TextProperty, valueBinding); + + // Add AutomationProperties.AutomationId for Ui Automation test. + multiControls.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, string.Format("combox{0}", parameterViewModel.Name)); + + multiControls.comboxParameter.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + string buttonToolTipAndName = string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.SelectMultipleValuesForParameterFormat, + parameterViewModel.Name); + + multiControls.multipleValueButton.SetValue(Button.ToolTipProperty, buttonToolTipAndName); + multiControls.multipleValueButton.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + buttonToolTipAndName); + + return multiControls; + } + + /// + /// Creates a TextBox control for input type field + /// + /// DataContext object + /// Row number + /// Return a TextBox control + private static TextBox CreateTextBoxControl(ParameterViewModel parameterViewModel, int rowNumber) + { + TextBox textBox = new TextBox(); + + textBox.DataContext = parameterViewModel; + textBox.SetValue(Grid.ColumnProperty, 1); + textBox.SetValue(Grid.RowProperty, rowNumber); + textBox.Margin = new Thickness(2); + textBox.SetBinding(TextBox.ToolTipProperty, new Binding("ToolTip")); + + Binding valueBinding = new Binding("Value"); + valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; + textBox.SetBinding(TextBox.TextProperty, valueBinding); + + //// Add AutomationProperties.AutomationId for UI Automation test. + textBox.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + string.Format(CultureInfo.CurrentCulture, "txt{0}", parameterViewModel.Name)); + + textBox.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + ShowCommandParameterType parameterType = parameterViewModel.Parameter.ParameterType; + + if (parameterType.IsArray) + { + parameterType = parameterType.ElementType; + } + + if (parameterType.IsScriptBlock || parameterType.ImplementsDictionary) + { + textBox.AcceptsReturn = true; + textBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; + textBox.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto; + textBox.Loaded += new RoutedEventHandler(ParameterSetControl.MultiLineTextBox_Loaded); + } + + return textBox; + } + + /// + /// Called for a newly created multiline text box to increase its height and + /// + /// event sender + /// event arguments + private static void MultiLineTextBox_Loaded(object sender, RoutedEventArgs e) + { + TextBox senderTextBox = (TextBox)sender; + senderTextBox.Loaded -= new RoutedEventHandler(ParameterSetControl.MultiLineTextBox_Loaded); + + // This will set the height to about 3 lines since the total height of the + // TextBox is a bit greater than a line's height + senderTextBox.Height = senderTextBox.ActualHeight * 2; + } + + #region Event Methods + + /// + /// When user switch ParameterSet.It will trigger this event. + /// This event method will renew generate all controls for current ParameterSet. + /// + /// Event sender + /// Event args + private void ParameterSetControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + this.firstFocusableElement = null; + this.MainGrid.Children.Clear(); + this.MainGrid.RowDefinitions.Clear(); + + ParameterSetViewModel viewModel = e.NewValue as ParameterSetViewModel; + if (viewModel == null) + { + return; + } + + this.currentParameterSetViewModel = viewModel; + + for (int rowNumber = 0; rowNumber < viewModel.Parameters.Count; rowNumber++) + { + ParameterViewModel parameter = viewModel.Parameters[rowNumber]; + this.MainGrid.RowDefinitions.Add(this.CreateNewRow()); + + if (parameter.Parameter.ParameterType.IsSwitch) + { + this.AddControlToMainGrid(ParameterSetControl.CreateCheckBox(parameter, rowNumber)); + } + else + { + this.CreateAndAddLabel(parameter, rowNumber); + Control control = null; + if (parameter.Parameter.HasParameterSet) + { + // For ValidateSet parameter + ArrayList itemsSource = new ArrayList(); + itemsSource.Add(string.Empty); + + for (int i = 0; i < parameter.Parameter.ValidParamSetValues.Count; i++) + { + itemsSource.Add(parameter.Parameter.ValidParamSetValues[i]); + } + + control = ParameterSetControl.CreateComboBoxControl(parameter, rowNumber, itemsSource); + } + else if (parameter.Parameter.ParameterType.IsEnum) + { + if (parameter.Parameter.ParameterType.HasFlagAttribute) + { + ArrayList itemsSource = new ArrayList(); + itemsSource.Add(string.Empty); + itemsSource.AddRange(parameter.Parameter.ParameterType.EnumValues); + control = ParameterSetControl.CreateComboBoxControl(parameter, rowNumber, itemsSource); + } + else + { + control = ParameterSetControl.CreateMultiSelectComboControl(parameter, rowNumber, parameter.Parameter.ParameterType.EnumValues); + } + } + else if (parameter.Parameter.ParameterType.IsBoolean) + { + control = ParameterSetControl.CreateComboBoxControl(parameter, rowNumber, new string[] { string.Empty, "$True", "$False" }); + } + else + { + // For input parameter + control = ParameterSetControl.CreateTextBoxControl(parameter, rowNumber); + } + + if (control != null) + { + this.AddControlToMainGrid(control); + } + } + } + } + + /// + /// When user trigger click on anyone CheckBox. Get value from sender. + /// + /// Event sender + /// Event args + private void CheckBox_Click(object sender, RoutedEventArgs e) + { + CheckBox senderCheck = (CheckBox)sender; + ((ParameterViewModel)senderCheck.DataContext).Value = senderCheck.IsChecked.ToString(); + } + + #endregion + + #region Private Method + + /// + /// Creates a RowDefinition for MainGrid + /// + /// Return a RowDefinition object + private RowDefinition CreateNewRow() + { + RowDefinition row = new RowDefinition(); + row.Height = GridLength.Auto; + return row; + } + + /// + /// Adds a control to MainGrid; + /// + /// Will adding UIControl + private void AddControlToMainGrid(UIElement uiControl) + { + if (this.firstFocusableElement == null && !(uiControl is Label)) + { + this.firstFocusableElement = uiControl; + } + + this.MainGrid.Children.Add(uiControl); + } + + /// + /// Creates a Lable control and add it to MainGrid + /// + /// DataContext object + /// Row number + private void CreateAndAddLabel(ParameterViewModel parameterViewModel, int rowNumber) + { + Label label = this.CreateLabel(parameterViewModel, rowNumber); + this.AddControlToMainGrid(label); + } + + /// + /// Creates a Label control for input type field + /// + /// DataContext object + /// Row number + /// Return a Label control + private Label CreateLabel(ParameterViewModel parameterViewModel, int rowNumber) + { + Label label = new Label(); + + label.SetBinding(Label.ContentProperty, new Binding("NameTextLabel")); + label.DataContext = parameterViewModel; + label.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; + label.SetValue(Grid.ColumnProperty, 0); + label.SetValue(Grid.RowProperty, rowNumber); + label.Margin = new Thickness(2); + label.SetBinding(Label.ToolTipProperty, new Binding("ToolTip")); + + //// Add AutomationProperties.AutomationId for Ui Automation test. + label.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + string.Format(CultureInfo.CurrentCulture, "lbl{0}", parameterViewModel.Name)); + + return label; + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml new file mode 100644 index 00000000000..932dbbe885e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml.cs new file mode 100644 index 00000000000..f63faf9d6a7 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Management.Automation; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Control taht shows cmdlets in a module and details for a selected cmdlet + /// + public partial class ShowModuleControl : UserControl + { + /// + /// Field used for the Owner parameter. + /// + private Window owner; + + /// + /// Initializes a new instance of the ShowModuleControl class + /// + public ShowModuleControl() + { + InitializeComponent(); + + // See comment in method summary to understand why this event is handled + this.CommandList.PreviewMouseMove += new MouseEventHandler(this.CommandList_PreviewMouseMove); + + // See comment in method summary to understand why this event is handled + this.CommandList.SelectionChanged += new SelectionChangedEventHandler(this.CommandList_SelectionChanged); + } + + /// + /// Gets or sets the owner of the container. + /// + public Window Owner + { + get { return this.owner; } + set { this.owner = value; } + } + + #region Events Handlers + /// + /// WPF has an interesting feature in list selection where if you hold the mouse button down, + /// it will select the item under it, but if you keep the mouse button down and move the mouse + /// (if the list supported drag and drop, the mouse action would be the same as dragging) it + /// will select other list items. + /// If the first selection change causes details for the item to be displayed and resizes the list + /// the selection can skip to another list item it happend to be over as the list got resized. + /// In summary, resizing the list on selection can cause a selection bug. If the user selects an + /// item in the end of the list the next item downwards can be selected. + /// The WPF drag-and-select feature is not a standard win32 list behavior, and we can do without it + /// since it causes this problem. + /// WPF sets up this behavior by using a mouse capture. We undo the behavior in the handler below + /// which removes the behavior. + /// + /// event sender + /// event arguments + private void CommandList_PreviewMouseMove(object sender, MouseEventArgs e) + { + if (this.CommandList.IsMouseCaptured) + { + this.CommandList.ReleaseMouseCapture(); + } + } + + /// + /// Ensures the selected item is scrolled into view and that the list is focused. + /// An item could be out of the view if the selection was changed in the object model + /// + /// event sender + /// event arguments + private void CommandList_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (this.CommandList.SelectedItem == null) + { + return; + } + + this.CommandList.ScrollIntoView(this.CommandList.SelectedItem); + } + #endregion Events Handlers + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs new file mode 100644 index 00000000000..589fb63b52c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.16808 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Management.UI.Internal.ShowCommand { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class ShowCommandSettings : global::System.Configuration.ApplicationSettingsBase { + + private static ShowCommandSettings defaultInstance = ((ShowCommandSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ShowCommandSettings()))); + + public static ShowCommandSettings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandTop { + get { + return ((double)(this["ShowOneCommandTop"])); + } + set { + this["ShowOneCommandTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandLeft { + get { + return ((double)(this["ShowOneCommandLeft"])); + } + set { + this["ShowOneCommandLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandWidth { + get { + return ((double)(this["ShowOneCommandWidth"])); + } + set { + this["ShowOneCommandWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandHeight { + get { + return ((double)(this["ShowOneCommandHeight"])); + } + set { + this["ShowOneCommandHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsTop { + get { + return ((double)(this["ShowCommandsTop"])); + } + set { + this["ShowCommandsTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsLeft { + get { + return ((double)(this["ShowCommandsLeft"])); + } + set { + this["ShowCommandsLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsWidth { + get { + return ((double)(this["ShowCommandsWidth"])); + } + set { + this["ShowCommandsWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsHeight { + get { + return ((double)(this["ShowCommandsHeight"])); + } + set { + this["ShowCommandsHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ShowCommandsWindowMaximized { + get { + return ((bool)(this["ShowCommandsWindowMaximized"])); + } + set { + this["ShowCommandsWindowMaximized"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ShowOneCommandWindowMaximized { + get { + return ((bool)(this["ShowOneCommandWindowMaximized"])); + } + set { + this["ShowOneCommandWindowMaximized"] = value; + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.settings b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.settings new file mode 100644 index 00000000000..6f6c457d258 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.settings @@ -0,0 +1,36 @@ + + + + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + False + + + False + + + \ No newline at end of file diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs new file mode 100644 index 00000000000..dc6c29fb39d --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs @@ -0,0 +1,659 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Windows; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains all Commands, Parameters, ParameterSet and Common Parameter. + /// + public class AllModulesViewModel : INotifyPropertyChanged + { + #region Private Fields + /// + /// Flag indicating a wait message is being displayed + /// + private bool waitMessageDisplayed; + + /// + /// True if this ViewModel is not supposed to show common parameters + /// + private bool noCommonParameter; + + /// + /// the filterName of command + /// + private string commandNameFilter; + + /// + /// Field used for the Modules property. + /// + private List modules; + + /// + /// true if a command can be run + /// + private bool canRun; + + /// + /// true if a command can be copied + /// + private bool canCopy; + + /// + /// the selected module being displayed in the GUI + /// + private ModuleViewModel selectedModule; + + /// + /// the visibility of the refresh button + /// + private Visibility refreshVisibility = Visibility.Collapsed; + + /// + /// Provides an extra viewModel object that allows callers to control certain aspects of the GUI + /// + private object extraViewModel; + + /// + /// private property for ZoomLevel + /// + private double zoomLevel = 1.0; + #endregion + + #region Construction and Destructor + /// + /// Initializes a new instance of the AllModulesViewModel class + /// + /// the loaded modules + /// commands to show + public AllModulesViewModel(Dictionary importedModules, IEnumerable commands) + { + if (commands == null || !commands.GetEnumerator().MoveNext()) + { + throw new ArgumentNullException("commands"); + } + + this.Initialization(importedModules, commands, true); + } + + /// + /// Initializes a new instance of the AllModulesViewModel class + /// + /// the loaded modules + /// All PowerShell commands + /// true not to show common parameters + public AllModulesViewModel(Dictionary importedModules, IEnumerable commands, bool noCommonParameter) + { + if (commands == null) + { + throw new ArgumentNullException("commands"); + } + + this.Initialization(importedModules, commands, noCommonParameter); + } + + #endregion + + #region INotifyPropertyChanged Members + /// + /// PropertyChanged Event + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Indicates the selected command in the selected module needs to display the help for a command + /// + public event EventHandler SelectedCommandInSelectedModuleNeedsHelp; + + /// + /// Indicates the selected command in the selected module needs to import a module for a command + /// + public event EventHandler SelectedCommandInSelectedModuleNeedsImportModule; + + /// + /// Indicates the selected command in the selected module should be run + /// + public event EventHandler RunSelectedCommandInSelectedModule; + + /// + /// Indicates we want to refresh the viewModel + /// + public event EventHandler Refresh; + + #region Public Properties + + /// + /// Get or Sets Zoom level + /// + public double ZoomLevel + { + get + { + return this.zoomLevel; + } + + set + { + if (value > 0) + { + this.zoomLevel = value / 100.0; + this.OnNotifyPropertyChanged("ZoomLevel"); + } + } + } + + /// + /// Gets the tooltip for the refresh button + /// + public static string RefreshTooltip + { + get { return string.Format(CultureInfo.CurrentUICulture, ShowCommandResources.RefreshShowCommandTooltipFormat, "import-module"); } + } + + /// + /// Gets or sets the visibility of the refresh button + /// + public Visibility RefreshVisibility + { + get + { + return this.refreshVisibility; + } + + set + { + if (this.refreshVisibility == value) + { + return; + } + + this.refreshVisibility = value; + this.OnNotifyPropertyChanged("RefreshVisibility"); + } + } + + /// + /// Gets a value indicating whether common parameters are displayed + /// + public bool NoCommonParameter + { + get { return this.noCommonParameter; } + } + + /// + /// Gets or sets the filterName of command + /// + public string CommandNameFilter + { + get + { + return this.commandNameFilter; + } + + set + { + if (this.CommandNameFilter == value) + { + return; + } + + this.commandNameFilter = value; + if (this.selectedModule != null) + { + this.selectedModule.RefreshFilteredCommands(this.CommandNameFilter); + this.selectedModule.SelectedCommand = null; + } + + this.OnNotifyPropertyChanged("CommandNameFilter"); + } + } + + /// + /// Gets or sets the selected module being displayed in the GUI + /// + public ModuleViewModel SelectedModule + { + get + { + return this.selectedModule; + } + + set + { + if (this.selectedModule == value) + { + return; + } + + if (this.selectedModule != null) + { + this.selectedModule.SelectedCommandNeedsImportModule -= new EventHandler(this.SelectedModule_SelectedCommandNeedsImportModule); + this.selectedModule.SelectedCommandNeedsHelp -= new EventHandler(this.SelectedModule_SelectedCommandNeedsHelp); + this.selectedModule.RunSelectedCommand -= new EventHandler(this.SelectedModule_RunSelectedCommand); + this.selectedModule.PropertyChanged -= new PropertyChangedEventHandler(this.SelectedModule_PropertyChanged); + } + + this.selectedModule = value; + this.SetCanRun(); + this.SetCanCopy(); + + if (this.selectedModule != null) + { + this.selectedModule.RefreshFilteredCommands(this.CommandNameFilter); + this.selectedModule.SelectedCommandNeedsImportModule += new EventHandler(this.SelectedModule_SelectedCommandNeedsImportModule); + this.selectedModule.SelectedCommandNeedsHelp += new EventHandler(this.SelectedModule_SelectedCommandNeedsHelp); + this.selectedModule.RunSelectedCommand += new EventHandler(this.SelectedModule_RunSelectedCommand); + this.selectedModule.PropertyChanged += new PropertyChangedEventHandler(this.SelectedModule_PropertyChanged); + this.selectedModule.SelectedCommand = null; + } + + this.OnNotifyPropertyChanged("SelectedModule"); + } + } + + /// + /// Gets a value indicating whether we can run a command + /// + public bool CanRun + { + get + { + return this.canRun; + } + } + + /// + /// Gets a value indicating whether we can copy a command + /// + public bool CanCopy + { + get + { + return this.canCopy; + } + } + + /// + /// Gets the Modules parameter. + /// + public List Modules + { + get { return this.modules; } + } + + /// + /// Gets the visibility of the wait message + /// + public Visibility WaitMessageVisibility + { + get + { + return this.waitMessageDisplayed ? Visibility.Visible : Visibility.Hidden; + } + } + + /// + /// Gets the visibility of the main grid + /// + public Visibility MainGridVisibility + { + get + { + return this.waitMessageDisplayed ? Visibility.Hidden : Visibility.Visible; + } + } + + /// + /// Gets a value indicating whether the main grid is displayed + /// + public bool MainGridDisplayed + { + get + { + return !this.waitMessageDisplayed; + } + } + + /// + /// Gets or sets a value indicating whether the wait message is displayed + /// + public bool WaitMessageDisplayed + { + get + { + return this.waitMessageDisplayed; + } + + set + { + if (this.waitMessageDisplayed == value) + { + return; + } + + this.waitMessageDisplayed = value; + this.SetCanCopy(); + this.SetCanRun(); + this.OnNotifyPropertyChanged("WaitMessageDisplayed"); + this.OnNotifyPropertyChanged("WaitMessageVisibility"); + this.OnNotifyPropertyChanged("MainGridDisplayed"); + this.OnNotifyPropertyChanged("MainGridVisibility"); + } + } + + /// + /// Gets or sets an extra viewModel object that allows callers to control certain aspects of the GUI + /// + public object ExtraViewModel + { + get + { + return this.extraViewModel; + } + + set + { + if (this.extraViewModel == value) + { + return; + } + + this.extraViewModel = value; + this.OnNotifyPropertyChanged("ExtraViewModel"); + } + } + #endregion + + /// + /// Returns the selected script + /// + /// the selected script + public string GetScript() + { + if (this.SelectedModule == null) + { + return null; + } + + if (this.SelectedModule.SelectedCommand == null) + { + return null; + } + + return this.SelectedModule.SelectedCommand.GetScript(); + } + + /// + /// Triggers Refresh + /// + internal void OnRefresh() + { + EventHandler handler = this.Refresh; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + #region Private Methods + /// + /// If current modules name is ALL, then return true. + /// + /// The modules name + /// Return true is the module name is ALLModulesViewModel. + private static bool IsAll(string name) + { + return name.Equals(ShowCommandResources.All, StringComparison.Ordinal); + } + + /// + /// Monitors property changes in the selected module to call: + /// SetCanRun for IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues + /// SetCanCopy for SetCanCopy + /// + /// event sender + /// event arguments + private void SelectedModule_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues") + { + this.SetCanRun(); + } + else if (e.PropertyName == "IsThereASelectedCommand") + { + this.SetCanCopy(); + } + } + + /// + /// Called to set this.CanRun when: + /// The SelectedModule changes, since there will be no selected command in the new module, and CanRun should be false + /// WaitMessageDisplayedMessage changes since this being true will cause this.MainGridDisplayed to be false and CanRun should be false + /// IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues changes in the selected module + /// + private void SetCanRun() + { + bool newValue = this.selectedModule != null && this.MainGridDisplayed && + this.selectedModule.IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues; + + if (this.canRun == newValue) + { + return; + } + + this.canRun = newValue; + this.OnNotifyPropertyChanged("CanRun"); + } + + /// + /// Called to set this.CanCopy when: + /// The SelectedModule changes, since there will be no selected command in the new module, and CanCopy should be false + /// WaitMessageDisplayedMessage changes since this being true will cause this.MainGridDisplayed to be false and CanCopy should be false + /// IsThereASelectedCommand changes in the selected module + /// + private void SetCanCopy() + { + bool newValue = this.selectedModule != null && this.MainGridDisplayed && this.selectedModule.IsThereASelectedCommand; + + if (this.canCopy == newValue) + { + return; + } + + this.canCopy = newValue; + this.OnNotifyPropertyChanged("CanCopy"); + } + + /// + /// Initialize AllModulesViewModel. + /// + /// All loaded modules + /// List of commands in all modules + /// Whether showing common parameter + private void Initialization(Dictionary importedModules, IEnumerable commands, bool noCommonParameterInModel) + { + if (commands == null) + { + return; + } + + Dictionary rawModuleViewModels = new Dictionary(); + + this.noCommonParameter = noCommonParameterInModel; + + // separates commands in their Modules + foreach (ShowCommandCommandInfo command in commands) + { + ModuleViewModel moduleViewModel; + if (!rawModuleViewModels.TryGetValue(command.ModuleName, out moduleViewModel)) + { + moduleViewModel = new ModuleViewModel(command.ModuleName, importedModules); + rawModuleViewModels.Add(command.ModuleName, moduleViewModel); + } + + CommandViewModel commandViewModel; + + try + { + commandViewModel = CommandViewModel.GetCommandViewModel(moduleViewModel, command, noCommonParameterInModel); + } + catch (RuntimeException) + { + continue; + } + + moduleViewModel.Commands.Add(commandViewModel); + moduleViewModel.SetAllModules(this); + } + + // populates this.modules + this.modules = new List(); + + // if there is just one module then use only it + if (rawModuleViewModels.Values.Count == 1) + { + this.modules.Add(rawModuleViewModels.Values.First()); + this.modules[0].SortCommands(false); + this.SelectedModule = this.modules[0]; + return; + } + + // If there are more modules, create an additional module to agregate all commands + ModuleViewModel allCommandsModule = new ModuleViewModel(ShowCommandResources.All, null); + this.modules.Add(allCommandsModule); + allCommandsModule.SetAllModules(this); + + if (rawModuleViewModels.Values.Count > 0) + { + foreach (ModuleViewModel module in rawModuleViewModels.Values) + { + module.SortCommands(false); + this.modules.Add(module); + + allCommandsModule.Commands.AddRange(module.Commands); + } + } + + allCommandsModule.SortCommands(true); + + this.modules.Sort(this.Compare); + this.SelectedModule = this.modules.Count == 0 ? null : this.modules[0]; + } + + /// + /// Compare two ModuleViewModel target and source. + /// + /// The source ModuleViewModel + /// The target ModuleViewModel + /// Compare result + private int Compare(ModuleViewModel source, ModuleViewModel target) + { + if (AllModulesViewModel.IsAll(source.Name) && !AllModulesViewModel.IsAll(target.Name)) + { + return -1; + } + + if (!AllModulesViewModel.IsAll(source.Name) && AllModulesViewModel.IsAll(target.Name)) + { + return 1; + } + + return string.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Called when the SelectedCommandNeedsHelp event is triggered in the Selected Module + /// + /// event sender + /// event arguments + private void SelectedModule_SelectedCommandNeedsHelp(object sender, HelpNeededEventArgs e) + { + this.OnSelectedCommandInSelectedModuleNeedsHelp(e); + } + + /// + /// Called when the SelectedCommandNeedsImportModule event is triggered in the Selected Module + /// + /// event sender + /// event arguments + private void SelectedModule_SelectedCommandNeedsImportModule(object sender, ImportModuleEventArgs e) + { + this.OnSelectedCommandInSelectedModuleNeedsImportModule(e); + } + + /// + /// Triggers SelectedCommandInSelectedModuleNeedsHelp + /// + /// event arguments + private void OnSelectedCommandInSelectedModuleNeedsHelp(HelpNeededEventArgs e) + { + EventHandler handler = this.SelectedCommandInSelectedModuleNeedsHelp; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Triggers SelectedCommandInSelectedModuleNeedsImportModule + /// + /// event arguments + private void OnSelectedCommandInSelectedModuleNeedsImportModule(ImportModuleEventArgs e) + { + EventHandler handler = this.SelectedCommandInSelectedModuleNeedsImportModule; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Called when the RunSelectedCommand is triggered in the selected module + /// + /// event sender + /// event arguments + private void SelectedModule_RunSelectedCommand(object sender, CommandEventArgs e) + { + this.OnRunSelectedCommandInSelectedModule(e); + } + + /// + /// Triggers RunSelectedCommandInSelectedModule + /// + /// event arguments + private void OnRunSelectedCommandInSelectedModule(CommandEventArgs e) + { + EventHandler handler = this.RunSelectedCommandInSelectedModule; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// If property changed will be notify + /// + /// The changed property + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandEventArgs.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandEventArgs.cs new file mode 100644 index 00000000000..5fe69bea162 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandEventArgs.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Arguments for the event triggered when something happens at the cmdlet level + /// + public class CommandEventArgs : EventArgs + { + /// + /// the command targeted by the event + /// + private CommandViewModel command; + + /// + /// Initializes a new instance of the CommandEventArgs class. + /// + /// the command targeted by the event + public CommandEventArgs(CommandViewModel command) + { + this.command = command; + } + + /// + /// Gets the command targeted by the event + /// + public CommandViewModel Command + { + get { return this.command; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs new file mode 100644 index 00000000000..e86bb257e75 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs @@ -0,0 +1,648 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Text; +using System.Windows; + +using Microsoft.Management.UI; +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +using SMAI = System.Management.Automation.Internal; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains information about a cmdlet's Shard ParameterSet, + /// ParameterSets, Parameters, Common Parameters and error message. + /// + public class CommandViewModel : INotifyPropertyChanged + { + #region Private Fields + /// + /// The name of the AllParameterSets + /// + private const string SharedParameterSetName = "__AllParameterSets"; + + /// + /// Grid length constant + /// + private static readonly GridLength star = new GridLength(1, GridUnitType.Star); + + /// + /// The module containing this cmdlet in the gui + /// + private ModuleViewModel parentModule; + + /// + /// The name of the default ParameterSet + /// + private string defaultParameterSetName; + + /// + /// Field used for the AreCommonParametersExpanded parameter. + /// + private bool areCommonParametersExpanded; + + /// + /// Field used for the SelectedParameterSet parameter. + /// + private ParameterSetViewModel selectedParameterSet; + + /// + /// Field used for the ParameterSets parameter. + /// + private List parameterSets = new List(); + + /// + /// Field used for the ParameterSetTabControlVisibility parameter. + /// + private bool noCommonParameters; + + /// + /// Field used for the CommonParameters parameter. + /// + private ParameterSetViewModel comonParameters; + + /// + /// The ShowCommandCommandInfo this model is based on + /// + private ShowCommandCommandInfo commandInfo; + + /// + /// value indicating whether the selected parameter set has all mandatory parameters valid + /// + private bool selectedParameterSetAllMandatoryParametersHaveValues; + + /// + /// value indicating whether the command name should be qualified by the module in GetScript + /// + private bool moduleQualifyCommandName; + + /// + /// The height for common parameters that will depend on CommonParameterVisibility + /// + private GridLength commonParametersHeight; + #endregion + + /// + /// Prevents a default instance of the CommandViewModel class from being created + /// + private CommandViewModel() + { + } + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + /// + /// Indicates the command needs to display the help for a command + /// + public event EventHandler HelpNeeded; + + /// + /// Indicates a module needs to be imported + /// + public event EventHandler ImportModule; + + #region Public Properties + /// + /// Gets or sets a value indicating whether the command name should be qualified by the module in GetScript + /// + public bool ModuleQualifyCommandName + { + get { return this.moduleQualifyCommandName; } + set { this.moduleQualifyCommandName = value; } + } + + /// + /// Gets or sets a value indicating whether the common parameters are expanded + /// + public bool AreCommonParametersExpanded + { + get + { + return this.areCommonParametersExpanded; + } + + set + { + if (this.areCommonParametersExpanded == value) + { + return; + } + + this.areCommonParametersExpanded = value; + this.OnNotifyPropertyChanged("AreCommonParametersExpanded"); + this.SetCommonParametersHeight(); + } + } + + /// + /// Gets or sets the SelectedParameterSet parameter. + /// + public ParameterSetViewModel SelectedParameterSet + { + get + { + return this.selectedParameterSet; + } + + set + { + if (this.selectedParameterSet != value) + { + if (this.selectedParameterSet != null) + { + this.selectedParameterSet.PropertyChanged -= new PropertyChangedEventHandler(this.SelectedParameterSet_PropertyChanged); + } + + this.selectedParameterSet = value; + if (this.selectedParameterSet != null) + { + this.selectedParameterSet.PropertyChanged += new PropertyChangedEventHandler(this.SelectedParameterSet_PropertyChanged); + this.SelectedParameterSetAllMandatoryParametersHaveValues = this.SelectedParameterSet.AllMandatoryParametersHaveValues; + } + else + { + this.SelectedParameterSetAllMandatoryParametersHaveValues = true; + } + + this.OnNotifyPropertyChanged("SelectedParameterSet"); + } + } + } + + /// + /// Gets or sets a value indicating whether the selected parameter set has all mandatory parameters valid. + /// If there is no selected parameter set this value is true + /// + public bool SelectedParameterSetAllMandatoryParametersHaveValues + { + get + { + return this.selectedParameterSetAllMandatoryParametersHaveValues; + } + + set + { + if (this.selectedParameterSetAllMandatoryParametersHaveValues == value) + { + return; + } + + this.selectedParameterSetAllMandatoryParametersHaveValues = value; + this.OnNotifyPropertyChanged("SelectedParameterSetAllMandatoryParametersHaveValues"); + } + } + + /// + /// Gets the ParameterSets parameter. + /// + public List ParameterSets + { + get { return this.parameterSets; } + } + + /// + /// Gets the visibility for the tab control displaying several ParameterSetControl. This is displayed when there are more than 1 parameter sets. + /// + public Visibility ParameterSetTabControlVisibility + { + get { return (this.ParameterSets.Count > 1) && this.IsImported ? Visibility.Visible : Visibility.Collapsed; } + } + + /// + /// Gets the visibility for the single ParameterSetControl displayed when there is only 1 parameter set + /// + public Visibility SingleParameterSetControlVisibility + { + get { return (this.ParameterSets.Count == 1) ? Visibility.Visible : Visibility.Collapsed; } + } + + /// + /// Gets the CommonParameters parameter. + /// + public ParameterSetViewModel CommonParameters + { + get { return this.comonParameters; } + } + + /// + /// Gets the CommonParameterVisibility parameter. + /// + public Visibility CommonParameterVisibility + { + get { return this.noCommonParameters || (this.CommonParameters.Parameters.Count == 0) ? Visibility.Collapsed : Visibility.Visible; } + } + + /// + /// Gets or sets the height for common parameters that will depend on CommonParameterVisibility + /// + public GridLength CommonParametersHeight + { + get + { + return this.commonParametersHeight; + } + + set + { + if (this.commonParametersHeight == value) + { + return; + } + + this.commonParametersHeight = value; + this.OnNotifyPropertyChanged("CommonParametersHeight"); + } + } + + /// + /// Gets the visibility for the control displayed when the module is not imported + /// + public Visibility NotImportedVisibility + { + get + { + return this.IsImported ? Visibility.Collapsed : Visibility.Visible; + } + } + + /// + /// Gets the visibility for the control displayed when there are no parameters + /// + public Visibility NoParameterVisibility + { + get + { + bool hasNoParameters = this.ParameterSets.Count == 0 || (this.ParameterSets.Count == 1 && this.ParameterSets[0].Parameters.Count == 0); + return this.IsImported && hasNoParameters ? Visibility.Visible : Visibility.Collapsed; + } + } + + /// + /// Gets a value indicating whether the cmdlet comes from a module which is imported + /// + public bool IsImported + { + get + { + return this.commandInfo.Module == null || this.ParentModule.IsModuleImported; + } + } + + /// + /// Gets the Name parameter. + /// + public string Name + { + get + { + if (this.commandInfo != null) + { + return this.commandInfo.Name; + } + + return string.Empty; + } + } + + /// + /// Gets the module path if it is not null or empty, or the name otherwise + /// + public string ModuleName + { + get + { + if (this.commandInfo != null && this.commandInfo.ModuleName != null) + { + return this.commandInfo.ModuleName; + } + + return string.Empty; + } + } + + /// + /// Gets the module containing this cmdlet in the GUI. + /// + public ModuleViewModel ParentModule + { + get + { + return this.parentModule; + } + } + + /// + /// Gets Tooltip string for the cmdlet + /// + public string ToolTip + { + get + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.CmdletTooltipFormat, + this.Name, + this.ParentModule.DisplayName, + this.IsImported ? ShowCommandResources.Imported : ShowCommandResources.NotImported); + } + } + + /// + /// Gets the message to be displayed when the cmdlet belongs to a module that is not imported + /// + public string ImportModuleMessage + { + get + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.NotImportedFormat, + this.ModuleName, + this.Name, + ShowCommandResources.ImportModuleButtonText); + } + } + + /// + /// Gets the title for the cmdlet details + /// + public string DetailsTitle + { + get + { + if (this.IsImported) + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.DetailsParameterTitleFormat, + this.Name); + } + else + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.NameLabelFormat, + this.Name); + } + } + } + #endregion + + /// + /// Gets a Grid length constant + /// + internal static GridLength Star + { + get { return CommandViewModel.star; } + } + + /// + /// Gets the builded PowerShell script. + /// + /// Return script as string + public string GetScript() + { + StringBuilder builder = new StringBuilder(); + + string commandName = this.commandInfo.CommandType == CommandTypes.ExternalScript ? this.commandInfo.Definition : this.Name; + + if (this.ModuleQualifyCommandName && !string.IsNullOrEmpty(this.ModuleName)) + { + commandName = this.ModuleName + "\\" + commandName; + } + + if (commandName.IndexOf(' ') != -1) + { + builder.AppendFormat("& \"{0}\"", commandName); + } + else + { + builder.Append(commandName); + } + + builder.Append(" "); + + if (this.SelectedParameterSet != null) + { + builder.Append(this.SelectedParameterSet.GetScript()); + builder.Append(" "); + } + + if (this.CommonParameters != null) + { + builder.Append(this.CommonParameters.GetScript()); + } + + string script = builder.ToString(); + + return script.Trim(); + } + + /// + /// Showing help information for current actived cmdlet. + /// + public void OpenHelpWindow() + { + this.OnHelpNeeded(); + } + + /// + /// Determins whether current command name and a specifed ParameterSetName have same name. + /// + /// The name of ShareParameterSet + /// Return true is ShareParameterSet. Else return false. + internal static bool IsSharedParameterSetName(string name) + { + return name.Equals(CommandViewModel.SharedParameterSetName, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Creates a new CommandViewModel out the . + /// + /// Module to which the CommandViewModel will belong to + /// Will showing command + /// true to ommit displaying common parameter + /// If commandInfo is null + /// + /// If could not create the CommandViewModel. For instance the ShowCommandCommandInfo corresponding to + /// the following function will throw a RuntimeException when the ShowCommandCommandInfo Parameters + /// are retrieved: + /// function CrashMe ([I.Am.A.Type.That.Does.Not.Exist]$name) {} + /// + /// The CommandViewModel corresponding to commandInfo + internal static CommandViewModel GetCommandViewModel(ModuleViewModel module, ShowCommandCommandInfo commandInfo, bool noCommonParameters) + { + if (commandInfo == null) + { + throw new ArgumentNullException("commandInfo"); + } + + CommandViewModel returnValue = new CommandViewModel(); + returnValue.commandInfo = commandInfo; + returnValue.noCommonParameters = noCommonParameters; + returnValue.parentModule = module; + + Dictionary commonParametersTable = new Dictionary(); + + foreach (ShowCommandParameterSetInfo parameterSetInfo in commandInfo.ParameterSets) + { + if (parameterSetInfo.IsDefault) + { + returnValue.defaultParameterSetName = parameterSetInfo.Name; + } + + List parametersForParameterSet = new List(); + foreach (ShowCommandParameterInfo parameterInfo in parameterSetInfo.Parameters) + { + bool isCommon = Cmdlet.CommonParameters.Contains(parameterInfo.Name); + + if (isCommon) + { + if (!commonParametersTable.ContainsKey(parameterInfo.Name)) + { + commonParametersTable.Add(parameterInfo.Name, new ParameterViewModel(parameterInfo, parameterSetInfo.Name)); + } + + continue; + } + + parametersForParameterSet.Add(new ParameterViewModel(parameterInfo, parameterSetInfo.Name)); + } + + if (parametersForParameterSet.Count != 0) + { + returnValue.ParameterSets.Add(new ParameterSetViewModel(parameterSetInfo.Name, parametersForParameterSet)); + } + } + + List commonParametersList = commonParametersTable.Values.ToList(); + returnValue.comonParameters = new ParameterSetViewModel(string.Empty, commonParametersList); + + returnValue.parameterSets.Sort(returnValue.Compare); + + if (returnValue.parameterSets.Count > 0) + { + // Setting SelectedParameterSet will also set SelectedParameterSetAllMandatoryParametersHaveValues + returnValue.SelectedParameterSet = returnValue.ParameterSets[0]; + } + else + { + returnValue.SelectedParameterSetAllMandatoryParametersHaveValues = true; + } + + returnValue.SetCommonParametersHeight(); + + return returnValue; + } + + /// + /// Called to trigger the event fired when help is needed for the command + /// + internal void OnHelpNeeded() + { + EventHandler handler = this.HelpNeeded; + if (handler != null) + { + handler(this, new HelpNeededEventArgs(this.Name)); + } + } + + /// + /// Called to trigger the event fired when a module needs to be imported + /// + internal void OnImportModule() + { + EventHandler handler = this.ImportModule; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + #region Private Methods + /// + /// Called to set the height for common parameters initially or when the AreCommonParametersExpanded changes + /// + private void SetCommonParametersHeight() + { + this.CommonParametersHeight = this.AreCommonParametersExpanded ? CommandViewModel.Star : GridLength.Auto; + } + + /// + /// Compares source and target by being the default parameter set and then by name + /// + /// source paremeterset + /// target parameterset + /// 0 if they are the same, -1 if source is smaller, 1 if source is larger + private int Compare(ParameterSetViewModel source, ParameterSetViewModel target) + { + if (this.defaultParameterSetName != null) + { + if (source.Name.Equals(this.defaultParameterSetName) && target.Name.Equals(this.defaultParameterSetName)) + { + return 0; + } + + if (source.Name.Equals(this.defaultParameterSetName, StringComparison.Ordinal)) + { + return -1; + } + + if (target.Name.Equals(this.defaultParameterSetName, StringComparison.Ordinal)) + { + return 1; + } + } + + return string.Compare(source.Name, target.Name, StringComparison.Ordinal); + } + + /// + /// If property changed will be notify + /// + /// The changed property + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + /// + /// Called when the PropertyChanged event is triggered on the SelectedParameterSet + /// + /// event sender + /// event arguments + private void SelectedParameterSet_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!e.PropertyName.Equals("AllMandatoryParametersHaveValues")) + { + return; + } + + this.SelectedParameterSetAllMandatoryParametersHaveValues = this.SelectedParameterSet.AllMandatoryParametersHaveValues; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/HelpNeededEventArgs.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/HelpNeededEventArgs.cs new file mode 100644 index 00000000000..d5d2fa66f83 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/HelpNeededEventArgs.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Arguments for the event triggered when it is necessary to display help for a command + /// + public class HelpNeededEventArgs : EventArgs + { + /// + /// the name for the command needing help + /// + private string commandName; + + /// + /// Initializes a new instance of the HelpNeededEventArgs class. + /// + /// the name for the command needing help + public HelpNeededEventArgs(string commandName) + { + this.commandName = commandName; + } + + /// + /// Gets the name for the command needing help + /// + public string CommandName + { + get { return this.commandName; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs new file mode 100644 index 00000000000..82095868c66 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Arguments for the event triggered when it is necessary to display help for a command + /// + public class ImportModuleEventArgs : EventArgs + { + /// + /// the name for the command belonging to the module to be imported + /// + private string commandName; + + /// + /// the module path or name for the module we want to import + /// + private string parentModuleName; + + /// + /// the name of the module that is selected, which can be different from parentModuleName + /// if "All" is selected + /// + private string selectedModuleName; + + /// + /// Initializes a new instance of the ImportModuleEventArgs class. + /// + /// the name for the command needing help + /// the name of the module containing the command + /// + /// the name of the module that is selected, which can be different from parentModuleName + /// if "All" is selected + /// + public ImportModuleEventArgs(string commandName, string parentModuleName, string selectedModuleName) + { + this.commandName = commandName; + this.parentModuleName = parentModuleName; + this.selectedModuleName = selectedModuleName; + } + + /// + /// Gets the name for the command belonging to the module to be imported + /// + public string CommandName + { + get { return this.commandName; } + } + + /// + /// Gets the module path or name for the module we want to import + /// + public string ParentModuleName + { + get { return this.parentModuleName; } + } + + /// + /// Gets the name of the module that is selected, which can be different from parentModuleName + /// if "All" is selected + /// + public string SelectedModuleName + { + get { return this.selectedModuleName; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs new file mode 100644 index 00000000000..f15058a8103 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs @@ -0,0 +1,531 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Management.Automation; +using System.Windows; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// ModuleViewModel Contains information about a PowerShell module. + /// + public class ModuleViewModel : INotifyPropertyChanged + { + /// + /// True if the module is imported + /// + private bool isModuleImported; + + /// + /// Field used for the Name parameter. + /// + private string name; + + /// + /// Filter commands property of this module + /// + private ObservableCollection filteredCommands; + + /// + /// The selected command property of this module + /// + private CommandViewModel selectedCommand; + + /// + /// Field used for the Commands parameter. + /// + private List commands; + + /// + /// value indicating whether there is a selected command which belongs to an imported module, + /// with no parameter sets or with a selected parameter set where all mandatory parameters have values + /// + private bool isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues; + + /// + /// value indicating whether there is a selected command + /// + private bool isThereASelectedCommand; + + /// + /// The AllModulesViewModel containing this, if any + /// + private AllModulesViewModel allModules; + + #region Construction and Destructor + /// + /// Initializes a new instance of the ModuleViewModel class. + /// + /// Module name + /// All loaded modules + public ModuleViewModel(string name, Dictionary importedModules) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + + this.name = name; + this.commands = new List(); + this.filteredCommands = new ObservableCollection(); + + // This check looks to see if the given module name shows up in + // the set of modules that are known to be imported in the current + // session. In remote PowerShell sessions, the core cmdlet module + // Microsoft.PowerShell.Core doesn't appear as being imported despite + // always being loaded by default. To make sure we don't incorrectly + // mark this module as not imported, check for it by name. + this.isModuleImported = + importedModules == null ? true : name.Length == 0 || + importedModules.ContainsKey(name) || + string.Equals("Microsoft.PowerShell.Core", name, StringComparison.OrdinalIgnoreCase); + } + #endregion + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Indicates the selected command in needs to display the help for a command + /// + public event EventHandler SelectedCommandNeedsHelp; + + /// + /// Indicates the selected command needs to import a module + /// + public event EventHandler SelectedCommandNeedsImportModule; + + /// + /// Indicates the selected command should be run + /// + public event EventHandler RunSelectedCommand; + + #region Public Property + /// + /// Gets the name property of this ModuleView + /// + public string Name + { + get { return this.name; } + } + + /// + /// Gets the GUI friendly module name + /// + public string DisplayName + { + get + { + if (!string.IsNullOrEmpty(this.name)) + { + return this.name; + } + + return ShowCommandResources.NoModuleName; + } + } + + /// + /// Gets CommandControl is visibility or not + /// + public Visibility CommandControlVisibility + { + get { return this.selectedCommand == null ? Visibility.Collapsed : Visibility.Visible; } + } + + /// + /// Gets CommandControl Height + /// + public GridLength CommandRowHeight + { + get { return this.selectedCommand == null ? GridLength.Auto : CommandViewModel.Star; } + } + + /// + /// Gets the commands under in this module + /// + public List Commands + { + get { return this.commands; } + } + + /// + /// Gets the filter commands of this module + /// + public ObservableCollection FilteredCommands + { + get { return this.filteredCommands; } + } + + /// + /// Gets or sets the selected commands of this module + /// + public CommandViewModel SelectedCommand + { + get + { + return this.selectedCommand; + } + + set + { + if (value == this.selectedCommand) + { + return; + } + + if (this.selectedCommand != null) + { + this.selectedCommand.PropertyChanged -= new PropertyChangedEventHandler(this.SelectedCommand_PropertyChanged); + this.selectedCommand.HelpNeeded -= new EventHandler(this.SelectedCommand_HelpNeeded); + this.selectedCommand.ImportModule -= new EventHandler(this.SelectedCommand_ImportModule); + } + + this.selectedCommand = value; + + this.SetIsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues(); + + if (this.selectedCommand != null) + { + this.selectedCommand.PropertyChanged += new PropertyChangedEventHandler(this.SelectedCommand_PropertyChanged); + this.selectedCommand.HelpNeeded += new EventHandler(this.SelectedCommand_HelpNeeded); + this.selectedCommand.ImportModule += new EventHandler(this.SelectedCommand_ImportModule); + this.IsThereASelectedCommand = true; + } + else + { + this.IsThereASelectedCommand = false; + } + + this.OnNotifyPropertyChanged("SelectedCommand"); + this.OnNotifyPropertyChanged("CommandControlVisibility"); + this.OnNotifyPropertyChanged("CommandRowHeight"); + } + } + + /// + /// Gets or sets a value indicating whether there is a selected command + /// + public bool IsThereASelectedCommand + { + get + { + return this.isThereASelectedCommand; + } + + set + { + if (value == this.isThereASelectedCommand) + { + return; + } + + this.isThereASelectedCommand = value; + this.OnNotifyPropertyChanged("IsThereASelectedCommand"); + } + } + + /// + /// Gets or sets a value indicating whether there is a selected command which belongs + /// to an imported module, with no parameter sets or with a selected parameter set + /// where all mandatory parameters have values + /// + public bool IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues + { + get + { + return this.isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues; + } + + set + { + if (value == this.isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues) + { + return; + } + + this.isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues = value; + + this.OnNotifyPropertyChanged("IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues"); + } + } + + /// + /// Gets the AllModulesViewModel containing this, if any + /// + public AllModulesViewModel AllModules + { + get + { + return this.allModules; + } + } + #endregion + + /// + /// Gets a value indicating whether the module is imported + /// + internal bool IsModuleImported + { + get + { + return this.isModuleImported; + } + } + + /// + /// Sets the AllModulesViewModel containing this + /// + /// the AllModulesViewModel containing this + internal void SetAllModules(AllModulesViewModel parentAllModules) + { + this.allModules = parentAllModules; + } + + /// + /// Sorts commands and optionally sets ModuleQualifyCommandName + /// + /// true to mark repeated commands with a flag that will produce a module qualified name in GetScript + internal void SortCommands(bool markRepeatedCmdlets) + { + this.commands.Sort(this.Compare); + + if (!markRepeatedCmdlets || this.commands.Count == 0) + { + return; + } + + CommandViewModel reference = this.commands[0]; + for (int i = 1; i < this.commands.Count; i++) + { + CommandViewModel command = this.commands[i]; + if (reference.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase)) + { + reference.ModuleQualifyCommandName = true; + command.ModuleQualifyCommandName = true; + } + else + { + reference = command; + } + } + } + + /// + /// According commandNameFilter to filter command,and added the filter commands into filteredCommands property + /// + /// current filter + internal void RefreshFilteredCommands(string filter) + { + this.filteredCommands.Clear(); + if (string.IsNullOrEmpty(filter)) + { + foreach (CommandViewModel command in this.Commands) + { + this.filteredCommands.Add(command); + } + + return; + } + + WildcardPattern filterPattern = null; + if (WildcardPattern.ContainsWildcardCharacters(filter)) + { + filterPattern = new WildcardPattern(filter, WildcardOptions.IgnoreCase); + } + + foreach (CommandViewModel command in this.Commands) + { + if (ModuleViewModel.Matches(filterPattern, command.Name, filter)) + { + this.filteredCommands.Add(command); + continue; + } + + if (filterPattern != null) + { + continue; + } + + string[] textSplit = filter.Split(' '); + if (textSplit.Length != 2) + { + continue; + } + + if (ModuleViewModel.Matches(filterPattern, command.Name, textSplit[0] + "-" + textSplit[1])) + { + this.filteredCommands.Add(command); + } + } + } + + /// + /// Callled in response to a GUI event that requires the command to be run + /// + internal void OnRunSelectedCommand() + { + EventHandler handler = this.RunSelectedCommand; + if (handler != null) + { + handler(this, new CommandEventArgs(this.SelectedCommand)); + } + } + + /// + /// Triggers the SelectedCommandNeedsHelp event + /// + /// event arguments + internal void OnSelectedCommandNeedsHelp(HelpNeededEventArgs e) + { + EventHandler handler = this.SelectedCommandNeedsHelp; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Triggers the SelectedCommandNeedsImportModule event + /// + internal void OnSelectedCommandNeedsImportModule() + { + EventHandler handler = this.SelectedCommandNeedsImportModule; + if (handler != null) + { + handler(this, new ImportModuleEventArgs(this.SelectedCommand.Name, this.SelectedCommand.ModuleName, this.Name)); + } + } + #region Private Method + + /// + /// Uses pattern matching if pattern is not null or calls MatchesEvenIfInPlural otherwise + /// + /// pattern corresponding to filter + /// command name string + /// filter string + /// true if coparisonText matches str or pattern + private static bool Matches(WildcardPattern filterPattern, string commandName, string filter) + { + if (filterPattern != null) + { + return filterPattern.IsMatch(commandName); + } + + return ModuleViewModel.MatchesEvenIfInPlural(commandName, filter); + } + + /// + /// Returns true if filter matches commandName, even when filter is in the plural + /// + /// command name string + /// filter string + /// return match result + private static bool MatchesEvenIfInPlural(string commandName, string filter) + { + if (commandName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1) + { + return true; + } + + if (filter.Length > 5 && filter.EndsWith("es", StringComparison.OrdinalIgnoreCase)) + { + filter = filter.Substring(0, filter.Length - 2); + return commandName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1; + } + + if (filter.Length > 4 && filter.EndsWith("s", StringComparison.OrdinalIgnoreCase)) + { + filter = filter.Substring(0, filter.Length - 1); + return commandName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1; + } + + return false; + } + + /// + /// Handles the HelpNeeded event in the selected command and triggers the SelectedCommandNeedsHelp event + /// + /// HelpNeeded event sender + /// HelpNeeded event argument + private void SelectedCommand_HelpNeeded(object sender, HelpNeededEventArgs e) + { + this.OnSelectedCommandNeedsHelp(e); + } + + /// + /// Handles the ImportModule event in the selected command and triggers the SelectedCommandNeedsImportModule event + /// + /// HelpNeeded event sender + /// HelpNeeded event argument + private void SelectedCommand_ImportModule(object sender, EventArgs e) + { + this.OnSelectedCommandNeedsImportModule(); + } + + /// + /// Called when the SelectedCommand property changes to update IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues + /// + /// event sender + /// event arguments + private void SelectedCommand_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!e.PropertyName.Equals("SelectedParameterSetAllMandatoryParametersHaveValues")) + { + return; + } + + this.SetIsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues(); + } + + /// + /// Called to set IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues when + /// SelectedParameterSetAllMandatoryParametersHaveValues changes in the SelectedCommand or + /// when the SelectedCommand changes + /// + private void SetIsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues() + { + this.IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues = + this.selectedCommand != null && + this.selectedCommand.IsImported && + this.selectedCommand.SelectedParameterSetAllMandatoryParametersHaveValues; + } + + /// + /// Compare source commandmodule is equal like target commandmodule + /// + /// source commandmodule + /// target commandmodule + /// return compare result + private int Compare(CommandViewModel source, CommandViewModel target) + { + return string.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); + } + #endregion + + /// + /// If property changed will be notify + /// + /// The changed property + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs new file mode 100644 index 00000000000..0ef79083b37 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs @@ -0,0 +1,394 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; +using System.Management.Automation.Language; +using System.Text; + +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains information about a single ParameterSet inside a cmdlet. + /// + public class ParameterSetViewModel : INotifyPropertyChanged + { + /// + /// Field used for the Name parameter. + /// + private string name; + + /// + /// value indicating all mandatory parameters have values + /// + private bool allMandatoryParametersHaveValues; + + /// + /// Field used for the Parameters parameter. + /// + private List parameters; + + #region Construction and Destructor + + /// + /// Initializes a new instance of the ParameterSetViewModel class. + /// + /// The name of the parameterSet + /// The array parametes of the parameterSet + [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "this type is internal, made public only for WPF Binding")] + public ParameterSetViewModel( + string name, + List parameters) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + + if (parameters == null) + { + throw new ArgumentNullException("parameters"); + } + + parameters.Sort(Compare); + + this.name = name; + this.parameters = parameters; + foreach (ParameterViewModel parameter in this.parameters) + { + if (!parameter.IsMandatory) + { + continue; + } + + parameter.PropertyChanged += new PropertyChangedEventHandler(this.MandatoryParameter_PropertyChanged); + } + + this.EvaluateAllMandatoryParametersHaveValues(); + } + #endregion + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Public Property + /// + /// Gets the ParameterSet Name + /// + public string Name + { + get { return this.name; } + } + + /// + /// Gets the Parameters of this parameterset + /// + public List Parameters + { + get { return this.parameters; } + } + + /// + /// Gets or sets a value indicating whether all mandatory parameters have values + /// + public bool AllMandatoryParametersHaveValues + { + get + { + return this.allMandatoryParametersHaveValues; + } + + set + { + if (this.allMandatoryParametersHaveValues != value) + { + this.allMandatoryParametersHaveValues = value; + this.OnNotifyPropertyChanged("AllMandatoryParametersHaveValues"); + } + } + } + #endregion + + #region Public Method + /// + /// Creates script according parameters of this parameterset + /// + /// Return script of this parameterset parameters + public string GetScript() + { + if (this.Parameters == null || this.Parameters.Count == 0) + { + return string.Empty; + } + + StringBuilder builder = new StringBuilder(); + foreach (ParameterViewModel parameter in this.Parameters) + { + if (parameter.Value == null) + { + continue; + } + + if (parameter.Parameter.ParameterType.IsSwitch) + { + if (((bool?)parameter.Value) == true) + { + builder.AppendFormat("-{0} ", parameter.Name); + } + + continue; + } + + string parameterValueString = parameter.Value.ToString(); + + if (parameterValueString.Length == 0) + { + continue; + } + + ShowCommandParameterType parameterType = parameter.Parameter.ParameterType; + + if (parameterType.IsEnum || parameterType.IsString || (parameterType.IsArray && parameterType.ElementType.IsString)) + { + parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "\"", "\""); + } + else if (parameterType.IsScriptBlock) + { + parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "{", "}"); + } + else + { + parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "(", ")"); + } + + builder.AppendFormat("-{0} {1} ", parameter.Name, parameterValueString); + } + + return builder.ToString().Trim(); + } + + /// + /// Gets the individual parameter count of this parameterset + /// + /// Return individual parameter count of this parameterset + public int GetIndividualParameterCount() + { + if (this.Parameters == null || this.Parameters.Count == 0) + { + return 0; + } + + int i = 0; + + foreach (ParameterViewModel p in this.Parameters) + { + if (p.IsInSharedParameterSet) + { + return i; + } + + i++; + } + + return i; + } + + #endregion + + #region Internal Method + + /// + /// Compare source parametermodel is equal like target parametermodel + /// + /// the source of parametermodel + /// the target of parametermodel + /// Return compare result + internal static int Compare(ParameterViewModel source, ParameterViewModel target) + { + if (source.Parameter.IsMandatory && !target.Parameter.IsMandatory) + { + return -1; + } + + if (!source.Parameter.IsMandatory && target.Parameter.IsMandatory) + { + return 1; + } + + return string.Compare(source.Parameter.Name, target.Parameter.Name); + } + + #endregion + + /// + /// Gets the delimited poarameter if it needs delimitation and is not delimited + /// + /// value needing delimitation + /// open delimitation + /// close delimitation + /// the delimited poarameter if it needs delimitation and is not delimited + private static string GetDelimitedParameter(string parameterValue, string openDelimiter, string closeDelimiter) + { + string parameterValueTrimmed = parameterValue.Trim(); + + if (parameterValueTrimmed.Length == 0) + { + return openDelimiter + parameterValue + closeDelimiter; + } + + char delimitationChar = ParameterSetViewModel.ParameterNeedsDelimitation(parameterValueTrimmed, openDelimiter.Length == 1 && openDelimiter[0] == '{'); + switch (delimitationChar) + { + case '1': + return openDelimiter + parameterValue + closeDelimiter; + case '\'': + return '\'' + parameterValue + '\''; + case '\"': + return '\"' + parameterValue + '\"'; + default: + return parameterValueTrimmed; + } + } + + /// + /// Returns '0' if the does not need delimitation, '1' if it does, and a quote character if it needs to be delimited with a quote + /// + /// parameter value to check + /// true if the parameter value should be a scriptblock + /// '0' if the parameter does not need delimitation, '1' if it needs, '\'' if it needs to be delimited with single quote and '\"' if it needs to be delimited with double quotes + private static char ParameterNeedsDelimitation(string parameterValue, bool requireScriptblock) + { + Token[] tokens; + ParseError[] errors; + ScriptBlockAst values = Parser.ParseInput("commandName -parameterName " + parameterValue, out tokens, out errors); + + if (values == null || values.EndBlock == null || values.EndBlock.Statements.Count == 0) + { + return '1'; + } + + PipelineAst pipeline = values.EndBlock.Statements[0] as PipelineAst; + if (pipeline == null || pipeline.PipelineElements.Count == 0) + { + return '1'; + } + + CommandAst commandAst = pipeline.PipelineElements[0] as CommandAst; + + if (commandAst == null || commandAst.CommandElements.Count == 0) + { + return '1'; + } + + // 3 is for CommandName, Parameter and its value + if (commandAst.CommandElements.Count != 3) + { + return '1'; + } + + if (requireScriptblock) + { + ScriptBlockExpressionAst scriptAst = commandAst.CommandElements[2] as ScriptBlockExpressionAst; + return scriptAst == null ? '1' : '0'; + } + + StringConstantExpressionAst stringValue = commandAst.CommandElements[2] as StringConstantExpressionAst; + if (stringValue != null) + { + if (errors.Length == 0) + { + return '0'; + } + + char stringTerminationChar; + + if (stringValue.StringConstantType == StringConstantType.BareWord) + { + stringTerminationChar = parameterValue[0]; + } + else if (stringValue.StringConstantType == StringConstantType.DoubleQuoted || stringValue.StringConstantType == StringConstantType.DoubleQuotedHereString) + { + stringTerminationChar = '\"'; + } + else + { + stringTerminationChar = '\''; + } + + char oppositeTerminationChar = stringTerminationChar == '\"' ? '\'' : '\"'; + + // If the string is not terminated, it should be delimited by the opposite string termination character + return oppositeTerminationChar; + } + + if (errors.Length != 0) + { + return '1'; + } + + return '0'; + } + + /// + /// Called to evaluate the value of AllMandatoryParametersHaveValues + /// + private void EvaluateAllMandatoryParametersHaveValues() + { + bool newCanRun = true; + foreach (ParameterViewModel parameter in this.parameters) + { + if (!parameter.IsMandatory) + { + continue; + } + + if (!parameter.HasValue) + { + newCanRun = false; + break; + } + } + + this.AllMandatoryParametersHaveValues = newCanRun; + } + + /// + /// If property changed will be notify + /// + /// The changed property + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + /// + /// Used to track changes to parameter values in order to verify the enabled state of buttons + /// + /// event arguments + /// event sender + private void MandatoryParameter_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!e.PropertyName.Equals("Value", StringComparison.Ordinal)) + { + return; + } + + this.EvaluateAllMandatoryParametersHaveValues(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs new file mode 100644 index 00000000000..a366c60b49c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs @@ -0,0 +1,278 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Management.Automation; +using System.Text; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains information about a single parameter inside a parameter set. + /// If a parameter with the same name belongs to two (or more) parameter sets, + /// there will be two (or more) ParameterViewModel objects for the parameters, + /// each one inside its own ParameterSetViewModel. + /// + public class ParameterViewModel : INotifyPropertyChanged + { + /// + /// ParameterMetadata contains information that is the same throughout parameter sets + /// like Name and Type. + /// Note: It also happens to contain a list of all ParameterSetMetadata for the parametersets + /// in this cmdlet, but this information is not used in this class since if a parameter is + /// in multiple parametersets, there will be a ParameterViewModel for each time the parameter + /// appears in a parameterset. + /// + private ShowCommandParameterInfo parameter; + + /// + /// value entered in the GUI for the parameter + /// + private object parameterValue; + + /// + /// Name of the parameter set this parameter is in + /// + private string parameterSetName; + + #region Construction and Destructor + /// + /// Initializes a new instance of the ParameterViewModel class. + /// + /// The parameter information for this parameter + /// the name of the parameter set this parameter is in + public ParameterViewModel(ShowCommandParameterInfo parameter, string parameterSetName) + { + if (parameter == null) + { + throw new ArgumentNullException("parameter"); + } + + if (parameterSetName == null) + { + throw new ArgumentNullException("parameterSetName"); + } + + this.parameter = parameter; + this.parameterSetName = parameterSetName; + + if (this.parameter.ParameterType.IsSwitch) + { + this.parameterValue = false; + } + else + { + this.parameterValue = string.Empty; + } + } + #endregion + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Properties + /// + /// Gets the ParameterMetadata that contains information that is the same throughout parameter sets + /// like Name and Type. + /// + public ShowCommandParameterInfo Parameter + { + get { return this.parameter; } + } + + /// + /// Gets or sets the value for this parameter from the GUI + /// + public object Value + { + get + { + return this.parameterValue; + } + + set + { + if (this.parameterValue != value) + { + this.parameterValue = value; + this.OnNotifyPropertyChanged("Value"); + } + } + } + + /// + /// Gets the parameter name + /// + public string Name + { + get { return this.Parameter.Name; } + } + + /// + /// Gets the name of the parameter set this parameter is in + /// + public string ParameterSetName + { + get { return this.parameterSetName; } + } + + /// + /// Gets a value indicating whether this parameter is in the shared parameterset + /// + public bool IsInSharedParameterSet + { + get { return CommandViewModel.IsSharedParameterSetName(this.parameterSetName); } + } + + /// + /// Gets Name with an extra suffix to indicate if the parameter is mandatory to serve + /// + public string NameTextLabel + { + get + { + return this.Parameter.IsMandatory ? + string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.MandatoryNameLabelFormat, + this.Name, + ShowCommandResources.MandatoryLabelSegment) : + string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.NameLabelFormat, + this.Name); + } + } + + /// + /// Gets Label in the case this parameter is used in a combo box + /// + public string NameCheckLabel + { + get + { + string returnValue = this.Parameter.Name; + if (this.Parameter.IsMandatory) + { + returnValue = string.Format(CultureInfo.CurrentUICulture, "{0}{1}", returnValue, ShowCommandResources.MandatoryLabelSegment); + } + + return returnValue; + } + } + + /// + /// Gets Tooltip string for the parameter + /// + public string ToolTip + { + get + { + return ParameterViewModel.EvaluateTooltip( + this.Parameter.ParameterType.FullName, + this.Parameter.Position, + this.Parameter.IsMandatory, + this.IsInSharedParameterSet, + this.Parameter.ValueFromPipeline); + } + } + + /// + /// Gets a value indicating whether the parameter is mandatory + /// + public bool IsMandatory + { + get { return this.Parameter.IsMandatory; } + } + + /// + /// Gets a value indicating whether the parameter has a value + /// + public bool HasValue + { + get + { + if (this.Value == null) + { + return false; + } + + if (this.Parameter.ParameterType.IsSwitch) + { + return ((bool?)this.Value) == true; + } + + return this.Value.ToString().Length != 0; + } + } + #endregion + + /// + /// Evaluates the tooltip based on the parameters + /// + /// parameter type name + /// parameter position + /// true if the parameter is mandatory + /// true if the parameter is shared by parameter sets + /// true if the parameter takes value from the pipeline + /// the tooltip based on the parameters + internal static string EvaluateTooltip(string typeName, int position, bool mandatory, bool shared, bool valueFromPipeline) + { + StringBuilder returnValue = new StringBuilder(string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.TypeFormat, + typeName)); + string newlineFormatString = Environment.NewLine + "{0}"; + + if (position >= 0) + { + string positionFormat = string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.PositionFormat, + position); + + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, positionFormat); + } + + string optionalOrMandatory = mandatory ? ShowCommandResources.Mandatory : ShowCommandResources.Optional; + + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, optionalOrMandatory); + + if (shared) + { + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, ShowCommandResources.CommonToAllParameterSets); + } + + if (valueFromPipeline) + { + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, ShowCommandResources.CanReceiveValueFromPipeline); + } + + return returnValue.ToString(); + } + + /// + /// If property changed will be notify + /// + /// The changed property + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml new file mode 100644 index 00000000000..0a6a3b72762 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs new file mode 100644 index 00000000000..3021114b47c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Windows; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for MultipleSelectionDialog.xaml + /// + public partial class MultipleSelectionDialog : Window + { + /// + /// Initializes a new instance of the MultipleSelectionDialog class. + /// + public MultipleSelectionDialog() + { + this.InitializeComponent(); + } + + /// + /// OK Click event function + /// + /// event sender + /// event arguments + private void ButtonOK_Click(object sender, RoutedEventArgs e) + { + this.DialogResult = true; + this.Close(); + } + + /// + /// Cancel Click event function + /// + /// event sender + /// event arguments + private void ButtonCancel_Click(object sender, RoutedEventArgs e) + { + this.DialogResult = false; + this.Close(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml new file mode 100644 index 00000000000..2f21faeef22 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs new file mode 100644 index 00000000000..82c8eb0c998 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Windows; +using System.Windows.Input; + +using Microsoft.Management.UI.Internal; +using Microsoft.Management.UI.Internal.ShowCommand; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for CmdletGUI.xaml + /// + public partial class ShowAllModulesWindow : Window + { + /// + /// private constants for ZoomLevel + /// + private double zoomLevel = 1.0; + + /// + /// Zoom Increments + /// + private const double ZOOM_INCREMENT = 0.2; + + /// + /// Max ZoomLevel + /// + private const double ZOOM_MAX = 3.0; + + /// + /// Min ZoomLevel + /// + private const double ZOOM_MIN = 0.5; + + #region Construction and Destructor + + /// + /// Initializes a new instance of the ShowAllModulesWindow class. + /// + public ShowAllModulesWindow() + { + this.InitializeComponent(); + + if (this.AllModulesControl != null && this.AllModulesControl.ShowModuleControl != null) + { + this.AllModulesControl.ShowModuleControl.Owner = this; + } + + this.SizeChanged += new SizeChangedEventHandler(this.ShowAllModulesWindow_SizeChanged); + this.LocationChanged += new System.EventHandler(this.ShowAllModulesWindow_LocationChanged); + this.StateChanged += new System.EventHandler(this.ShowAllModulesWindow_StateChanged); + this.Loaded += new RoutedEventHandler(this.ShowAllModulesWindow_Loaded); + + RoutedCommand plusSettings = new RoutedCommand(); + KeyGestureConverter keyGestureConverter = new KeyGestureConverter(); + + try + { + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn1Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn2Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn3Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn4Shortcut)); + CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Add")); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Plus")); + CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + + RoutedCommand minusSettings = new RoutedCommand(); + try + { + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut1Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut2Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut3Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut4Shortcut)); + + CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Subtract")); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Minus")); + CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + } + + /// + /// Saves the user settings + /// + /// event arguments + protected override void OnClosed(System.EventArgs e) + { + ShowCommandSettings.Default.Save(); + base.OnClosed(e); + } + + /// + /// Sets the focus on the CommandName control + /// + /// event sender + /// event arguments + private void ShowAllModulesWindow_Loaded(object sender, RoutedEventArgs e) + { + this.AllModulesControl.CommandName.Focus(); + } + + /// + /// Saves size changes in user settings + /// + /// event sender + /// event arguments + private void ShowAllModulesWindow_SizeChanged(object sender, SizeChangedEventArgs e) + { + ShowCommandSettings.Default.ShowCommandsWidth = this.Width; + ShowCommandSettings.Default.ShowCommandsHeight = this.Height; + } + + /// + /// Saves position changes in user settings + /// + /// event sender + /// event arguments + private void ShowAllModulesWindow_LocationChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowCommandsTop = this.Top; + ShowCommandSettings.Default.ShowCommandsLeft = this.Left; + } + + /// + /// Updates the user setting with window state + /// + /// event sender + /// event arguments + private void ShowAllModulesWindow_StateChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowCommandsWindowMaximized = this.WindowState == WindowState.Maximized; + } + + /// + /// Implements ZoomIn + /// + /// + /// + private void ZoomEventHandlerPlus(object sender, ExecutedRoutedEventArgs e) + { + AllModulesViewModel viewModel = this.DataContext as AllModulesViewModel; + if (viewModel == null) + { + return; + } + + if (this.zoomLevel == 0) + { + this.zoomLevel = 1; + } + + if (this.zoomLevel < ZOOM_MAX) + { + // ViewModel applies ZoomLevel after dividing it by 100, So multiply it by 100 and then later reset to normal by dividing for next zoom + this.zoomLevel = (this.zoomLevel + ZOOM_INCREMENT) * 100; + viewModel.ZoomLevel = this.zoomLevel; + this.zoomLevel /= 100; + } + } + + /// + /// Implements ZoomOut + /// + /// + /// + private void ZoomEventHandlerMinus(object sender, ExecutedRoutedEventArgs e) + { + AllModulesViewModel viewModel = this.DataContext as AllModulesViewModel; + if (viewModel == null) + { + return; + } + + if (this.zoomLevel >= ZOOM_MIN) + { + // ViewModel applies ZoomLevel after dividing it by 100, So multiply it by 100 and then later reset to normal by dividing it for next zoom + this.zoomLevel = (this.zoomLevel - ZOOM_INCREMENT) * 100; + viewModel.ZoomLevel = this.zoomLevel; + this.zoomLevel /= 100; + } + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml new file mode 100644 index 00000000000..d373f52a985 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml.cs new file mode 100644 index 00000000000..0d2000cedbc --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Windows; + +using Microsoft.Management.UI.Internal.ShowCommand; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for CmdletGUI.xaml + /// + public partial class ShowCommandWindow : Window + { + #region Construction and Destructor + + /// + /// Initializes a new instance of the ShowCommandWindow class. + /// + public ShowCommandWindow() + { + this.InitializeComponent(); + this.SizeChanged += new SizeChangedEventHandler(this.ShowCommandWindow_SizeChanged); + this.LocationChanged += new System.EventHandler(this.ShowCommandWindow_LocationChanged); + this.StateChanged += new System.EventHandler(this.ShowCommandWindow_StateChanged); + } + + /// + /// Saves the user settings + /// + /// event arguments + protected override void OnClosed(System.EventArgs e) + { + ShowCommandSettings.Default.Save(); + base.OnClosed(e); + } + + /// + /// Saves size changes in user settings + /// + /// event sender + /// event arguments + private void ShowCommandWindow_SizeChanged(object sender, SizeChangedEventArgs e) + { + ShowCommandSettings.Default.ShowOneCommandWidth = this.Width; + ShowCommandSettings.Default.ShowOneCommandHeight = this.Height; + } + + /// + /// Saves position changes in user settings + /// + /// event sender + /// event arguments + private void ShowCommandWindow_LocationChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowOneCommandTop = this.Top; + ShowCommandSettings.Default.ShowOneCommandLeft = this.Left; + } + + /// + /// Updates the user setting with window state + /// + /// event sender + /// event arguments + private void ShowCommandWindow_StateChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowOneCommandWindowMaximized = this.WindowState == WindowState.Maximized; + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/app.config b/src/Microsoft.Management.UI.Internal/app.config new file mode 100644 index 00000000000..42747cf7b50 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/app.config @@ -0,0 +1,99 @@ + + + + +
+
+ + + + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + False + + + False + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + -1 + + + -1 + + + False + + + 100 + + + 500 + + + 700 + + + True + + + + \ No newline at end of file diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs new file mode 100644 index 00000000000..03daefdfb69 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; +using System.Threading; +using System.Windows; + +using Microsoft.Management.UI; +using Microsoft.PowerShell.Commands.ShowCommandInternal; + +namespace Microsoft.PowerShell.Commands.Internal +{ + /// + /// Implements thw WPF window part of the the ShowWindow option of get-help + /// + internal static class HelpWindowHelper + { + /// + /// Shows the help window + /// + /// object with help information + /// cmdlet calling this method + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from methods called using reflection")] + private static void ShowHelpWindow(PSObject helpObj, PSCmdlet cmdlet) + { + Window ownerWindow = ShowCommandHelper.GetHostWindow(cmdlet); + if (ownerWindow != null) + { + ownerWindow.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + HelpWindow helpWindow = new HelpWindow(helpObj); + helpWindow.Owner = ownerWindow; + helpWindow.Show(); + + helpWindow.Closed += new EventHandler(delegate(object sender, EventArgs e) { ownerWindow.Focus(); }); + }), + string.Empty); + return; + } + + Thread guiThread = new Thread( + (ThreadStart)delegate + { + HelpWindow helpWindow = new HelpWindow(helpObj); + helpWindow.ShowDialog(); + }); + guiThread.SetApartmentState(ApartmentState.STA); + guiThread.Start(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs new file mode 100644 index 00000000000..28d2523f459 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs @@ -0,0 +1,596 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; +using System.Threading; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// OutGridViewWindow definition for PowerShell command out-gridview. + /// + internal class OutGridViewWindow + { + #region private Fields + + /// + /// Zoom Increments + /// + private const double ZOOM_INCREMENT = 0.2; + + /// + /// Max ZoomLevel + /// + private const double ZOOM_MAX = 3.0; + + /// + /// Min ZoomLevel + /// + private const double ZOOM_MIN = 0.5; + + /// + /// Window for gridView. + /// + private Window gridViewWindow; + + /// + /// Local ManagementList. + /// + private ManagementList managementList; + + /// + /// A collection of PSObjects to be data bound to the local Management List. + /// + private ObservableCollection listItems; + + /// + /// Event used for the thread gridViewWindows signaling main thread after Windows loaded + /// + private AutoResetEvent gridViewWindowLoaded; + + /// Is used to store any Management list calls exceptions. + private Exception exception = null; + + /// + /// Is used to block thread of the pipeline. + /// + private AutoResetEvent closedEvent; + + /// + /// OK Button's content. + /// + private static readonly string OKButtonContent = XamlLocalizableResources.OutGridView_Button_OK; + + /// + /// Cancel Button's content. + /// + private static readonly string CancelButtonContent = XamlLocalizableResources.OutGridView_Button_Cancel; + + /// + /// Used to store selected items in the ok processing + /// + private List selectedItems; + + /// + /// The GUI thread of Out-GridView + /// + private Thread guiThread; + + /// + /// private constants for ZoomLevel + /// + private double zoomLevel = 1.0; + + #endregion private Fields + + #region internal Constructors + + /// + /// Constructor for OutGridView. + /// + internal OutGridViewWindow() + { + // Initialize the data source collection. + this.listItems = new ObservableCollection(); + } + + #endregion internal Constructors + + #region private delegates + /// + /// ThreadDelegate definition. + /// + /// Start GridView Window delegate. + private delegate void ThreadDelegate(object arg); + + #endregion private delegates + + #region Private method that are intended to be called by the Out-GridView cmdlet. + + /// + /// Start a new thread as STA for gridView Window. + /// + /// commands of the PowerShell. + /// selection mode of the list + /// closedEvent + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private void StartWindow(string invocation, string outputModeOptions, AutoResetEvent closedEvent) + { + this.closedEvent = closedEvent; + this.gridViewWindowLoaded = new AutoResetEvent(false); + + ParameterizedThreadStart threadStart = new ParameterizedThreadStart( + new ThreadDelegate(delegate + { + try + { + this.gridViewWindow = new Window(); + this.managementList = CreateManagementList(outputModeOptions); + this.gridViewWindow.Loaded += new RoutedEventHandler(this.GridViewWindowLoaded); + this.gridViewWindow.Content = CreateMainGrid(outputModeOptions); + this.gridViewWindow.Title = invocation; + this.gridViewWindow.Closed += new EventHandler(this.GridViewWindowClosed); + + RoutedCommand plusSettings = new RoutedCommand(); + KeyGestureConverter keyGestureConverter = new KeyGestureConverter(); + + try + { + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn1Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn2Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn3Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn4Shortcut)); + this.gridViewWindow.CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Add")); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Plus")); + this.gridViewWindow.CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + + RoutedCommand minusSettings = new RoutedCommand(); + try + { + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut1Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut2Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut3Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut4Shortcut)); + + this.gridViewWindow.CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Subtract")); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Minus")); + this.gridViewWindow.CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + + this.gridViewWindow.ShowDialog(); + } + catch (Exception e) + { + // Store the exception in a local variable that will be checked later. + if (e.InnerException != null) + { + this.exception = e.InnerException; + } + else + { + this.exception = e; + } + } + })); + + guiThread = new Thread(threadStart); + guiThread.SetApartmentState(ApartmentState.STA); + + guiThread.Start(); + } + + /// + /// Implements ZoomIn + /// + /// + /// + private void ZoomEventHandlerPlus(object sender, ExecutedRoutedEventArgs e) + { + if (this.zoomLevel == 0) + { + this.zoomLevel = 1; + } + + if (this.zoomLevel < ZOOM_MAX) + { + this.zoomLevel = this.zoomLevel + ZOOM_INCREMENT; + Grid g = this.gridViewWindow.Content as Grid; + + if (g != null) + { + g.LayoutTransform = new ScaleTransform(this.zoomLevel, this.zoomLevel, 0, 0); + } + } + } + + /// + /// Implements ZoomOut + /// + /// + /// + private void ZoomEventHandlerMinus(object sender, ExecutedRoutedEventArgs e) + { + if (this.zoomLevel >= ZOOM_MIN) + { + this.zoomLevel = this.zoomLevel - ZOOM_INCREMENT; + Grid g = this.gridViewWindow.Content as Grid; + if (g != null) + { + g.LayoutTransform = new ScaleTransform(this.zoomLevel, this.zoomLevel, 0, 0); + } + } + } + + /// + /// Creates a new ManagementList. + /// + /// Output mode of the out-gridview + /// A new ManagementList + private ManagementList CreateManagementList(string outputMode) + { + ManagementList newList = new ManagementList(); + + newList.ViewSaverUserActionState = UserActionState.Hidden; + newList.ViewManagerUserActionState = UserActionState.Hidden; + newList.List.VerticalAlignment = VerticalAlignment.Stretch; + newList.List.SetValue(Grid.RowProperty, 0); + newList.List.SelectionMode = (outputMode == "Single") ? SelectionMode.Single : SelectionMode.Extended; + + return newList; + } + + /// + /// Creates a new main grid for window. + /// + /// Output mode of the out-gridview + /// A new mainGrid + private Grid CreateMainGrid(string outputMode) + { + Grid mainGrid = new Grid(); + mainGrid.RowDefinitions.Add(new RowDefinition()); + mainGrid.RowDefinitions[0].Height = new GridLength(1, GridUnitType.Star); + mainGrid.Children.Add(managementList); + + if (outputMode == "None") + { + return mainGrid; + } + + // OK and Cancel button should only be displayed if OutputMode is not None. + mainGrid.RowDefinitions.Add(new RowDefinition()); + mainGrid.RowDefinitions[1].Height = GridLength.Auto; + mainGrid.Children.Add(CreateButtonGrid()); + + return mainGrid; + } + + /// + /// Creates a OK button. + /// + /// A new buttonGrid + private Grid CreateButtonGrid() + { + Grid buttonGrid = new Grid(); + + //// This will allow OK and Cancel to have the same width + buttonGrid.SetValue(Grid.IsSharedSizeScopeProperty, true); + buttonGrid.ColumnDefinitions.Add(new ColumnDefinition()); + buttonGrid.ColumnDefinitions.Add(new ColumnDefinition()); + buttonGrid.ColumnDefinitions[0].Width = GridLength.Auto; + buttonGrid.ColumnDefinitions[0].SharedSizeGroup = "okCancel"; + buttonGrid.ColumnDefinitions[1].Width = GridLength.Auto; + buttonGrid.ColumnDefinitions[1].SharedSizeGroup = "okCancel"; + buttonGrid.HorizontalAlignment = HorizontalAlignment.Right; + buttonGrid.SetValue(Grid.RowProperty, 1); + + //// This will add OK and Cancel button to buttonGrid. + buttonGrid.Children.Add(CreateOKButton()); + buttonGrid.Children.Add(CreateCancelButton()); + + return buttonGrid; + } + + /// + /// Creates a OK button. + /// + /// A new OK button + private Button CreateOKButton() + { + Button ok = new Button(); + ok.Content = OKButtonContent; + ok.Margin = new Thickness(5); + ok.Padding = new Thickness(2); + ok.SetValue(Grid.ColumnProperty, 0); + ok.IsDefault = true; + ok.SetValue(AutomationProperties.AutomationIdProperty, "OGVOK"); + ok.Click += new RoutedEventHandler(OK_Click); + return ok; + } + + /// + /// Creates a Cancel button. + /// + /// A new Cancel button + private Button CreateCancelButton() + { + Button cancel = new Button(); + cancel.Content = CancelButtonContent; + cancel.Margin = new Thickness(5); + cancel.Padding = new Thickness(2); + cancel.SetValue(Grid.ColumnProperty, 1); + cancel.IsCancel = true; + cancel.SetValue(AutomationProperties.AutomationIdProperty, "OGVCancel"); + cancel.Click += new RoutedEventHandler(Cancel_Click); + return cancel; + } + + /// + /// Store the selected items for use in EndProcessing + /// + /// event sender + /// event arguments + private void OK_Click(object sender, RoutedEventArgs e) + { + if (this.managementList.List.SelectedItems.Count != 0) + { + this.selectedItems = new List(); + foreach (PSObject obj in this.managementList.List.SelectedItems) + { + this.selectedItems.Add(obj); + } + } + + this.gridViewWindow.Close(); + } + + /// + /// Closes the window + /// + /// event sender + /// event arguments + private void Cancel_Click(object sender, RoutedEventArgs e) + { + this.gridViewWindow.Close(); + } + + /// + /// Gets selected items from List. + /// + /// Selected items of the list + private List SelectedItems() + { + return this.selectedItems; + } + + /// + /// Closes the window + /// + public void CloseWindow() + { + this.gridViewWindow.Dispatcher.Invoke(new ThreadStart(delegate { this.gridViewWindow.Close(); })); + } + + /// + /// Add column definitions to the underlying management list. + /// + /// An array of property names to add. + /// An array of display names to add. + /// An array of types to add. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private void AddColumns(string[] propertyNames, string[] displayNames, Type[] types) + { + // Wait for the gridViewWindow thread to signal that loading of Window is done + if (this.gridViewWindowLoaded != null) + { + this.gridViewWindowLoaded.WaitOne(); + this.gridViewWindowLoaded = null; + } + + this.managementList.Dispatcher.Invoke( + System.Windows.Threading.DispatcherPriority.Normal, + new Action( + delegate() + { + // Pick the length of the shortest incoming arrays. Normally all incoming arrays should be of the same length. + int length = propertyNames.Length; + if (length > displayNames.Length) + { + length = displayNames.Length; + } + + if (length > types.Length) + { + length = types.Length; + } + + try + { + // Clear all columns in case the view is changed. + this.managementList.List.Columns.Clear(); + + // Clear column filter rules. + this.managementList.AddFilterRulePicker.ColumnFilterRules.Clear(); + + // Add columns with provided names and Types. + for (int i = 0; i < propertyNames.Length; ++i) + { + DataTemplate dataTemplate; + bool haveTemplate = this.managementList.FilterRulePanel.TryGetContentTemplate(types[i], out dataTemplate); + InnerListColumn column = null; + + if (haveTemplate) + { + column = new InnerListColumn(new UIPropertyGroupDescription(propertyNames[i], displayNames[i], types[i])); + } + else + { + column = new InnerListColumn(new UIPropertyGroupDescription(propertyNames[i], displayNames[i], typeof(string))); + } + + this.managementList.AddColumn(column); + } + + this.managementList.List.SetColumnHeaderActions(); + + if (this.managementList.List.ItemsSource == null) + { + // Setting ItemsSource implicitly regenerates all columns. + this.managementList.List.ItemsSource = this.listItems; + } + + // Set focus on ListView + this.managementList.List.SelectedIndex = 0; + this.managementList.List.Focus(); + } + catch (Exception e) + { + // Store the exception in a local variable that will be checked later. + if (e.InnerException != null) + { + this.exception = e.InnerException; + } + else + { + this.exception = e; + } + } + })); + } + + /// + /// Add an item to ObservableCollection. + /// + /// PSObject of comlet data. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private void AddItem(PSObject value) + { + if (this.GetWindowClosedStatus()) + { + return; + } + + this.managementList.Dispatcher.BeginInvoke( + System.Windows.Threading.DispatcherPriority.Normal, + new Action( + delegate() + { + try + { + this.listItems.Add(value); + } + catch (Exception e) + { + // Store the exception in a local variable that will be checked later. + if (e.InnerException != null) + { + this.exception = e.InnerException; + } + else + { + this.exception = e; + } + } + })); + } + + /// + /// Returns the state of GridView Window. + /// + /// The status of GridView Window close or not. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private bool GetWindowClosedStatus() + { + if (this.closedEvent == null) + { + return false; + } + + return this.closedEvent.WaitOne(0); + } + + /// + /// Returns any exception that has been thrown by previous method calls. + /// + /// The thrown and caught exception. It returns null if no exceptions were thrown by any previous method calls. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private Exception GetLastException() + { + Exception local = this.exception; + + if (local != null) + { + // Clear the caught exception. + this.exception = null; + return local; + } + + return this.exception; + } + + #endregion Private method that are intended to be called by the Out-GridView cmdlet. + + #region Private methods + + /// + /// GridView Window is closing callback process. + /// + /// The sender object. + /// Event Args. + private void GridViewWindowClosed(object sender, EventArgs e) + { + if (this.closedEvent != null && !this.closedEvent.SafeWaitHandle.IsClosed) + { + try + { + this.closedEvent.Set(); + } + catch (ObjectDisposedException) + { + // we tried to avoid this exception with "&& !this.closedEvent.SafeWaitHandle.IsClosed" + // but since this runs in a different thread the if condition could be evaluated and after that + // the handle disposed + } + } + } + + /// + /// Set loaded as true when this method invoked. + /// + /// The sender object. + /// RoutedEvent Args. + private void GridViewWindowLoaded(object sender, RoutedEventArgs e) + { + // signal the main thread + this.gridViewWindowLoaded.Set(); + + // Make gridview window as active window + this.gridViewWindow.Activate(); + + // Set up AutomationId and Name + AutomationProperties.SetName(this.gridViewWindow, GraphicalHostResources.OutGridViewWindowObjectName); + AutomationProperties.SetAutomationId(this.gridViewWindow, "OutGridViewWindow"); + } + + #endregion Private methods + } +} diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs new file mode 100644 index 00000000000..91d5c860bb2 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs @@ -0,0 +1,1304 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; +using System.Reflection; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Threading; + +using Microsoft.Management.UI; +using Microsoft.Management.UI.Internal; +using Microsoft.Management.UI.Internal.ShowCommand; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Implements thw WPF window part of the show-command cmdlet + /// + internal class ShowCommandHelper : IDisposable + { + #region fields + + internal const string CommandTypeSegment = " -CommandType Cmdlet, Function, Script, ExternalScript"; + + /// + /// Method that will return the dialog from ShowAllModulesWindow or ShowCommandWindow. + /// This is necessary because the PlainInvokeAndShowDialog thread starter cannot receive parameters + /// + private DispatcherOperationCallback methodThatReturnsDialog; + + /// + /// Event set when the window is closed + /// + private AutoResetEvent windowClosed = new AutoResetEvent(false); + + /// + /// Event set when help is needed + /// + private AutoResetEvent helpNeeded = new AutoResetEvent(false); + + /// + /// Event set when it is necessary to import a module + /// + private AutoResetEvent importModuleNeeded = new AutoResetEvent(false); + + /// + /// Event set when the window is loaded + /// + private AutoResetEvent windowLoaded = new AutoResetEvent(false); + + /// + /// String with the command that needs help set when helpNeeded is set + /// + private string commandNeedingHelp; + + /// + /// String with the command name that needs to import a module + /// + private string commandNeedingImportModule; + + /// + /// String with the module name that needs to be imported + /// + private string parentModuleNeedingImportModule; + + /// + /// String with the selected module at the time a module needs to be imported + /// + private string selectedModuleNeedingImportModule; + + /// + /// Keeps the window for the implementation of CloseWindow + /// + private Window window; + + /// + /// host window, if any + /// + private Window hostWindow; + + /// + /// ViewModel when showing all modules + /// + private AllModulesViewModel allModulesViewModel; + + /// + /// ViewModel when showing a single command + /// + private CommandViewModel commandViewModel; + + /// + /// true when the window is closed with cancel + /// + private bool dialogCanceled = true; + #endregion fields + + #region GetSerializedCommand script + + private const string ScriptGetSerializedCommand = @" +Function PSGetSerializedShowCommandInfo +{ + Function GetParameterType + { + param ( + [Type] $parameterType) + + $returnParameterType = new-object PSObject + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""FullName"" -Value $parameterType.FullName + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""IsEnum"" -Value $parameterType.IsEnum + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""IsArray"" -Value $parameterType.IsArray + + if ($parameterType.IsEnum) + { + $enumValues = [System.Enum]::GetValues($parameterType) + } + else + { + $enumValues = [string[]] @() + } + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""EnumValues"" -Value $enumValues + + if ($parameterType.IsArray) + { + $hasFlagAttribute = ($parameterType.GetCustomAttributes([System.FlagsAttribute], $true).Length -gt 0) + + # Recurse into array elements. + $elementType = GetParameterType($parameterType.GetElementType()) + } + else + { + $hasFlagAttribute = $false + $elementType = $null + } + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""HasFlagAttribute"" -Value $hasFlagAttribute + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""ElementType"" -Value $elementType + + + if (!($parameterType.IsEnum) -and !($parameterType.IsArray)) + { + $implementsDictionary = [System.Collections.IDictionary].IsAssignableFrom($parameterType) + } + else + { + $implementsDictionary = $false + } + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""ImplementsDictionary"" -Value $implementsDictionary + + return $returnParameterType + } + + Function GetParameterInfo + { + param ( + $parameters) + + [PSObject[]] $parameterInfos = @() + + foreach ($parameter in $parameters) + { + $parameterInfo = new-object PSObject + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $parameter.Name + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""IsMandatory"" -Value $parameter.IsMandatory + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""ValueFromPipeline"" -Value $parameter.ValueFromPipeline + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""Position"" -Value $parameter.Position + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""ParameterType"" -Value (GetParameterType($parameter.ParameterType)) + + $hasParameterSet = $false + [string[]] $validValues = @() + if ($PSVersionTable.PSVersion.Major -gt 2) + { + $validateSetAttributes = $parameter.Attributes | Where { + [ValidateSet].IsAssignableFrom($_.GetType()) + } + if (($validateSetAttributes -ne $null) -and ($validateSetAttributes.Count -gt 0)) + { + $hasParameterSet = $true + $validValues = $validateSetAttributes[0].ValidValues + } + } + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""HasParameterSet"" -Value $hasParameterSet + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""ValidParamSetValues"" -Value $validValues + + $parameterInfos += $parameterInfo + } + + return (,$parameterInfos) + } + + Function GetParameterSets + { + param ( + [System.Management.Automation.CommandInfo] $cmdInfo + ) + + $parameterSets = $null + try + { + $parameterSets = $cmdInfo.ParameterSets + } + catch [System.InvalidOperationException] { } + catch [System.Management.Automation.PSNotSupportedException] { } + catch [System.Management.Automation.PSNotImplementedException] { } + + if (($parameterSets -eq $null) -or ($parameterSets.Count -eq 0)) + { + return (,@()) + } + + [PSObject[]] $returnParameterSets = @() + + foreach ($parameterSet in $parameterSets) + { + $parameterSetInfo = new-object PSObject + $parameterSetInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $parameterSet.Name + $parameterSetInfo | Add-Member -MemberType NoteProperty -Name ""IsDefault"" -Value $parameterSet.IsDefault + $parameterSetInfo | Add-Member -MemberType NoteProperty -Name ""Parameters"" -Value (GetParameterInfo($parameterSet.Parameters)) + + $returnParameterSets += $parameterSetInfo + } + + return (,$returnParameterSets) + } + + Function GetModuleInfo + { + param ( + [System.Management.Automation.CommandInfo] $cmdInfo + ) + + if ($cmdInfo.ModuleName -ne $null) + { + $moduleName = $cmdInfo.ModuleName + } + else + { + $moduleName = """" + } + + $moduleInfo = new-object PSObject + $moduleInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $moduleName + + return $moduleInfo + } + + Function ConvertToShowCommandInfo + { + param ( + [System.Management.Automation.CommandInfo] $cmdInfo + ) + + $showCommandInfo = new-object PSObject + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $cmdInfo.Name + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""ModuleName"" -Value $cmdInfo.ModuleName + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""Module"" -Value (GetModuleInfo($cmdInfo)) + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""CommandType"" -Value $cmdInfo.CommandType + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""Definition"" -Value $cmdInfo.Definition + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""ParameterSets"" -Value (GetParameterSets($cmdInfo)) + + return $showCommandInfo + } + + $commandList = @(""Cmdlet"", ""Function"", ""Script"", ""ExternalScript"") + if ($PSVersionTable.PSVersion.Major -gt 2) + { + $commandList += ""Workflow"" + } + + foreach ($command in @(Get-Command -CommandType $commandList)) + { + Write-Output (ConvertToShowCommandInfo($command)) + } +}"; + + #endregion + + #region constructor and destructor + /// + /// Prevents a default instance of the ShowCommandHelper class from being created + /// + private ShowCommandHelper() + { + } + + /// + /// Finalizes an instance of the ShowCommandHelper class + /// + ~ShowCommandHelper() + { + this.Dispose(false); + } + #endregion constructor and destructor + + #region properties called using reflection + /// + /// Gets the Screen Width + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static double ScreenWidth + { + get + { + return System.Windows.SystemParameters.PrimaryScreenWidth; + } + } + + /// + /// Gets the Screen Height + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static double ScreenHeight + { + get + { + return System.Windows.SystemParameters.PrimaryScreenHeight; + } + } + + /// + /// Gets the event set when the show-command window is closed + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent WindowClosed + { + get + { + return this.windowClosed; + } + } + + /// + /// Gets the event set when help is needed for a command + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent HelpNeeded + { + get + { + return this.helpNeeded; + } + } + + /// + /// Gets the event set when it is necessary to import a module + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent ImportModuleNeeded + { + get + { + return this.importModuleNeeded; + } + } + + /// + /// Gets the event set when the window is loaded + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent WindowLoaded + { + get + { + return this.windowLoaded; + } + } + + /// + /// Gets the command needing help when HelpNeeded is set + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private string CommandNeedingHelp + { + get + { + return this.commandNeedingHelp; + } + } + + /// + /// Gets the module we want to import + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private string ParentModuleNeedingImportModule + { + get + { + return this.parentModuleNeedingImportModule; + } + } + + /// + /// Gets a value indicating whether there is a host window + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private bool HasHostWindow + { + get + { + return this.hostWindow != null; + } + } + #endregion properties called using reflection + + #region public Dispose + /// + /// Dispose method in IDisposeable + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + #endregion public Dispose + + #region internal static methods called using reflection from show-command + /// + /// Sets the text in the clipboard + /// + /// text to set the clipboard to + internal static void SetClipboardText(string text) + { + try + { + Clipboard.SetText(text); + } + catch (System.Runtime.InteropServices.COMException) + { + // This is the recommended way to set clipboard text + System.Threading.Thread.Sleep(0); + try + { + Clipboard.SetText(text); + } + catch (System.Runtime.InteropServices.COMException) + { + } + } + } + + /// + /// Gets the command to be run to get commands and imported modules + /// + /// Boolean flag determining whether Show-Command is queried in the local or remote runspace scenario + /// Boolean flag to indicate that it is the second attempt to query Show-Command data + /// the command to be run to get commands and imported modules + internal static string GetShowAllModulesCommand(bool isRemoteRunspace = false, bool isFirstChance = true) + { + string scriptBase; + + if (isRemoteRunspace) + { + if (isFirstChance) + { + // Return command to run. + scriptBase = "@(Get-Command " + ShowCommandHelper.CommandTypeSegment + @" -ShowCommandInfo)"; + } + else + { + // Return script to run. + scriptBase = GetSerializedCommandScript(); + } + } + else + { + scriptBase = "@(Get-Command " + ShowCommandHelper.CommandTypeSegment + ")"; + } + + scriptBase += ShowCommandHelper.GetGetModuleSuffix(); + return scriptBase; + } + + /// + /// Retrieves the script for Get-SerializedCommand from local machine + /// + /// String representation of the script for Get-SerializedCommand + private static string GetSerializedCommandScript() + { + return string.Format( + CultureInfo.InvariantCulture, + "@({0};{1};{2})", + ScriptGetSerializedCommand, + @"PSGetSerializedShowCommandInfo", + @"Remove-Item -Path 'function:\PSGetSerializedShowCommandInfo' -Force"); + } + + /// + /// Gets the command to be run to in order to import a module and refresh the command data + /// + /// module we want to import + /// Boolean flag determining whether Show-Command is queried in the local or remote runspace scenario + /// Boolean flag to indicate that it is the second attempt to query Show-Command data + /// the command to be run to in order to import a module and refresh the command data + internal static string GetImportModuleCommand(string module, bool isRemoteRunspace = false, bool isFirstChance = true) + { + string scriptBase = "Import-Module " + ShowCommandHelper.SingleQuote(module); + + if (isRemoteRunspace) + { + if (isFirstChance) + { + scriptBase += ";@(Get-Command " + ShowCommandHelper.CommandTypeSegment + @" -ShowCommandInfo )"; + } + else + { + scriptBase += GetSerializedCommandScript(); + } + } + else + { + scriptBase += ";@(Get-Command " + ShowCommandHelper.CommandTypeSegment + ")"; + } + + scriptBase += ShowCommandHelper.GetGetModuleSuffix(); + return scriptBase; + } + + /// + /// gets the command to be run in order to show help for a command + /// + /// command we want to get help from + /// the command to be run in order to show help for a command + internal static string GetHelpCommand(string command) + { + return "Get-Help " + ShowCommandHelper.SingleQuote(command); + } + + /// + /// Constructs a dictionary of imported modules based on the module names + /// + /// the imported modules + /// a dictionary of imported modules based on the module names + internal static Dictionary GetImportedModulesDictionary(object[] moduleObjects) + { + Dictionary returnValue = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (PSObject rawModule in moduleObjects) + { + ShowCommandModuleInfo wrappedModule = null; + PSModuleInfo module = rawModule.BaseObject as PSModuleInfo; + if (module != null) + { + wrappedModule = new ShowCommandModuleInfo(module); + } + else + { + wrappedModule = new ShowCommandModuleInfo(rawModule); + } + + // It is probably an issue somewhere else that a module would show up twice in the list, but we want to avoid + // throwing an exception regarding that in returnValue.Add + if (!returnValue.ContainsKey(wrappedModule.Name)) + { + returnValue.Add(wrappedModule.Name, wrappedModule); + } + } + + return returnValue; + } + + /// + /// Constructs a list of commands out of + /// + /// the results of a get-command command + /// a list of commands out of + internal static List GetCommandList(object[] commandObjects) + { + List returnValue = new List(); + foreach (PSObject rawCommand in commandObjects) + { + CommandInfo command = rawCommand.BaseObject as CommandInfo; + if (command != null) + { + returnValue.Add(new ShowCommandCommandInfo(command)); + } + else + { + PSObject obj = rawCommand as PSObject; + if (obj != null) + { + returnValue.Add(new ShowCommandCommandInfo(obj)); + } + } + } + + return returnValue; + } + + /// + /// Constructs an array of objects out of + /// + /// The result of a get-command command + /// An array of objects out of + internal static object[] ObjectArrayFromObjectCollection(object commandObjects) + { + object[] objectArray = commandObjects as object[]; + if (objectArray == null) + { + objectArray = ((System.Collections.ArrayList)commandObjects).ToArray(); + } + + return objectArray; + } + + /// + /// Called after a module in is imported to refresh the view model. + /// Gets a new AllModulesViewModel populated with and . + /// The is used to cleanup event listening in the old view model and to copy NoCommonParameters. + /// The new ViewModel will have the command selected according to , + /// and . + /// + /// the viewModel before the module was imported + /// the list of imported modules + /// the list of commands + /// the name of the module that was selected in + /// the name of the module that was imported + /// the name of the command that was selected in + /// The new ViewModel based on and . + internal static AllModulesViewModel GetNewAllModulesViewModel(AllModulesViewModel oldViewModel, Dictionary importedModules, IEnumerable commands, string selectedModuleNeedingImportModule, string parentModuleNeedingImportModule, string commandNeedingImportModule) + { + string oldFilter = null; + + if (oldViewModel.SelectedModule != null) + { + // this will allow the old view model to stop listening for events before we + // replace it with a new view model + oldViewModel.SelectedModule.SelectedCommand = null; + oldViewModel.SelectedModule = null; + oldFilter = oldViewModel.CommandNameFilter; + } + + AllModulesViewModel returnValue = new AllModulesViewModel(importedModules, commands, oldViewModel.NoCommonParameter); + if (!string.IsNullOrEmpty(oldFilter)) + { + returnValue.CommandNameFilter = oldFilter; + } + + if (selectedModuleNeedingImportModule == null || parentModuleNeedingImportModule == null) + { + return returnValue; + } + + ModuleViewModel moduleToSelect = returnValue.Modules.Find( + new Predicate(delegate(ModuleViewModel module) + { + return module.Name.Equals(selectedModuleNeedingImportModule, StringComparison.OrdinalIgnoreCase) ? true : false; + })); + + if (moduleToSelect == null) + { + return returnValue; + } + + returnValue.SelectedModule = moduleToSelect; + + CommandViewModel commandToSelect = moduleToSelect.Commands.Find( + new Predicate(delegate(CommandViewModel command) + { + return command.ModuleName.Equals(parentModuleNeedingImportModule, StringComparison.OrdinalIgnoreCase) && + command.Name.Equals(commandNeedingImportModule, StringComparison.OrdinalIgnoreCase) ? true : false; + })); + + if (commandToSelect == null) + { + return returnValue; + } + + moduleToSelect.SelectedCommand = commandToSelect; + return returnValue; + } + + /// + /// Gets an error message to be displayed when failed to import a module + /// + /// command belongiong to the module to import + /// module to import + /// error importing the module + /// an error message to be displayed when failed to import a module + internal static string GetImportModuleFailedMessage(string command, string module, string error) + { + return string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.ImportModuleFailedFormat, + command, + module, + error); + } + + /// + /// Single quotes + /// + /// string to quote + /// single quoted + internal static string SingleQuote(string str) + { + if (str == null) + { + str = string.Empty; + } + + return "\'" + System.Management.Automation.Language.CodeGeneration.EscapeSingleQuotedStringContent(str) + "\'"; + } + #endregion internal static methods called using reflection from show-command + + #region internal static methods used internally in this assembly + /// + /// Gets the host window, if it is present or null if it is not + /// + /// cmdlet calling this method + /// the host window, if it is present or null if it is not + internal static Window GetHostWindow(PSCmdlet cmdlet) + { + PSPropertyInfo windowProperty = cmdlet.Host.PrivateData.Properties["Window"]; + if (windowProperty == null) + { + return null; + } + + try + { + return windowProperty.Value as Window; + } + catch (ExtendedTypeSystemException) + { + return null; + } + } + #endregion internal static methods used internally in this assembly + + #region static private methods used only on this file + + /// + /// Gets a property value using reflection + /// + /// type containing the property + /// object containing the property (null for a static property) + /// name of property to get + /// flags passed to reflection + /// + /// property value or null if it was not able to retrieve it. This method is not suitable to return a property value that might be null. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from a method called using reflection")] + private static object GetPropertyValue(Type type, object obj, string propertyName, BindingFlags bindingFlags) + { + PropertyInfo property = type.GetProperty(propertyName, bindingFlags); + if (property == null) + { + return null; + } + + try + { + return property.GetValue(obj, new object[] { }); + } + catch (ArgumentException) + { + return null; + } + catch (TargetException) + { + return null; + } + catch (TargetParameterCountException) + { + return null; + } + catch (MethodAccessException) + { + return null; + } + catch (TargetInvocationException) + { + return null; + } + } + + /// + /// Sets a property value using reflection + /// + /// type containing the property + /// object containing the property (null for a static property) + /// name of property to set + /// value to set the property with + /// flags passed to reflection + /// true if it was able to set + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from a method called using reflection")] + private static bool SetPropertyValue(Type type, object obj, string propertyName, object value, BindingFlags bindingFlags) + { + PropertyInfo property = type.GetProperty(propertyName, bindingFlags); + if (property == null) + { + return false; + } + + try + { + property.SetValue(obj, value, new object[] { }); + } + catch (ArgumentException) + { + return false; + } + catch (TargetException) + { + return false; + } + catch (TargetParameterCountException) + { + return false; + } + catch (MethodAccessException) + { + return false; + } + catch (TargetInvocationException) + { + return false; + } + + return true; + } + + /// + /// Gets the suffix that adds imported modules to a command + /// + /// the suffix that adds imported modules to a command + private static string GetGetModuleSuffix() + { + return ",@(get-module)"; + } + + #endregion static private methods used only on this file + + #region private methods called using reflection from show-command + /// + /// Gets the command to be run when calling show-command for a particular command + /// + /// the particular command we are running show-command on + /// true if we want to include aliases and retrieve modules + /// the command to be run when calling show-command for a particular command + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static string GetShowCommandCommand(string commandName, bool includeAliasAndModules) + { + string quotedCommandName = ShowCommandHelper.SingleQuote(commandName); + return "@(get-command " + quotedCommandName + " " + ShowCommandHelper.CommandTypeSegment + + (includeAliasAndModules ? ",Alias" : string.Empty) + ")" + + (includeAliasAndModules ? ShowCommandHelper.GetGetModuleSuffix() : string.Empty); + } + + /// + /// Gets a CommandViewModel of a CommandInfo + /// + /// command we want to get a CommandViewModel of + /// true if we do not want common parameters + /// the loaded modules + /// True to qualify command with module name in GetScript + /// a CommandViewModel of a CommandInfo + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static object GetCommandViewModel(ShowCommandCommandInfo command, bool noCommonParameter, Dictionary importedModules, bool moduleQualify) + { + CommandViewModel returnValue = CommandViewModel.GetCommandViewModel(new ModuleViewModel(command.ModuleName, importedModules), command, noCommonParameter); + returnValue.ModuleQualifyCommandName = moduleQualify; + return returnValue; + } + + /// + /// Dispatches a message to the window for it to activate + /// + /// window to be activated + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from ActivateWindow() which is called using reflection")] + private static void ActivateWindow(Window window) + { + window.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + window.Activate(); + }), + string.Empty); + } + + /// + /// Shows the window listing cmdlets + /// + /// cmdlet calling this method + /// All loaded modules + /// commands to be listed + /// true if we should not show common parameters + /// window width + /// window height + /// true if the GUI should mention ok instead of run + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ShowAllModulesWindow(PSCmdlet cmdlet, Dictionary importedModules, IEnumerable commands, bool noCommonParameter, double windowWidth, double windowHeight, bool passThrough) + { + this.methodThatReturnsDialog = new DispatcherOperationCallback(delegate(object ignored) + { + ShowAllModulesWindow allModulesWindow = new ShowAllModulesWindow(); + this.allModulesViewModel = new AllModulesViewModel(importedModules, commands, noCommonParameter); + + this.SetupButtonEvents(allModulesWindow.Run, allModulesWindow.Copy, allModulesWindow.Cancel, passThrough); + this.SetupWindow(allModulesWindow); + this.SetupViewModel(); + CommonHelper.SetStartingPositionAndSize( + allModulesWindow, + ShowCommandSettings.Default.ShowCommandsTop, + ShowCommandSettings.Default.ShowCommandsLeft, + windowWidth != 0.0 && windowWidth > allModulesWindow.MinWidth ? windowWidth : ShowCommandSettings.Default.ShowCommandsWidth, + windowHeight != 0.0 && windowHeight > allModulesWindow.MinHeight ? windowHeight : ShowCommandSettings.Default.ShowCommandsHeight, + allModulesWindow.Width, + allModulesWindow.Height, + ShowCommandSettings.Default.ShowCommandsWindowMaximized); + + return allModulesWindow; + }); + + this.CallShowDialog(cmdlet); + } + + /// + /// Calls ShowsDialog on methodThatReturnsDialog either in a separate thread or dispatched + /// to the hostWindow thread if there is a hostWindow + /// + /// cmdlet used to retrieve the host window + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from a method called using reflection")] + private void CallShowDialog(PSCmdlet cmdlet) + { + this.hostWindow = ShowCommandHelper.GetHostWindow(cmdlet); + if (this.hostWindow == null) + { + Thread guiThread = new Thread(new ThreadStart(this.PlainInvokeAndShowDialog)); + guiThread.SetApartmentState(ApartmentState.STA); + guiThread.Start(); + return; + } + + this.hostWindow.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + Window childWindow = (Window)this.methodThatReturnsDialog.Invoke(null); + childWindow.Owner = this.hostWindow; + childWindow.Show(); + }), + string.Empty); + } + + /// + /// Called from CallMethodThatShowsDialog as the thtead start when there is no host window + /// + private void PlainInvokeAndShowDialog() + { + ((Window)this.methodThatReturnsDialog.Invoke(null)).ShowDialog(); + } + + /// + /// Shows the window for the cmdlet + /// + /// cmdlet calling this method + /// command to show in the window + /// window width + /// window height + /// true if the GUI should mention ok instead of run + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ShowCommandWindow(PSCmdlet cmdlet, object commandViewModelObj, double windowWidth, double windowHeight, bool passThrough) + { + this.methodThatReturnsDialog = new DispatcherOperationCallback(delegate(object ignored) + { + this.commandViewModel = (CommandViewModel)commandViewModelObj; + ShowCommandWindow showCommandWindow = new ShowCommandWindow(); + + this.commandViewModel.HelpNeeded += new EventHandler(this.CommandNeedsHelp); + showCommandWindow.DataContext = this.commandViewModel; + + this.SetupButtonEvents(showCommandWindow.Run, showCommandWindow.Copy, showCommandWindow.Cancel, passThrough); + this.SetupWindow(showCommandWindow); + + CommonHelper.SetStartingPositionAndSize( + showCommandWindow, + ShowCommandSettings.Default.ShowOneCommandTop, + ShowCommandSettings.Default.ShowOneCommandLeft, + windowWidth != 0.0 && windowWidth > showCommandWindow.MinWidth ? windowWidth : ShowCommandSettings.Default.ShowOneCommandWidth, + windowHeight != 0.0 && windowHeight > showCommandWindow.MinHeight ? windowHeight : ShowCommandSettings.Default.ShowOneCommandHeight, + showCommandWindow.Width, + showCommandWindow.Height, + ShowCommandSettings.Default.ShowOneCommandWindowMaximized); + + return showCommandWindow; + }); + + this.CallShowDialog(cmdlet); + } + + /// + /// Called when the module importation is done + /// + /// all modules currently imported + /// commands to be displayed + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ImportModuleDone(Dictionary importedModules, IEnumerable commands) + { + this.allModulesViewModel.WaitMessageDisplayed = false; + if (this.window != null) + { + this.window.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + this.allModulesViewModel = ShowCommandHelper.GetNewAllModulesViewModel( + this.allModulesViewModel, + importedModules, + commands, + this.selectedModuleNeedingImportModule, + this.parentModuleNeedingImportModule, + this.commandNeedingImportModule); + this.SetupViewModel(); + }), + string.Empty); + } + } + + /// + /// Called when the module importation has failed + /// + /// reason why the module importation failed + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ImportModuleFailed(Exception reason) + { + this.allModulesViewModel.WaitMessageDisplayed = false; + if (this.window != null) + { + this.window.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + string message = ShowCommandHelper.GetImportModuleFailedMessage( + this.commandNeedingImportModule, + this.parentModuleNeedingImportModule, + reason.Message); + MessageBox.Show(this.window, message, ShowCommandResources.ShowCommandError, MessageBoxButton.OK, MessageBoxImage.Error); + }), + string.Empty); + } + } + + /// + /// Called when the results or get-help are ready in order to display the help window for a command + /// + /// results of a get-help call + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void DisplayHelp(Collection getHelpResults) + { + if (this.window != null && getHelpResults != null && getHelpResults.Count > 0) + { + this.window.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + HelpWindow help = new HelpWindow(getHelpResults[0]); + help.Owner = this.window; + help.Show(); + }), + string.Empty); + } + } + + /// + /// Activates this.window + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ActivateWindow() + { + if (this.window != null) + { + ShowCommandHelper.ActivateWindow(this.window); + } + } + + /// + /// returns the script to execute if dialog has not been canceled + /// + /// the script to execute if dialog has not been canceled + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private string GetScript() + { + if (this.dialogCanceled) + { + return null; + } + + return this.InternalGetScript(); + } + #endregion private methods called using reflection from show-command + + #region instance private methods used only on this file + /// + /// Sets up window settings common between the two flavors of show-command + /// + /// the window being displayed + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from ShowAllModulesWindow and ShowCommandWindow which are called with reflection")] + private void SetupWindow(Window commandWindow) + { + this.window = commandWindow; + this.window.Closed += new EventHandler(this.Window_Closed); + this.window.Loaded += new RoutedEventHandler(this.Window_Loaded); + } + + /// + /// Handles the SelectedCommandInSelectedModuleNeedsImportModule event + /// + /// event sender + /// event arguments + private void CommandNeedsImportModule(object sender, ImportModuleEventArgs e) + { + this.commandNeedingImportModule = e.CommandName; + this.parentModuleNeedingImportModule = e.ParentModuleName; + this.selectedModuleNeedingImportModule = e.SelectedModuleName; + this.allModulesViewModel.WaitMessageDisplayed = true; + this.ImportModuleNeeded.Set(); + } + + /// + /// Handles the SelectedCommandInSelectedModuleNeedsHelp event + /// + /// event sender + /// event arguments + private void CommandNeedsHelp(object sender, HelpNeededEventArgs e) + { + this.commandNeedingHelp = e.CommandName; + this.HelpNeeded.Set(); + } + + /// + /// Called when the window is closed to set this.dialogCanceled + /// + /// event sender + /// event arguments + private void Window_Closed(object sender, EventArgs e) + { + if (this.hostWindow != null) + { + this.hostWindow.Focus(); + } + + this.window = null; + this.windowClosed.Set(); + } + + /// + /// Called when the window is loaded to set this.Window_Loaded + /// + /// event sender + /// event arguments + private void Window_Loaded(object sender, RoutedEventArgs e) + { + this.window.Loaded -= new RoutedEventHandler(this.Window_Loaded); + this.windowLoaded.Set(); + } + + /// + /// Sets up event listening on the buttons + /// + /// button to run command + /// button to copy command code + /// button to close window + /// true to change the text of Run to OK + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from methods called using reflection")] + private void SetupButtonEvents(Button run, Button copy, Button cancel, bool passThrough) + { + if (passThrough) + { + run.Content = ShowCommandResources.ActionButtons_Button_Ok; + } + + run.Click += new RoutedEventHandler(this.Buttons_RunClick); + copy.Click += new RoutedEventHandler(this.Buttons_CopyClick); + cancel.Click += new RoutedEventHandler(this.Buttons_CancelClick); + } + + /// + /// Sets up event listening for a new viewModel + /// + private void SetupViewModel() + { + this.allModulesViewModel.SelectedCommandInSelectedModuleNeedsHelp += new EventHandler(this.CommandNeedsHelp); + this.allModulesViewModel.SelectedCommandInSelectedModuleNeedsImportModule += new EventHandler(this.CommandNeedsImportModule); + this.window.DataContext = this.allModulesViewModel; + } + + /// + /// Copies the script into the clipboard + /// + /// event sender + /// event arguments + private void Buttons_CopyClick(object sender, RoutedEventArgs e) + { + string script = this.InternalGetScript(); + if (script == null) + { + return; + } + + this.window.Dispatcher.Invoke(new ThreadStart(delegate { ShowCommandHelper.SetClipboardText(script); })); + } + + /// + /// Sets a succesfull dialog result and then closes the window + /// + /// event sender + /// event arguments + private void Buttons_RunClick(object sender, RoutedEventArgs e) + { + this.dialogCanceled = false; + this.CloseWindow(); + } + + /// + /// Closes the window. + /// + /// event sender + /// event arguments + private void Buttons_CancelClick(object sender, RoutedEventArgs e) + { + this.CloseWindow(); + } + + /// + /// closes the window. + /// + private void CloseWindow() + { + if (this.window == null) + { + return; + } + + this.window.Dispatcher.Invoke(new ThreadStart(delegate + { + // This can happen if ISE is closed while show-command is up + if (this.window != null) + { + this.window.Close(); + } + })); + } + + /// + /// Showing a MessageBox when user type a invalidate command name. + /// + /// Error message. + private void ShowErrorString(string errorString) + { + if (errorString != null && errorString.Trim().Length > 0) + { + MessageBox.Show( + string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.EndProcessingErrorMessage, + errorString), + "Show-Command", + MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + + /// + /// returns the script to execute + /// + /// the script to execute + private string InternalGetScript() + { + if (this.allModulesViewModel != null) + { + return this.allModulesViewModel.GetScript(); + } + + if (this.commandViewModel == null) + { + return null; + } + + return this.commandViewModel.GetScript(); + } + + /// + /// Implements IDisposable logic + /// + /// true if being called from Dispose + private void Dispose(bool isDisposing) + { + if (isDisposing) + { + this.windowClosed.Dispose(); + this.helpNeeded.Dispose(); + this.windowLoaded.Dispose(); + this.importModuleNeeded.Dispose(); + } + } + #endregion instance private methods used only on this file + } +} diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Add16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Add16.png new file mode 100644 index 00000000000..e5b964d9199 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Add16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/CloseTile16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/CloseTile16.png new file mode 100644 index 00000000000..bef254260eb Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/CloseTile16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Delete16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Delete16.png new file mode 100644 index 00000000000..1faef5d45ca Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Delete16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Error16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Error16.png new file mode 100644 index 00000000000..8fce7ba03df Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Error16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Help.ico b/src/Microsoft.Management.UI.Internal/resources/Graphics/Help.ico new file mode 100644 index 00000000000..f6b92d0b8c2 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Help.ico differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Rename16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Rename16.png new file mode 100644 index 00000000000..fa59ea35618 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Rename16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Save16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Save16.png new file mode 100644 index 00000000000..66893c82d66 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Save16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerAscending.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerAscending.png new file mode 100644 index 00000000000..a58856e553c Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerAscending.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerDescending.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerDescending.png new file mode 100644 index 00000000000..d95bc066e5b Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerDescending.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass10.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass10.png new file mode 100644 index 00000000000..71abc2ecf32 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass10.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass16.png new file mode 100644 index 00000000000..37cf86490d4 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/down.ico b/src/Microsoft.Management.UI.Internal/resources/Graphics/down.ico new file mode 100644 index 00000000000..98e0339426a Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/down.ico differ diff --git a/src/Microsoft.Management.UI.Internal/resources/public.GraphicalHostResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.GraphicalHostResources.resx new file mode 100644 index 00000000000..0a86d51a82e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.GraphicalHostResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OutGridViewWindow Object + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.HelpWindowResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.HelpWindowResources.resx new file mode 100644 index 00000000000..3bcab7bfb8a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.HelpWindowResources.resx @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + + Match Case + + + CommonParameters + Name of a group of parameters common to all cmdlets + + + Description + + + Examples + + + _Find: + + + Help Sections + + + {0} Help + + + Inputs + + + {0} {1} + + + Methods + + + _Next + + + No matches found + + + Notes + + + OK + + + 1 match + + + Outputs + + + Accept wildcard characters? + + + Default value + + + Accept pipeline input? + + + Position? + + + Required? + + + Parameters + + + _Previous + + + Properties + + + RelatedLinks + + + Remarks + + + Search Options + + + Settings + + + {0} matches + + + Synopsis + + + Syntax + + + Help for {0} + {0} is the name of a cmdlet + + + Whole Word + + + {0}% + + + Zoom + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.InvariantResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.InvariantResources.resx new file mode 100644 index 00000000000..62924692a43 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.InvariantResources.resx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + {0} cannot be modified directly, use {1} instead. + + + Columns + + + {0:G} + The format string that is used by the InnerList in the case where a DateTime type is used. The {0} will be the column value. + + + {0} + The format string that is used by the InnerList in the default case. The {0} will be the column value. + + + {0:N} + The format string that is used by the InnerList in the case where a floating point number is used. The {0} will be the column value. + + + {0:N0} + The format string that is used by the InnerList in the case where a whole number type is used. The {0} will be the column value. + + + {0} does not support adding to the Items collection, use {1} instead. + + + If View is set to a {0}, it should have the type {1}. + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.ShowCommandResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.ShowCommandResources.resx new file mode 100644 index 00000000000..5853a0a3c01 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.ShowCommandResources.resx @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + + Co_py + + + _Run + + + All + + + Modules: + + + ? + + + Help + + + Common Parameters + + + Errors + + + Parameters for "{0}": + + + Name: {0} +Module: {1} ({2}) + + + The following errors occurred running the command: +{0} + + + * + Used in MandatoryNameLabelFormat to designate a mandatory parameter with a * + + + {0}:{1} + This is a label for a control, hence the colon. {0} is a parameter name, {1} is MandatoryLabelSegment or an empty string + + + {0}: + This is a label for a control, hence the colon. {0} is a parameter name + + + Name: + + + OK + + + ... + + + Command Name + + + Modules + + + <No module name> + + + Select multiple values for "{0}" + + + Can receive value from pipeline + + + Common to all parameter sets + + + Mandatory + + + Optional + + + Position: {0} + + + Type: {0} + + + Imported + + + Not Imported + + + Show Details + + + Failed to import the module required by command "{0}". Module name: "{1}". Error message: "{2}". + + + To import the "{0}" module and its cmdlets, including "{1}", click {2}. + + + Show Command - Error + + + Please Wait... + + + Refresh + + + There are no parameters. + + + Click after using "{0}" to see the new commands + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.UICultureResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.UICultureResources.resx new file mode 100644 index 00000000000..32ae43c624c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.UICultureResources.resx @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Select Columns... + + + (none) + The group title for items within a column whose value is empty/null. + + + Value should be of Type {0}. + This text represents the error which will be shown when the entered type does not match the type we are expecting. {0} is the expected type. + + + The current selection is empty. + The error validation string to present to the user when they have selected a value out of bounds. + + + contains + A filter rule that indicates a field must contain the specified value. + + + does not contain + A filter rule that indicates a field must not contain the specified value. + + + does not equal + A filter rule that indicates a field must not equal the specified value. + + + equals + A filter rule that indicates a field must equal the specified value. + + + is greater than or equal to + A filter rule that indicates a field must be greater than or equal to the specified value. + + + is between + A filter rule that indicates a field must be between the specified values. + + + is empty + A filter rule that indicates a field must be empty. + + + is not empty + A filter rule that indicates a field must not be empty. + + + is less than or equal to + A filter rule that indicates a field must be less than or equal to the specified value. + + + ends with + A filter rule that indicates a field must end with the specified value. + + + starts with + A filter rule that indicates a field must start with the specified value. + + + Back + The text representing the tool tip and help text for the Back Button in the Back Forward History control when the button is disabled + + + Forward + The text representing the tool tip and help text for the Forward Button in the Back Forward History control when the button is disabled + + + The value must be a valid date in the following format: {0}. + {0} will be filled in with the culture appropriate ShortDatePattern + + + The value must be a valid number. + + + Search + The default background text of the search box. + + + LeftToRight + This value will be loaded at runtime to define the flow direction of WPF application. This value should be set to "RightToLeft" for mirrored language and "LeftToRight" for others. + + + + An ellipsis character. + + + Ctrl+Add + + + Ctrl+Shift+Add + + + Ctrl+Plus + + + Ctrl+Shift+Plus + + + Ctrl+Subtract + + + Ctrl+Shift+Subtract + + + Ctrl+Minus + + + Ctrl+Shift+Minus + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.XamlLocalizableResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.XamlLocalizableResources.resx new file mode 100644 index 00000000000..caff99962fd --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.XamlLocalizableResources.resx @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Available Columns + + + Add + + + Remove + + + Selected Columns + + + Back + Localizable AutomationName for control that is used by accessibility screen readers. + + + Forward + Localizable AutomationName for control that is used by accessibility screen readers. + + + Find in this column + Background text shown in the search box. + + + Expand + + + Name + + + New Query + + + Tasks + This is the title string for the Task Pane. + + + Tasks + AutomationProperties.Name of a SeparatedList. + + + Indeterminate Progress Icon + + + Add criteria + + + Overwrite the existing query or type a different name to save a new query. Each query consists of criteria, sorting, and column customizations. + + + Ok + + + Cancel + + + Click to save a search query + + + Available columns + + + >> + The contents of a button which indicates that items will move from the left column to right column + + + << + The contents of a button which indicates that items will move from the right column to left column + + + Move up + + + Move down + + + OK + + + Cancel + + + Select columns + + + Move selected column to list of visible columns + + + Move selected column to list of hidden columns + + + This column may not be removed. + + + The list must always display at least one column. + + + Selected columns + + + Find in this column + + + Expand + + + Click to clear all filter criteria. + + + Click to add search criteria. + + + Click to expand search criteria. + + + There are currently no saved queries. + + + Queries + + + Delete + + + Rename + + + {0} rule + The text representation of a rule in the filter panel, displayed to accessibility clients. {0} will be the name of the rule. + + + Add + + + Cancel + + + Add Filter Criteria + + + Value + The name for text input fields + + + <Empty> + + + Rules + The name of the panel which contains the filter rules + + + Delete + + + Query + + + Queries + + + Search + + + ({0} of {1}) + The text displayed in the management list title when the list has a filter applied. {0} will be the number of items shown in the list. {1} will be the total number of items in the list before filtering. + + + Searching... + The text displayed in the management list title when the list is processing a filter. + + + ({0}) + The text displayed in the management list title when the list does not have a filter applied. {0} will be the number of items shown in the list. + + + Filter + Localizable AutomationName for control that is used by accessibility screen readers. + + + Filter + + + Shortcut Rules + The name used to indicate custom filter rules which are specific to a particular application. + + + Columns Rules + The name used to indicate filter rules that are based upon the properties of the items in the list. + + + Sort Glyph + + + Sort Glyph + + + Collapse + + + Collapse + + + Sorted ascending + The text used for the accessible ItemStatus property when a column is sorted ascending. + + + Sorted descending + The text used for the accessible ItemStatus property when a column is sorted descending. + + + Collapse + + + Expand + + + Search + + + Cancel + + + Clear All + + + Clear All + + + Clear Search Text + + + Tasks + + + Search + The accessible name of the Search button in the filter panel. + + + Cancel + The accessible name of the Stop Search button in the filter panel. + + + Expand or Collapse Filter Panel + The accessible name of the button that expands/collapses the filter panel. + + + Filter + The background text of the list's search box when filtering is immediate. + + + Click to display saved search queries. + + + Filter applied. + + + and + The first header operator indicates that it is the first item in the list of filter rules. The AND value is used to indicate that it is and'ed with the above SearchBox. + + + and + The header operator indicates that it is the first item in a group of filter rules which are the same. The AND value is used to indicate that it is and'ed with the other groups in the panel. + + + or + The Item operator indicates that it is NOT the first item in a group of filter rules which are the same. The OR value is used to indicate that it is or'ed with the other items in the same group. + + + No matches found. + The text displayed in the ManagementList when the filter has been applied but matching items were found. + + + Collapse + + + Expand + + + Show Children + + + Show Children + + + {0}: {1} + The format string used for the ManagementList title when query has been applied. For example, "Users: My Fancy Query" + + + Cancel + + + OK + + diff --git a/src/Microsoft.Management.UI.Internal/themes/generic.xaml b/src/Microsoft.Management.UI.Internal/themes/generic.xaml new file mode 100644 index 00000000000..2a16a1d87cc --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/themes/generic.xaml @@ -0,0 +1,2790 @@ + + + + + M 0,0 L 4,3.5 L 0,7 Z + + M7.3391155,8.3084572 L7.3391708,16.109179 10.974259,12.431834 z + + + + + + + + + + + + + + + + + + M0.83647823,8.3277649 L5.9975096,11.519699 11.198949,8.3030383 + + M205.63696,8.9046901 L201.73368,12.456607 205.68294,16.093484 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj index 505b20f19fe..895a258df20 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj +++ b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj @@ -16,25 +16,9 @@ - - - - - - - - - - - - - - - - @@ -42,7 +26,6 @@ - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs index e1c26880df7..ae0d1c2c229 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs @@ -36,7 +36,7 @@ public enum OutputModeOption /// /// Implementation for the Out-GridView command. /// - [Cmdlet(VerbsData.Out, "GridView", DefaultParameterSetName = "PassThru", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113364")] + [Cmdlet(VerbsData.Out, "GridView", DefaultParameterSetName = "PassThru", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2109378")] public class OutGridViewCommand : PSCmdlet, IDisposable { #region Properties @@ -312,7 +312,7 @@ internal GridHeader(OutGridViewCommand parentCmd) internal static GridHeader ConstructGridHeader(PSObject input, OutGridViewCommand parentCmd) { if (DefaultScalarTypes.IsTypeInList(input.TypeNames) || - OutOfBandFormatViewManager.IsPropertyLessObject(input)) + !OutOfBandFormatViewManager.HasNonRemotingProperties(input)) { return new ScalarTypeHeader(parentCmd, input); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs index 3681de8c4a7..2e260c5953d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs @@ -18,7 +18,7 @@ namespace Microsoft.PowerShell.Commands /// /// Show-Command displays a GUI for a cmdlet, or for all cmdlets if no specific cmdlet is specified. /// - [Cmdlet(VerbsCommon.Show, "Command", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217448")] + [Cmdlet(VerbsCommon.Show, "Command", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2109589")] public class ShowCommandCommand : PSCmdlet, IDisposable { #region Private Fields @@ -156,7 +156,8 @@ public void RunScript(string script) return; } - if (!ConsoleInputWithNativeMethods.AddToConsoleInputBuffer(script, true)) + // Don't send newline at end as PSReadLine shows it rather than executing + if (!ConsoleInputWithNativeMethods.AddToConsoleInputBuffer(script, newLine: false)) { this.WriteDebug(FormatAndOut_out_gridview.CannotWriteToConsoleInputBuffer); this.RunScriptSilentlyAndWithErrorHookup(script); @@ -225,8 +226,8 @@ protected override void EndProcessing() return; } - // We wait untill the window is loaded and then activate it - // to work arround the console window gaining activation somewhere + // We wait until the window is loaded and then activate it + // to work around the console window gaining activation somewhere // in the end of ProcessRecord, which causes the keyboard focus // (and use oif tab key to focus controls) to go away from the window _showCommandProxy.WindowLoaded.WaitOne(); @@ -280,7 +281,7 @@ private void RunScriptSilentlyAndWithErrorHookup(string script) output.DataAdded += new EventHandler(this.Output_DataAdded); _errors.DataAdded += new EventHandler(this.Error_DataAdded); - PowerShell ps = PowerShell.Create(RunspaceMode.CurrentRunspace); + System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); ps.Streams.Error = _errors; ps.Commands.AddScript(script); diff --git a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 3e7e474b590..5ff7170c0b9 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -23,7 +23,8 @@ CmdletsToExport = @( 'ConvertFrom-StringData', 'Format-Table', 'New-TemporaryFile', 'New-TimeSpan', 'Get-TraceSource', 'Set-TraceSource', 'Add-Type', 'Get-TypeData', 'Remove-TypeData', 'Update-TypeData', 'Get-UICulture', 'Get-Unique', 'Get-Uptime', 'Clear-Variable', 'Get-Variable', 'New-Variable', 'Remove-Variable', 'Set-Variable', 'Get-Verb', 'Write-Verbose', - 'Write-Warning', 'Invoke-WebRequest', 'Format-Wide', 'ConvertTo-Xml', 'Select-Xml', 'Get-Error', 'Update-List' + 'Write-Warning', 'Invoke-WebRequest', 'Format-Wide', 'ConvertTo-Xml', 'Select-Xml', 'Get-Error', 'Update-List', + 'Out-GridView', 'Show-Command' ) FunctionsToExport = @() AliasesToExport = @('fhx') diff --git a/src/ResGen/Program.cs b/src/ResGen/Program.cs index 0a0f21aaa08..f07b992484d 100644 --- a/src/ResGen/Program.cs +++ b/src/ResGen/Program.cs @@ -47,8 +47,15 @@ public static void Main(string[] args) foreach (string resxPath in Directory.EnumerateFiles(resourcePath, fileFilter)) { + string accessModifier = "internal"; string className = Path.GetFileNameWithoutExtension(resxPath); - string sourceCode = GetStronglyTypeCsFileForResx(resxPath, moduleName, className); + if (className.StartsWith("public.", StringComparison.InvariantCultureIgnoreCase)) + { + accessModifier = "public"; + className = className.Substring(className.IndexOf(".") + 1); + } + + string sourceCode = GetStronglyTypeCsFileForResx(resxPath, moduleName, className, accessModifier); string outPath = Path.Combine(genFolder, className + ".cs"); Console.WriteLine("ResGen for " + outPath); File.WriteAllText(outPath, sourceCode); @@ -57,14 +64,13 @@ public static void Main(string[] args) } } - private static string GetStronglyTypeCsFileForResx(string xmlPath, string moduleName, string className) + private static string GetStronglyTypeCsFileForResx(string xmlPath, string moduleName, string className, string accessModifier) { // Example // // className = Full.Name.Of.The.ClassFoo // shortClassName = ClassFoo // namespaceName = Full.Name.Of.The - string shortClassName = className; string namespaceName = null; int lastIndexOfDot = className.LastIndexOf('.'); @@ -80,10 +86,10 @@ private static string GetStronglyTypeCsFileForResx(string xmlPath, string module { string value = data.Value.Replace("\n", "\n ///"); string name = data.Attribute("name").Value.Replace(' ', '_'); - entries.AppendFormat(ENTRY, name, value); + entries.AppendFormat(ENTRY, name, value, accessModifier); } - string bodyCode = string.Format(BODY, shortClassName, moduleName, entries.ToString(), className); + string bodyCode = string.Format(BODY, shortClassName, moduleName, entries.ToString(), className, accessModifier, accessModifier.Equals("public", StringComparison.InvariantCultureIgnoreCase) ? "public." : string.Empty); if (namespaceName != null) { bodyCode = string.Format(NAMESPACE, namespaceName, bodyCode); @@ -112,6 +118,7 @@ namespace {0} {{ {1} }} "; + private static readonly string BODY = @" using System; using System.Reflection; @@ -123,24 +130,25 @@ namespace {0} {{ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] -internal class {0} {{ +{4} class {0} {{ private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; + /// constructor [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(""Microsoft.Performance"", ""CA1811:AvoidUncalledPrivateCode"")] - internal {0}() {{ + {4} {0}() {{ }} /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager {{ + {4} static global::System.Resources.ResourceManager ResourceManager {{ get {{ if (object.ReferenceEquals(resourceMan, null)) {{ - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(""{1}.resources.{3}"", typeof({0}).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(""{1}.resources.{5}{3}"", typeof({0}).Assembly); resourceMan = temp; }} @@ -153,7 +161,7 @@ internal class {0} {{ /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture {{ + {4} static global::System.Globalization.CultureInfo Culture {{ get {{ return resourceCulture; }} @@ -171,12 +179,11 @@ internal class {0} {{ /// /// Looks up a localized string similar to {1} /// - internal static string {0} {{ + {2} static string {0} {{ get {{ return ResourceManager.GetString(""{0}"", resourceCulture); }} }} "; - } } diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs index c01ce96a4c4..49425d7854d 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs @@ -476,7 +476,7 @@ private static bool IsNotRemotingProperty(string name) private static readonly MemberNamePredicate NameIsNotRemotingProperty = IsNotRemotingProperty; - private static bool HasNonRemotingProperties(PSObject so) => so.GetFirstPropertyOrDefault(NameIsNotRemotingProperty) != null; + internal static bool HasNonRemotingProperties(PSObject so) => so.GetFirstPropertyOrDefault(NameIsNotRemotingProperty) != null; internal static FormatEntryData GenerateOutOfBandData(TerminatingErrorContext errorContext, PSPropertyExpressionFactory expressionFactory, TypeInfoDataBase db, PSObject so, int enumerationLimit, bool useToStringFallback, out List errors) diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 2d75ce3db68..7788ff1f39f 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -109,7 +109,6 @@ - diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index f4a95c82910..dfc98ed6f83 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4599,6 +4599,8 @@ internal static SessionStateAliasEntry[] BuiltInAliases new SessionStateAliasEntry("rmdir", "Remove-Item"), new SessionStateAliasEntry("cnsn", "Connect-PSSession", string.Empty, ReadOnly), new SessionStateAliasEntry("dnsn", "Disconnect-PSSession", string.Empty, ReadOnly), + new SessionStateAliasEntry("ogv", "Out-GridView", string.Empty, ReadOnly), + new SessionStateAliasEntry("shcm", "Show-Command", string.Empty, ReadOnly), #endif // Bash built-ins we purposefully keep even if they override native commands new SessionStateAliasEntry("cd", "Set-Location", string.Empty, AllScope), @@ -4614,12 +4616,10 @@ internal static SessionStateAliasEntry[] BuiltInAliases #if !CORECLR new SessionStateAliasEntry("gwmi", "Get-WmiObject", string.Empty, ReadOnly), new SessionStateAliasEntry("iwmi", "Invoke-WMIMethod", string.Empty, ReadOnly), - new SessionStateAliasEntry("ogv", "Out-GridView", string.Empty, ReadOnly), new SessionStateAliasEntry("ise", "powershell_ise.exe", string.Empty, ReadOnly), new SessionStateAliasEntry("rwmi", "Remove-WMIObject", string.Empty, ReadOnly), new SessionStateAliasEntry("sc", "Set-Content", string.Empty, ReadOnly), new SessionStateAliasEntry("swmi", "Set-WMIInstance", string.Empty, ReadOnly), - new SessionStateAliasEntry("shcm", "Show-Command", string.Empty, ReadOnly), new SessionStateAliasEntry("trcm", "Trace-Command", string.Empty, ReadOnly), new SessionStateAliasEntry("lp", "Out-Printer"), #endif diff --git a/src/System.Management.Automation/help/HelpCommands.cs b/src/System.Management.Automation/help/HelpCommands.cs index 838676f41ce..c666cae6402 100644 --- a/src/System.Management.Automation/help/HelpCommands.cs +++ b/src/System.Management.Automation/help/HelpCommands.cs @@ -202,6 +202,32 @@ public SwitchParameter Online private bool _showOnlineHelp; +#if !UNIX + private GraphicalHostReflectionWrapper graphicalHostReflectionWrapper; + private bool showWindow; + + /// + /// Gets or sets a value indicating whether the help should be displayed in a separate window. + /// + [Parameter(ParameterSetName = "ShowWindow", Mandatory = true)] + public SwitchParameter ShowWindow + { + get + { + return showWindow; + } + + set + { + showWindow = value; + if (showWindow) + { + VerifyParameterForbiddenInRemoteRunspace(this, "ShowWindow"); + } + } + } +#endif + // The following variable controls the view. private HelpView _viewTokenToAdd = HelpView.Default; @@ -243,6 +269,12 @@ protected override void ProcessRecord() HelpSystem helpSystem = this.Context.HelpSystem; try { +#if !UNIX + if (this.ShowWindow) + { + this.graphicalHostReflectionWrapper = GraphicalHostReflectionWrapper.GetGraphicalHostReflectionWrapper(this, "Microsoft.PowerShell.Commands.Internal.HelpWindowHelper"); + } +#endif helpSystem.OnProgress += new HelpSystem.HelpProgressHandler(HelpSystem_OnProgress); bool failed = false; @@ -559,6 +591,12 @@ private void WriteObjectsOrShowOnlineHelp(HelpInfo helpInfo, bool showFullHelp) throw PSTraceSource.NewInvalidOperationException(HelpErrors.NoURIFound); } } +#if !UNIX + else if (showFullHelp && ShowWindow) + { + graphicalHostReflectionWrapper.CallStaticMethod("ShowHelpWindow", helpInfo.FullHelp, this); + } +#endif else { // show inline help diff --git a/src/powershell-win-core/powershell-win-core.csproj b/src/powershell-win-core/powershell-win-core.csproj index ac1486d91fc..8da925b560c 100644 --- a/src/powershell-win-core/powershell-win-core.csproj +++ b/src/powershell-win-core/powershell-win-core.csproj @@ -50,6 +50,7 @@ + diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index 33dbdafa9fd..aea7462062d 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -125,7 +125,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Alias", "nsn", "New-PSSession", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "" "Alias", "nv", "New-Variable", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "nwsn", "New-PSWorkflowSession", $($FullCLR ), "ReadOnly", "", "" -"Alias", "ogv", "Out-GridView", $($FullCLR ), "ReadOnly", "", "" +"Alias", "ogv", "Out-GridView", $($FullCLR -or $CoreWindows ), "ReadOnly", "", "" "Alias", "oh", "Out-Host", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "popd", "Pop-Location", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "AllScope", "" "Alias", "ps", "Get-Process", $($FullCLR -or $CoreWindows ), "", "", "" @@ -161,7 +161,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Alias", "scb", "Set-Clipboard", $($FullCLR ), "ReadOnly", "", "" "Alias", "select", "Select-Object", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "AllScope", "" "Alias", "set", "Set-Variable", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "" -"Alias", "shcm", "Show-Command", $($FullCLR ), "ReadOnly", "", "" +"Alias", "shcm", "Show-Command", $($FullCLR -or $CoreWindows ), "ReadOnly", "", "" "Alias", "si", "Set-Item", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "sl", "Set-Location", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", "" "Alias", "sleep", "Start-Sleep", $($FullCLR -or $CoreWindows ), "ReadOnly", "", "" @@ -368,7 +368,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "New-WSManSessionOption", "", $($FullCLR -or $CoreWindows ), "", "", "None" "Cmdlet", "Out-Default", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "Out-File", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" -"Cmdlet", "Out-GridView", "", $($FullCLR ), "", "", "" +"Cmdlet", "Out-GridView", "", $($FullCLR -or $CoreWindows ), "", "", "None" "Cmdlet", "Out-Host", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "Out-LineOutput", "", $($FullCLR ), "", "", "" "Cmdlet", "Out-Null", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" @@ -439,7 +439,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Set-WmiInstance", "", $($FullCLR ), "", "", "" "Cmdlet", "Set-WSManInstance", "", $($FullCLR -or $CoreWindows ), "", "", "None" "Cmdlet", "Set-WSManQuickConfig", "", $($FullCLR -or $CoreWindows ), "", "", "None" -"Cmdlet", "Show-Command", "", $($FullCLR ), "", "", "" +"Cmdlet", "Show-Command", "", $($FullCLR -or $CoreWindows ), "", "", "None" "Cmdlet", "Show-ControlPanelItem", "", $($FullCLR ), "", "", "" "Cmdlet", "Show-EventLog", "", $($FullCLR ), "", "", "" "Cmdlet", "Show-Markdown", "", $( $CoreWindows -or $CoreUnix), "", "", "None" diff --git a/test/powershell/engine/Help/assets/HelpURI/V2Cmdlets.csv b/test/powershell/engine/Help/assets/HelpURI/V2Cmdlets.csv index 7545e9a7e64..34a4c8de544 100644 --- a/test/powershell/engine/Help/assets/HelpURI/V2Cmdlets.csv +++ b/test/powershell/engine/Help/assets/HelpURI/V2Cmdlets.csv @@ -143,7 +143,7 @@ New-WSManSessionOption,https://go.microsoft.com/fwlink/?LinkID=141449 Object Pipeline,https://go.microsoft.com/fwlink/?LinkID=135239 Out-Default,https://go.microsoft.com/fwlink/?LinkID=113362 Out-File,https://go.microsoft.com/fwlink/?LinkID=113363 -Out-GridView,https://go.microsoft.com/fwlink/?LinkID=113364 +Out-GridView,https://go.microsoft.com/fwlink/?LinkID=2109378 Out-Host,https://go.microsoft.com/fwlink/?LinkID=113365 Out-Null,https://go.microsoft.com/fwlink/?LinkID=113366 Out-Printer,https://go.microsoft.com/fwlink/?LinkID=113367 diff --git a/test/powershell/engine/Help/assets/HelpURI/V3Cmdlets.csv b/test/powershell/engine/Help/assets/HelpURI/V3Cmdlets.csv index 4253a8c6e04..b42894f83be 100644 --- a/test/powershell/engine/Help/assets/HelpURI/V3Cmdlets.csv +++ b/test/powershell/engine/Help/assets/HelpURI/V3Cmdlets.csv @@ -42,7 +42,7 @@ Set-ContentFileSystem,https://go.microsoft.com/fwlink/?LinkID=204570 Set-JobTrigger,https://go.microsoft.com/fwlink/?LinkID=223916 Set-ScheduledJob,https://go.microsoft.com/fwlink/?LinkID=223924 Set-ScheduledJobOption,https://go.microsoft.com/fwlink/?LinkID=223921 -Show-Command,https://go.microsoft.com/fwlink/?LinkID=217448 +Show-Command,https://go.microsoft.com/fwlink/?LinkID=2109589 Show-ControlPanelItem,https://go.microsoft.com/fwlink/?LinkID=219983 Suspend-Job,https://go.microsoft.com/fwlink/?LinkID=210613 Test-PSSessionConfigurationFile,https://go.microsoft.com/fwlink/?LinkID=217039