vsintegration/src/FSharp.ProjectSystem.PropertyPages/PropertyPages/ApplicationPropPageBase.vb (316 lines of code) (raw):
' Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
Imports EnvDTE
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports VSLangProj80
Imports VslangProj90
Imports Microsoft.VisualStudio.Editors
Imports Microsoft.VisualStudio.Editors.PropertyPages
Namespace Microsoft.VisualStudio.Editors.PropertyPages
''' <summary>
''' Contains functionality common to the application prop pages of VB, C# and J#
''' See comments in proppage.vb: "Application property pages (VB, C#, J#)"
''' </summary>
''' <remarks></remarks>
'''
Friend Class ApplicationPropPageBase
Inherits PropPageUserControlBase
Private m_LastIconImage As String
Protected m_DefaultIconText As String
Protected m_DefaultManifestText As String
Protected m_NoManifestText As String
Protected m_DefaultIcon As Icon
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.SuspendLayout()
Me.ResumeLayout(False)
End Sub
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
'InitializeComponent()
m_DefaultIconText = SR.GetString(SR.PPG_Application_DefaultIconText)
m_DefaultManifestText = SR.GetString(SR.PPG_Application_DefaultManifestText)
m_NoManifestText = SR.GetString(SR.PPG_Application_NoManifestText)
m_DefaultIcon = System.Drawing.SystemIcons.Application
End Sub
#Region "Application icon support"
''' <summary>
''' Retrieves the last set value for the Icon (as a path)
''' </summary>
''' <value></value>
''' <remarks></remarks>
Protected ReadOnly Property LastIconImage() As String
Get
Return m_LastIconImage
End Get
End Property
''' <summary>
''' Obtains the icon path from the textbox
''' </summary>
''' <param name="control"></param>
''' <param name="prop"></param>
''' <param name="value"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Function ApplicationIconGet(ByVal control As Control, ByVal prop As PropertyDescriptor, ByRef value As Object) As Boolean
Dim ApplicationIconCombobox As ComboBox = DirectCast(control, ComboBox)
Dim ApplicationIconText As String = Trim(CStr(ApplicationIconCombobox.SelectedItem))
'If no item selected, just use textbox value
If ApplicationIconText = "" Then
ApplicationIconText = Trim(ApplicationIconCombobox.Text)
End If
If IconEntryIsSpecial(ApplicationIconText) Then
ApplicationIconText = ""
End If
value = ApplicationIconText
Return True
End Function
''' <summary>
''' Populates the given application icon combobox with appropriate entries
''' </summary>
''' <param name="FindIconsInProject">If False, only the standard items are added (this is faster
''' and so may be appropriate for page initialization).</param>
''' <param name="ApplicationIconCombobox">The combobox that displays the list of icons</param>
''' <param name="CurrentIconValue">The current icon as a relative path.</param>
''' <remarks>
''' CurrentIconValue must be passed in because it's pulled from the control's current value, which is initially
''' set up by PropertyControlData), since clearing the list will clear the text value, too,
''' for a dropdown list.
''' </remarks>
Protected Overridable Sub PopulateIconList(ByVal FindIconsInProject As Boolean, ByVal ApplicationIconCombobox As ComboBox, ByVal CurrentIconValue As String)
Dim fInsideInitPrevious As Boolean = m_fInsideInit
m_fInsideInit = True
Try
ApplicationIconCombobox.Items.Clear()
ApplicationIconCombobox.Items.Add(Me.m_DefaultIconText)
If FindIconsInProject Then
For Each ProjectItem As EnvDTE.ProjectItem In DTEProject.ProjectItems
AddIconsFromProjectItem(ProjectItem, ApplicationIconCombobox)
Next
End If
If CurrentIconValue Is Nothing OrElse CurrentIconValue.Length = 0 Then
ApplicationIconCombobox.SelectedIndex = 0
Debug.Assert(IconEntryIsDefault(DirectCast(ApplicationIconCombobox.SelectedItem, String)), "First item should be the (Default Icon)")
Else
'Can't simply set SelectedItem, because it uses a case-sensitive comparison
For Each Item As String In ApplicationIconCombobox.Items
If Item.Equals(CurrentIconValue, StringComparison.OrdinalIgnoreCase) Then
ApplicationIconCombobox.SelectedItem = Item
Exit For
End If
Next
If ApplicationIconCombobox.SelectedItem Is Nothing Then
'CurrentIcon is not in the last - add it after the default icon
Debug.Assert(ApplicationIconCombobox.Items.Count >= 1, "Where's the default icon in the list?")
Debug.Assert(IconEntryIsDefault(DirectCast(ApplicationIconCombobox.Items(0), String)), "First item should be the (Default Icon)")
ApplicationIconCombobox.Items.Insert(1, CurrentIconValue)
ApplicationIconCombobox.SelectedItem = CurrentIconValue
End If
Debug.Assert(TryCast(ApplicationIconCombobox.SelectedItem, String).Equals(CurrentIconValue, StringComparison.OrdinalIgnoreCase))
End If
Finally
m_fInsideInit = fInsideInitPrevious
End Try
End Sub
''' <summary>
''' Adds an icon entry to the application icon combobox in its correct place
''' </summary>
''' <param name="ApplicationIconCombobox"></param>
''' <remarks></remarks>
Protected Overridable Sub AddIconEntryToCombobox(ByVal ApplicationIconCombobox As ComboBox, ByVal IconRelativePath As String)
'By default, add the icon to the end of the combobox's dropdown list
ApplicationIconCombobox.Items.Add(IconRelativePath)
End Sub
''' <summary>
''' Adds the given filename to the project.
''' </summary>
''' <param name="IconFileName"></param>
''' <remarks></remarks>
Private Function AddIconFileToProject(ByVal IconFileName As String) As ProjectItem
'Note: we allow the project to set the Build Action property for the icon to the default (content),
' which causes it to get deployed. That is the desired behavior.
Return AddFileToProject(DTEProject.ProjectItems, IconFileName, True)
End Function
''' <summary>
''' Allows the user to browse for an icon, and then adds it to the project and
''' to the given combobox.
''' </summary>
''' <param name="ApplicationIconCombobox">The combobox that displays the list of icons to choose from.</param>
''' <param name="ApplicationIconPictureBox">The picturebox that displays the image of the currently selected icon.</param>
''' <remarks></remarks>
Protected Sub BrowseForAppIcon(ByVal ApplicationIconCombobox As ComboBox, ByVal ApplicationIconPictureBox As PictureBox)
Dim sInitialDirectory As String
Dim sFileName As String
sInitialDirectory = Trim(ApplicationIconCombobox.Text)
If sInitialDirectory = "" OrElse IconEntryIsSpecial(sInitialDirectory) Then
sFileName = ""
sInitialDirectory = ""
Else
sFileName = System.IO.Path.GetFileName(sInitialDirectory)
sInitialDirectory = System.IO.Path.GetDirectoryName(sInitialDirectory)
End If
Dim fileNames As ArrayList = Common.Utils.GetFilesViaBrowse(ServiceProvider, Me.Handle, sInitialDirectory, SR.GetString(SR.PPG_AddExistingFilesTitle), _
Common.CreateDialogFilter(SR.GetString(SR.PPG_AddIconFilesFilter), ".ico"), _
0, False, sFileName)
If fileNames IsNot Nothing AndAlso fileNames.Count = 1 Then
sFileName = CStr(fileNames(0))
If System.IO.File.Exists(sFileName) Then
'Verify it's actually a usable .ico before adding it to the project
Dim ValidIcon As Boolean = False
Try
Dim Icon As New System.Drawing.Icon(sFileName)
ValidIcon = True
Icon.Dispose()
Catch ex As ArgumentException
ShowErrorMessage(SR.GetString(SR.PPG_Application_BadIcon_1Arg, sFileName))
Catch ex As Exception
Common.RethrowIfUnrecoverable(ex)
ShowErrorMessage(ex)
End Try
If Not ValidIcon Then
'Restore the previous setting (this is important because it might be the
' special <browse> item in VB, etc.).
ApplicationIconCombobox.SelectedItem = m_LastIconImage
Return
End If
Dim ProjectItem As EnvDTE.ProjectItem = Nothing
Try
ProjectItem = AddIconFileToProject(sFileName)
Catch ex As Exception
Common.RethrowIfUnrecoverable(ex)
ShowErrorMessage(SR.GetString(SR.PPG_Application_CantAddIcon), ex)
End Try
If ProjectItem Is Nothing Then
'Could not copy
ApplicationIconCombobox.SelectedItem = m_LastIconImage
Else
Dim sRelativePath As String = GetProjectRelativeFilePath(ProjectItem.FileNames(1))
'Find the item in the list and select it
ApplicationIconCombobox.SelectedIndex = -1
For Index As Integer = 0 To ApplicationIconCombobox.Items.Count - 1
Dim ItemPath As String = DirectCast(ApplicationIconCombobox.Items.Item(Index), String)
If ItemPath.Equals(sRelativePath, StringComparison.OrdinalIgnoreCase) Then
ApplicationIconCombobox.SelectedIndex = Index
Exit For
End If
Next
If ApplicationIconCombobox.SelectedIndex = -1 Then
'Icon is not in the list, so add it to the list
'Now get the new path of copied file
sRelativePath = GetProjectRelativeFilePath(ProjectItem.FileNames(1))
AddIconEntryToCombobox(ApplicationIconCombobox, sRelativePath)
ApplicationIconCombobox.SelectedItem = sRelativePath
End If
End If
UpdateIconImage(ApplicationIconCombobox, ApplicationIconPictureBox, True)
SetDirty(VsProjPropId.VBPROJPROPID_ApplicationIcon, True)
Else
Debug.Fail("File returned from browse dialog doesn't exist")
End If
Else
'Restore the previous setting
ApplicationIconCombobox.SelectedItem = m_LastIconImage
End If
End Sub
''' <summary>
''' Update the image displayed for the currently-selected application icon
''' </summary>
''' <param name="ApplicationIconCombobox">The combobox that displays the list of icons to choose from.</param>
''' <param name="ApplicationIconPictureBox">The picturebox that displays the image of the currently selected icon.</param>
''' <remarks></remarks>
Protected Sub UpdateIconImage(ByVal ApplicationIconCombobox As ComboBox, ByVal ApplicationIconPictureBox As PictureBox, ByVal AddToProject As Boolean)
If ApplicationIconCombobox.Enabled Then
Dim ApplicationIconText As String = Trim(CStr(ApplicationIconCombobox.SelectedItem))
If ApplicationIconText = "" Then
'If combobox item not selected, just get user typed text
ApplicationIconText = Trim(ApplicationIconCombobox.Text)
End If
If Not SetIconImagePath(ApplicationIconText, ApplicationIconCombobox, ApplicationIconPictureBox, AddToProject) Then
'Path did not exist, revert to previous
If Not SetIconImagePath(m_LastIconImage, ApplicationIconCombobox, ApplicationIconPictureBox, AddToProject) Then
'Still failed
If Not SetIconImagePath(m_DefaultIconText, ApplicationIconCombobox, ApplicationIconPictureBox, AddToProject) Then
Debug.Fail("should never happen")
End If
End If
End If
Else
'The icon combobox is disabled (probably wrong project type or output type), so
' just show a blank entry in the icon picturebox
ApplicationIconPictureBox.Image = Nothing
'clear the last icon cache so the next call
'to SetIconImagePath will not exit early if the path is equal
m_LastIconImage = ""
End If
End Sub
Private Function SetIconImagePath(ByVal path As String, ByVal ApplicationIconCombobox As ComboBox, ByVal ApplicationIconPictureBox As PictureBox, ByVal AddToProject As Boolean) As Boolean
If path IsNot Nothing AndAlso path.Equals(m_LastIconImage, StringComparison.Ordinal) Then
'PERF: Nothing to do if nothing has changed
Return True
End If
'Check for a valid path
If IconEntryIsSpecial(path) OrElse path = "" Then
m_LastIconImage = m_DefaultIconText
ApplicationIconPictureBox.Image = IconToImage(m_DefaultIcon, ApplicationIconPictureBox.ClientSize)
ApplicationIconCombobox.SelectedItem = m_DefaultIconText
Return True
End If
' Verify all the characters in the path are valid
If path.IndexOfAny(IO.Path.GetInvalidPathChars()) >= 0 Then
ShowErrorMessage(SR.GetString(SR.PPG_Application_CantAddIcon))
Return False
End If
If Not IO.Path.IsPathRooted(path) Then
path = IO.Path.Combine(GetProjectPath(), path)
End If
If System.IO.File.Exists(path) Then
'System.Drawing.Image will hold on to any file that we give it, so that it can
' be lazy about getting the bits out of it. This means the file will be locked,
' which we don't want. Make a copy of the file as a memory stream and use that to
' create the Image.
Try
Dim IconContents As Byte() = IO.File.ReadAllBytes(path)
Dim IconStream As New IO.MemoryStream(IconContents, 0, IconContents.Length)
ApplicationIconPictureBox.Image = IconToImage(New Icon(IconStream), ApplicationIconPictureBox.ClientSize)
Catch ex As Exception
Common.RethrowIfUnrecoverable(ex, True)
'This could mean a bad icon file, I/O problems, etc. At any rate, it doesn't make sense to
' display an error message (doesn't necessarily mean the user just selected it, it might have
' been in the project file), so we'll just show a blank image
ApplicationIconPictureBox.Image = New Bitmap(1, 1)
End Try
Dim sRelativePath As String = GetProjectRelativeFilePath(path)
'Find the item in the list and select it
If ApplicationIconCombobox.Items.Count = 0 Then
'The combobox has not been filled with any entries yet (there should at least be the <default> entry).
' We must do that before we continue, but don't bother adding icons from the project, that would
' take too long
PopulateIconList(False, ApplicationIconCombobox, sRelativePath)
End If
For Index As Integer = 0 To ApplicationIconCombobox.Items.Count - 1
Dim ItemPath As String = DirectCast(ApplicationIconCombobox.Items.Item(Index), String)
If ItemPath.Equals(sRelativePath, StringComparison.OrdinalIgnoreCase) Then
ApplicationIconCombobox.SelectedIndex = Index
Exit For
End If
Next
If ApplicationIconCombobox.SelectedIndex = -1 Then
'Icon is not in the project, so add it, if requested
If AddToProject Then
Dim ProjectItem As EnvDTE.ProjectItem = Nothing
Try
ProjectItem = AddIconFileToProject(path)
Catch ex As Exception
Common.RethrowIfUnrecoverable(ex)
ShowErrorMessage(SR.GetString(SR.PPG_Application_CantAddIcon), ex)
Return False
End Try
'Now get the new path of copied file
sRelativePath = GetProjectRelativeFilePath(ProjectItem.FileNames(1))
End If
'Add it to the combobox
ApplicationIconCombobox.Items.Add(sRelativePath)
ApplicationIconCombobox.SelectedItem = sRelativePath
End If
m_LastIconImage = sRelativePath
Return True
End If
Return False
End Function
''' <summary>
''' Converts an icon to a bitmap of the correct size
''' </summary>
''' <param name="Icon">The icon to convert</param>
''' <param name="PictureBoxSize">The size into which the image needs to fit</param>
''' <returns></returns>
''' <remarks></remarks>
Private Function IconToImage(ByVal Icon As Icon, ByVal PictureBoxSize As Size) As Image
'If the icon is bigger than the PictureBox, we'll shrink it. Otherwise we leave it at its
' current size.
If Icon.Size.Width > PictureBoxSize.Width OrElse Icon.Size.Height > PictureBoxSize.Height Then
Return Icon.ToBitmap().GetThumbnailImage(PictureBoxSize.Width, PictureBoxSize.Height, Nothing, Nothing)
Else
Return Icon.ToBitmap()
End If
End Function
''' <summary>
''' Given a ProjectItem, adds that item to the icon combobox if it's an .ico file. Also
''' adds any .ico files underneath it recursively.
''' </summary>
''' <param name="ProjectItem"></param>
''' <remarks></remarks>
Protected Sub AddIconsFromProjectItem(ByVal ProjectItem As EnvDTE.ProjectItem, ByVal ApplicationIconCombobox As ComboBox)
For Index As Short = 1 To ProjectItem.FileCount
Dim FileName As String = ProjectItem.FileNames(Index)
Dim ext As String = System.IO.Path.GetExtension(FileName)
If ext.Equals(".ico", StringComparison.OrdinalIgnoreCase) Then
ApplicationIconCombobox.Items.Add(GetProjectRelativeFilePath(FileName))
End If
'Recurse into folders (we definitely want stuff in the Resources
' folder, for instance)
For Each Child As EnvDTE.ProjectItem In ProjectItem.ProjectItems
AddIconsFromProjectItem(Child, ApplicationIconCombobox)
Next
Next
End Sub
''' <summary>
''' Returns true if the text is the special "Browse" text for the icon combobox
''' </summary>
''' <param name="EntryText"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Overridable Function IconEntryIsBrowse(ByVal EntryText As String) As Boolean
Return False
End Function
''' <summary>
''' Returns true if the text is the special "(Default Icon)" text for the icon combobox
''' </summary>
''' <param name="EntryText"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Overridable Function IconEntryIsDefault(ByVal EntryText As String) As Boolean
Return EntryText IsNot Nothing AndAlso EntryText.Equals(m_DefaultIconText, StringComparison.OrdinalIgnoreCase)
End Function
''' <summary>
''' Returns true if the text is a special value (like Browse or Default)
''' </summary>
''' <param name="EntryText"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Function IconEntryIsSpecial(ByVal EntryText As String) As Boolean
Return IconEntryIsBrowse(EntryText) OrElse IconEntryIsDefault(EntryText)
End Function
#End Region
#Region "Application Manifest Support"
Protected Function ApplicationManifestSupported() As Boolean
Return Not GetPropertyControlData(VsProjPropId90.VBPROJPROPID_ApplicationManifest).IsMissing
End Function
''' <summary>
''' Populates the given application manifest combobox with appropriate entries
''' </summary>
''' <param name="FindManifestInProject">If False, only the standard items are added (this is faster
''' and so may be appropriate for page initialization).</param>
''' <param name="ApplicationManifestCombobox">The combobox that displays the list of manifests</param>
''' <param name="CurrentManifestValue">The current manifest as a relative path.</param>
''' <remarks>
''' CurrentManifestValue must be passed in because it's pulled from the control's current value, which is initially
''' set up by PropertyControlData), since clearing the list will clear the text value, too,
''' for a dropdown list.
''' </remarks>
Protected Overridable Sub PopulateManifestList(ByVal FindManifestInProject As Boolean, ByVal ApplicationManifestCombobox As ComboBox, ByVal CurrentManifestValue As String)
Dim fInsideInitPrevious As Boolean = m_fInsideInit
m_fInsideInit = True
Try
ApplicationManifestCombobox.Items.Clear()
ApplicationManifestCombobox.Items.Add(Me.m_DefaultManifestText)
ApplicationManifestCombobox.Items.Add(Me.m_NoManifestText)
If FindManifestInProject Then
For Each ProjectItem As EnvDTE.ProjectItem In DTEProject.ProjectItems
AddManifestsFromProjectItem(ProjectItem, ApplicationManifestCombobox)
Next
End If
If String.IsNullOrEmpty(CurrentManifestValue) Then
ApplicationManifestCombobox.SelectedIndex = 0
ElseIf String.Equals(CurrentManifestValue, prjApplicationManifestValues.prjApplicationManifest_Default, StringComparison.OrdinalIgnoreCase) Then
ApplicationManifestCombobox.SelectedIndex = 0
ElseIf String.Equals(CurrentManifestValue, prjApplicationManifestValues.prjApplicationManifest_NoManifest, StringComparison.OrdinalIgnoreCase) Then
ApplicationManifestCombobox.SelectedIndex = 1
Else
'Can't simply set SelectedItem, because it uses a case-sensitive comparison
For Each Item As String In ApplicationManifestCombobox.Items
' Compare using oridinal ignore case as they are file paths.
If Item.Equals(CurrentManifestValue, StringComparison.OrdinalIgnoreCase) Then
ApplicationManifestCombobox.SelectedItem = Item
Exit For
End If
Next
If ApplicationManifestCombobox.SelectedItem Is Nothing Then
'CurrentIcon is not in the last - add it after the default manifest
Debug.Assert(ApplicationManifestCombobox.Items.Count >= 2, "Where are the default manifest values in the list?")
ApplicationManifestCombobox.Items.Insert(2, CurrentManifestValue)
ApplicationManifestCombobox.SelectedItem = CurrentManifestValue
End If
End If
Finally
m_fInsideInit = fInsideInitPrevious
End Try
End Sub
''' <summary>
''' Given a ProjectItem, adds that item to the manifest combobox if it's an .manifest file. Also
''' adds any .manifest files underneath it recursively.
''' </summary>
''' <param name="ProjectItem"></param>
''' <remarks></remarks>
Protected Sub AddManifestsFromProjectItem(ByVal ProjectItem As EnvDTE.ProjectItem, ByVal ApplicationManifestCombobox As ComboBox)
For Index As Short = 1 To ProjectItem.FileCount
Dim FileName As String = ProjectItem.FileNames(Index)
Dim ext As String = System.IO.Path.GetExtension(FileName)
If ext.Equals(".manifest", StringComparison.OrdinalIgnoreCase) Then
ApplicationManifestCombobox.Items.Add(GetProjectRelativeFilePath(FileName))
End If
'Recurse into folders (we definitely want stuff in the Resources
' folder, for instance)
For Each Child As EnvDTE.ProjectItem In ProjectItem.ProjectItems
AddManifestsFromProjectItem(Child, ApplicationManifestCombobox)
Next
Next
End Sub
#End Region
End Class
End Namespace