I assume this is a shared resource somewhere in Windows. Rather than making a copy for each app, is there a way to use this icon just like all Winforms apps use it?
How is this specified for Winforms apps by default? I don't see any reference of any icons in code or project settings. Just that it uses the "default icon".
It is stored as a resource in the System.Windows.Forms.dll assembly. You could get a copy with Reflector. Open the assembly, open the Resources node, all the way down to "wfc.ico". Right-click, Save As. Not sure why you'd want to use it, given that it is the default.
You set a custom icon for your application with Project + Properties, Application tab, Icon setting. Each form has its own Icon property.
If you have Visual Studio 2010 installed then there is a large collection of icons (potentially including the application icon/s), check out the following directory:
%ProgramFiles%\Microsoft Visual Studio 10.0\Common7\VS2010ImageLibrary\1033
There may be a similar directory for previous VS versions, take a look if needs be.
EDIT:
On doing a search in the folder of the unzipped file for app there are two notable results:
Application.ico and ApplicationGeneric.ico + its *.png counterpart.
If you have VS 2010 and any of the icons in here are suitable, I believe you don't need to copy a single one - you should be able to include the file indirectly (as a shared/linked file) when adding using the Existing Item... dialog; you do this by selecting the arrow next to Add button and selecting the Add As Link option.
What I can't see working as desired is simply overwriting these files in an attempt to apply a global change.
It is stored as a resource in the System.Windows.Forms.dll assembly. You could get a copy with reflection as folow:
public static class FormUtils
{
private static Icon _defaultFormIcon;
public static Icon DefaultFormIcon
{
get
{
if (_defaultFormIcon == null)
_defaultFormIcon = (Icon)typeof(Form).
GetProperty("DefaultIcon", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null, null);
return _defaultFormIcon;
}
}
public static void SetDefaultIcon()
{
var icon = Icon.ExtractAssociatedIcon(EntryAssemblyInfo.ExecutablePath);
typeof(Form)
.GetField("defaultIcon", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
.SetValue(null, icon);
}
}
public static class FormExtensions
{
internal static void GetIconIfDefault(this Form dest, Form source)
{
if (dest.Icon == FormUtils.DefaultFormIcon)
dest.Icon = source.Icon;
}
}
So as you can see in the code you have in this way the same Icon.Handle. The same reference.
Form.DefaultIcon is an internal lazy loaded static property in class Form.
You can also override the default Winforms icon for your application. In Program.cs i use:
FormUtils.SetDefaultIcon();
This function will then override the default icon with the icon specified in your Application properties, the icon of your executable.
You can just use the Save method:
C#:
string IcoFilename = "C:\\Junk\\Default.ico";
using (System.IO.FileStream fs = new System.IO.FileStream(IcoFilename, System.IO.FileMode.Create))
{
this.Icon.Save(fs);
}
Visual Basic:
Dim strFilename As String = "C:\Junk\Default.ico"
Using fs As New System.IO.FileStream(strFilename, IO.FileMode.Create)
Me.Icon.Save(fs)
End Using
I had a problem which was similar, but different. Rather than needing to get the default icon, I needed to check to see whether the icon on a form was set or if it was left as the default. While I could have used reflection to get it, I ended up using a simpler solution:
private static Icon defaultIcon = new Form().Icon;
// ...
if(this.Icon == defaultIcon)
{
// ...
}
Related
I am trying to create a Visual Studio extension that will open a document window containing a custom control of mine that hosts an editor pane. I can get the document to load, the correct content type loads with my custom content extensions (classifier, quick info), a window with my custom control showing the editor pane loads, but the text cannot be modified. I can select and highlight text, but no keys work, no commands, and no mouse input other than text selection.
The following is what I have in my Package class:
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[ProvideEditorExtension(typeof(MyCustomEditorFactory), ".cust")]
[Guid(MyCustomExtensionsPackage.PackageGuidString)]
public sealed class MyCustomExtensionsPackage : AsyncPackage
{
public const string PackageGuidString = "ec2c4646-d0cc-42c6-b0a6-d0ff3e318cef";
#region Package Members
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
RegisterEditorFactory(new MyCustomEditorFactory(this));
}
#endregion
}
I have verified that the RegisterEditorFactory method is called, and that my editor factory is called when files with the .cust extension are opened.
The following is my editor factory:
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Utilities;
using System;
using System.ComponentModel.Composition;
using System.Runtime.InteropServices;
[ComVisible(true)]
[Guid(EditorFactoryGuidString)]
public sealed class MyCustomEditorFactory : IVsEditorFactory
{
public const string EditorFactoryGuidString = "38AD95BA-8891-46A2-A5EA-25F5F36EEAE0";
private MyCustomExtensionsPackage _package;
private Microsoft.VisualStudio.OLE.Interop.IServiceProvider _vsServiceProvider;
[Import]
public IContentTypeRegistryService ContentTypeRegistry { get; set; }
[Import]
public ITextEditorFactoryService TextEditorFactory { get; set; }
public MyCustomEditorFactory(MyCustomExtensionsPackage package)
{
_package = package;
}
public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)
{
_vsServiceProvider = psp;
return (VSConstants.S_OK);
}
public int Close()
{
return (VSConstants.S_OK);
}
public int MapLogicalView(ref Guid rguidLogicalView, out string pbstrPhysicalView)
{
pbstrPhysicalView = null;
return (VSConstants.LOGVIEWID_Primary == rguidLogicalView ? VSConstants.S_OK : VSConstants.E_NOTIMPL);
}
public int CreateEditorInstance(uint grfCreateDoc, string pszMkDocument, string pszPhysicalView, IVsHierarchy pvHier, uint itemid, IntPtr punkDocDataExisting, out IntPtr ppunkDocView, out IntPtr ppunkDocData, out string pbstrEditorCaption, out Guid pguidCmdUI, out int pgrfCDW)
{
ThreadHelper.ThrowIfNotOnUIThread();
ppunkDocView = IntPtr.Zero;
ppunkDocData = IntPtr.Zero;
pbstrEditorCaption = string.Empty;
pguidCmdUI = VSConstants.GUID_TextEditorFactory;
pgrfCDW = 0;
int retVal = VSConstants.E_FAIL;
if ((grfCreateDoc & (VSConstants.CEF_OPENFILE | VSConstants.CEF_SILENT)) != 0)
{
IVsTextLines textBuffer = null;
if (punkDocDataExisting == IntPtr.Zero)
{
IComponentModel mef = _package.GetService<SComponentModel, IComponentModel>();
mef.DefaultCompositionService.SatisfyImportsOnce(this);
IVsEditorAdaptersFactoryService eafs = mef.GetService<IVsEditorAdaptersFactoryService>();
textBuffer = eafs.CreateVsTextBufferAdapter(_vsServiceProvider, ContentTypeRegistry.GetContentType("CUST")) as IVsTextLines;
string fileText = System.IO.File.ReadAllText(pszMkDocument);
textBuffer.InitializeContent(fileText, fileText.Length);
string[] roles = new string[]
{
PredefinedTextViewRoles.Analyzable,
PredefinedTextViewRoles.Editable,
PredefinedTextViewRoles.Interactive,
PredefinedTextViewRoles.Document,
PredefinedTextViewRoles.PrimaryDocument
};
IWpfTextView dataView = TextEditorFactory.CreateTextView(eafs.GetDataBuffer(textBuffer), TextEditorFactory.CreateTextViewRoleSet(roles));
dataView.Options.SetOptionValue(DefaultTextViewHostOptions.LineNumberMarginName, true);
dataView.Options.SetOptionValue(DefaultTextViewHostOptions.ShowCaretPositionOptionName, true);
dataView.Options.SetOptionValue(DefaultTextViewHostOptions.ChangeTrackingName, true);
dataView.Options.SetOptionValue(DefaultTextViewOptions.ViewProhibitUserInputName, false);
IWpfTextViewHost wpfHost = TextEditorFactory.CreateTextViewHost(dataView, false);
MyCustomEditor editor = new MyCustomEditor(wpfHost);
ppunkDocData = Marshal.GetIUnknownForObject(textBuffer);
ppunkDocView = Marshal.GetIUnknownForObject(editor);
retVal = VSConstants.S_OK;
}
else
{
//code for document already open
}
else
{
retVal = VSConstants.E_INVALIDARG;
}
}
return (retVal);
}
}
There's a lot to unpack there, but ultimately I'm just
Creating an IVsTextBuffer from an IVsEditorAdaptersFactoryService
Loading the contents of the text buffer
Creating an IWpfTextView with the IVsTextBuffer from a ITextEditorFactoryService
Creating an IWpfTextViewHost with the IWpfTextView from the same ITextEditorFactoryService
Creating my my custom window pane and passing in the IWpfTextViewHost
Returning the IVsTextBuffer as the document data and my custom window pane as the document view
My custom window pane code is simply:
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Text.Editor;
using System.Runtime.InteropServices;
[ComVisible(true)]
public sealed class MyCustomEditor : WindowPane
{
private IWpfTextViewHost _wpfHost;
public MyCustomEditor(IWpfTextViewHost wpfHost)
{
_wpfHost = wpfHost;
Content = new MyCustomEditorControl(wpfHost.HostControl);
}
}
I've omitted the code for the MyCustomEditorControl for the sake of brevity, but just assume it's a simple WPF UserControl with a ContentPresenter. The constructor just sets Content property of the ContentPresenter with the wpfHost.HostControl that's passed in.
All of this works insofar as displaying the contents of a file, but I just can't edit anything. It's like the input bindings aren't wired up or enabled, but I can't find what properties to set or enable on which objects, and the documentation is rather poor on this topic. I've gone through the Visual Studio extensions walkthroughs on MSDN, but it seems to be a mish-mash of pre-WPF and post-WPF APIs with no clear guidance on what the authoritative approach is.
Admittedly, I may be naive in assuming that the IWpfTextView would automatically handle user input, but in my defense it's created from an ITextEditorFactoryService and having "TextEditor" in the name carries certain implications.
I'm going to go through a rather exhaustive list of things I've tried and checked to hopefully help narrow down the possibilities or where my mistake is:
After the document window is open, I've checked the Running Document Table and there is an entry for it that is automatically created after CreateEditorInstance exits. It links to the data buffer that's returned, the correct file path, and has an edit lock but no read lock. I've inspected the RDT when C# files are opened in a normal editor window and those have read and write locks. I tried manually setting a read lock on the RDT as well, the counter increments, but that doesn't seem to make a difference.
I've inspected the return result from ITextBuffer.CheckEditAccess on the data buffer and it returns true so the text buffer is reporting it's editable.
The textview roles DO have an effect on the final rendered IWpfTextViewHost. If I add or omit roles, the appearance and functionality changes. For example, adding or removing "ZOOMABLE" adds or removes the dropdown box with the control zoom level. However, "EDITABLE" has no effect on editability.
The editor options DO have an effect. For example, adding or removing the line number margin options does show/hide the line numbers. However, ViewProhibitUserInputName has no effect on editability.
I don't create a code window with CreateVsCodeWindowAdapter because the resulting IWpfTextViewHost that can be retrieved from GetWpfTextViewHost is already parented to another control. If I disconnect it with (IWpfTextViewHost.HostControl.Parent as Border).Child = null, then the contents of IWpfTextViewHost.HostControl gets disposed of for some reason. The IWpfTextViewHost.HostControl loads and it displays a window with margins and scrollbars, but the content is blank and if I click into the left margin of the content pane then Visual Studio throws an ObjectDisposedException. Please note I'm not nulling out the host control itself or its contents, I'm only telling its existing parent it no longer has a child.
I've inspected the editor GUID property of the IVsWindowFrame that opens my document and it's set to the value returned in the pguidCmdUI parameter, which I believe is correct.
I'm not certain if the value being set for the pguidCmdUI parameter in the editor factory CreateEditorInstance is correct. I'm aware it's used for command routing, which might explain why I can't type or use the mouse, but I'm not sure what the correct value should be. I read somewhere it's supposed to be the GUID of the factory that created the editor, so I set it to the text editor factory GUID since that's what created the IWpfTextView. I tried setting it to my editor factory GUID at the top of the file, as well as typeof(IWpfTextView).GUID, Guid.Empty, and assorted other GUIDs, but there's no change.
If I pass my IWpfTextView into IVsEditorAdapterFactoryService.GetViewAdapter, I get back null.
If I create a IVsTextView from IVsEditorAdapterFactoryService.CreateVsTextViewAdapter, I can have both an IVsTextView and an IWpfTextView, but the two don't know about each other and I don't know see any way to map them. I don't even know if they need to be mapped, or if IVsTextView needs to exist or is just the legacy text view interface.
When I inspect the created IVsTextView, it's base type is a SimpleTextViewWindow that is an undocumented class that's internal to Microsoft's Visual Studio implementation, it has WpfTextView and WpfTextViewHost properties, but they're both null (or throw an exception that they can't be read) and are unsettable. Microsoft obviously has some internal voodoo magic to map IVsTextViews to WpfTextViews in their own code, but I don't know what it is.
I'm aware of other code samples that create an IVsInvisibleEditor and get the data buffer from that, but that seems kind of hacky. I mean, it technically works and no one seems to know of any alternative because the documentation in this area is so poor, so I'm not judging or criticizing, it just seems like that shouldn't be necessary. It's my (possibly incorrect) understanding that an invisible editor is a virtual editor that is created for documents that are opened in memory but not hosted in a window, but I'm hosting mine in a window. Additionally all of the working code samples I could find are all copy/pasting from the same Microsoft VSIX code example. In that example, the virtual editor is created in a tool window that's created under the assumption that a document is already open in an existing editor window. So that tool window is creating a virtual editor side-by-side to a physical one for a tool window. If I follow the same invisible editor approach, then I'm effectively creating a virtual editor in memory and then a second physical document editor window. So basically, I'm creating two editors for one document. That doesn't seem right. Again, it technically works, but it seems off.
If the expectation is that we have to implement IOleCommandTarget on our editor window and manually handle every single keystroke, shortcut, and mouse button and manipulate the underlying text buffer directly, then that is going to be the most depressing thing ever. That would mean they tout being able to use WPF, but then give us a document view without a WPF control that natively handles text editing even though they exist in the framework.
Sorry for the ridiculously long post, but I wanted to provide as much information as I possibly could about where I'm at and what I've tried.
Regarding CreateVsCodeWindowAdapter, see the "official" explanation how to make it work: https://developercommunity.visualstudio.com/t/projectionbuffertutorial-gives-error-in-dev16/498617
I'm using .NET Core 3 preview 6 and Visual Studio 2019 16.2 for creating WinForms application.
In .NET Framework I used type-safe mechanism to load resources, something like that:
this.pictureBox1.BackgroundImage = global::MyNamespace.Properties.Resources.Image1;
this.textBox1.Text = global::MyNamespace.Properties.Resources.Script1;
But in .NET Core 3 I must write special helper class with several methods:
public static class EmbeddedResource
{
public static Image GetImage(String resourceName)
{
try
{
using (var stream = typeof(EmbeddedResource).GetTypeInfo().Assembly.GetManifestResourceStream(resourceName))
return Image.FromStream(stream);
}
catch(Exception exception)
{
throw new Exception($"Failed to read Embedded Resource {resourceName}");
}
}
public static String GetString(String resourceName)
{
try
{
using (var stream = typeof(EmbeddedResource).GetTypeInfo().Assembly.GetManifestResourceStream(resourceName))
using (var reader = new StreamReader(stream, Encoding.UTF8))
return reader.ReadToEnd();
}
catch(Exception exception)
{
throw new Exception($"Failed to read Embedded Resource {resourceName}");
}
}
}
And use it like that:
this.pictureBox1.BackgroundImage = EmbeddedResource.GetImage("MyNamespace.Image1.jpg");
this.textBox1.Text = EmbeddedResource.GetString("MyNamespace.Script1.sql");
Is there a better way (e. g. strictly-typed and resourceName error-safe) to do that?
Thank you in advance.
Visual Studio 2019 16.2 Has design-time support for Resx file for Windows Forms .NET Core Projects. It is the same feature that is supported in previous versions of Visual Studio for Windows Forms classic .NET projects.
It means you can:
Add New Item → Choose Resources File and set a name like Resources.Resx and press Add. The file will be opened in design mode. (Later to open it in design mode, just double click on it.)
Add an image to the designer by dragging an image file and dropping it into the designer. You can also add the image by click on Add Resource tool strip drop down button and choosing Add Existing File ....
Then the image will be accessible using a property which has the same name as the image. For example I created a Properties folder and created Resources.Resx under that folder, then added MyImage.jpg to the resource file, the I could use it this way:
this.BackgroundImage = Properties.Resources.MyImage;
Note - Create default Resource file for the project in Properties folder
Right click on Project → Choose Properties
In project properties window, choose Resources (left side, bottom of the list).
At the center, you will see a link This project does not contain a default resources file. Click here to create one. Click on the link and it will create a Resources.Resx file under Properties folder for your project.
im an currently following this guide to add windows toast notifications to my app.
https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop
i need to create a shortcut that contains the System.AppUserModel.ID and System.AppUserModel.ToastActivatorCLSID.
now the website says to just have your installer create this and they recommend using WIX. which is fine but i would rather just create the shortcut from C# code.
so there is this example that creates the shortcut via C#.
https://code.msdn.microsoft.com/windowsdesktop/sending-toast-notifications-71e230a2
but it only shows adding the AppUserModel.ID and not the ToastActivatorCLSID...
here is some of that code...
private void InstallShortcut(String shortcutPath)
{
// Find the path to the current executable
String exePath = Process.GetCurrentProcess().MainModule.FileName;
IShellLinkW newShortcut = (IShellLinkW)new CShellLink();
// Create a shortcut to the exe
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetPath(exePath));
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetArguments(""));
// Open the shortcut property store, set the AppUserModelId property
IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut;
using (PropVariant appId = new PropVariant(APP_ID))
{
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(System Properties.System.AppUserModel.ID, appId));
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
}
// Commit the shortcut to disk
IPersistFile newShortcutSave = (IPersistFile)newShortcut;
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutSave.Save(shortcutPath, true));
}
the thing is that the AppUserModel does not have a ToastActivatorCLSID property. seems strange.
i figured i could just add another using block to add the ToastActivatorCLSID property like this
using (PropVariant clsId = new PropVariant(CLSID))
{
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(System Properties.System.AppUserModel.ToastActivatorCLSID, CLSID));
ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
}
but the SystemProperties.System.AppUserModel.ToastActivatorCLSID doesnt exist.
right now the appuser model is coming from Microsoft.WindowsAPICodePack.Shell.PropertySystem.
this page shows it should exist
https://learn.microsoft.com/en-us/windows/desktop/properties/props-system-appusermodel-toastactivatorclsid
i would if with that information i could make some kind of interface or something to add in that ToastActivatorCLSID.
there is very little on the internet on this topic. Don't know if there is a different reference or something.
any help would be great
I've been running into the same problem and I've found a work around.
Clone this github repo: https://github.com/aybe/Windows-API-Code-Pack-1.1
open the WindowsAPICodePack12.sln in visual studio.
open Shell > PropertySystem > SystemProperties.cs
find the AppUserModel class on line 2302
add this code to the class:
public static PropertyKey ToastActivatorCLSID
{
get
{
PropertyKey key = new PropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 26);
return key;
}
}
Build the project. Open the bin folder (where the built projects go) and find Microsoft.WindowsAPICodePack.dll and Microsoft.WindowsAPICodePack.Shell.dll
Reference these dlls in your project instead of the original code pack.
What I want:
I'm editing a WordprocessingDocument, and adding some tracked changes in it. This part is done. Now, I want MS word to show all revisions by default, i.e., it shouldn't require user to click on the red side bar to open the tracked changes in the document.
What I did: For this, I found a class RevisionView, which adds the xml element <w:revisionView /> in settings.xml under w:settings element. The RevisionView has some properties like Comments, DisplayRevision, Formatting etc. I explicitly set them all to true.
RevisionView revView = new RevisionView();
revView.DisplayRevision = new OnOffValue(true);
revView.Formatting = new OnOffValue(true);
revView.InkAnnotations = new OnOffValue(true);
revView.Markup = new OnOffValue(true);
revView.Comments = new OnOffValue(true);
and then I added this revView to the Settings:
Settings settings = wordprocessingDocument.MainDocumentPart.DocumentSettingsPart.Settings;
settings.RemoveAllChildren<RevisionView>();
settings.AppendChild(revView);
settings.Save();
And then I reviewed the document xml explicitly, it is adding the following xml in the settings:
<w:revisionView w:markup="true" w:comments="true" w:insDel="true" w:formatting="true" w:inkAnnotations="true" />
But adding this element in settings doesn't affect the view. It isn't showing the revisions opened by default.
Then, for testing purpose, I changed the zoom element in the settings.xml by hand from <w:zoom w:percent="100" /> to <w:zoom w:percent="120" />. What I expected was: word would change the zoom for this document from 100 to 120 now. But it didn't do that, the zoom was 100 even after changing to 120 in settings.xml.
One more thing: I can't use interop as I have to deploy this to a server, that's why I'm doing all this using OpenXmlSdk.
What I'm asking:
Is it even possible to do what i want?
If it is, then what am I doing wrong? Is RevisionView the option, on what should I rely?
Is there a way to force word to apply (override the default application level settings) the settings provided in settings.xml?
Why isn't word changing the zoom from 100 to 120?
Here are the answers to your questions:
Is it even possible to do what i want?
What you are trying to do is: Upon opening the docx file, have the Reviewing Pane be open automatically. I could not find a way to force the Word client to do this with OpenXml.
If it is, then what am I doing wrong? Is RevisionView the option, on what should I rely?
Its not possible, so the answer here is also no.
Is there a way to force word to apply (override the default application level settings) the settings provided in settings.xml?
Yes using the OpenXml SDK. The Settings Part has properties that you can control with code to alter the default Word client behavior.
Why isn't word changing the zoom from 100 to 120?
I can't answer this without seeing your file. Perhaps you did not save the file back correctly when you edited the file manually.
I was able to build a simple console app with the following code. The app will change the zoom level for any Word file to 120%. You need to add your file path to the code.
I generated most of this code using the OpenXml Productivity Tool..
Note - When building this in Visual Studio, don't forget to add DocumentFormat.OpenXml and WindowsBase to your project references.
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace ConsoleApp4
{
class Program
{
private static WordprocessingDocument document;
private static System.Collections.Generic.IDictionary<System.String, OpenXmlPart> UriPartDictionary = new System.Collections.Generic.Dictionary<System.String, OpenXmlPart>();
private static System.Collections.Generic.IDictionary<System.String, DataPart> UriNewDataPartDictionary = new System.Collections.Generic.Dictionary<System.String, DataPart>();
static void Main(string[] args)
{
using (document = WordprocessingDocument.Open("<DOCX FILE PATH HERE>", true))
{
BuildUriPartDictionary();
ChangeParts();
}
}
private static void BuildUriPartDictionary()
{
System.Collections.Generic.Queue<OpenXmlPartContainer> queue = new System.Collections.Generic.Queue<OpenXmlPartContainer>();
queue.Enqueue(document);
while (queue.Count > 0)
{
foreach (var part in queue.Dequeue().Parts)
{
if (!UriPartDictionary.Keys.Contains(part.OpenXmlPart.Uri.ToString()))
{
UriPartDictionary.Add(part.OpenXmlPart.Uri.ToString(), part.OpenXmlPart);
queue.Enqueue(part.OpenXmlPart);
}
}
}
}
private static void ChangeParts()
{
ChangeDocumentSettingsPart1(((DocumentSettingsPart)UriPartDictionary["/word/settings.xml"]));
}
private static void ChangeDocumentSettingsPart1(DocumentSettingsPart documentSettingsPart)
{
Settings settings1 = documentSettingsPart.Settings;
Zoom zoom1 = settings1.GetFirstChild<Zoom>();
zoom1.Percent = "120";
}
}
}
This is a cosmetic issue but my application has a default icon on the control panel. Many other applications have custom icons.
My application does have custom icons for the menu and task bar.
How can the icon displayed on the Control Panel be changed using Visual Studio 2015 or later?
Update:
There has been a change in how Visual Studio creates installers. I'm not sure when it occurred, but 2015 definitely does not have a "deployment project". The majority of the hits on Google suggest going to the deployment project properties which does not exist under VS 2015 apps.
This was why I included the tag for visual-studio-2015. Sorry, not to have mentioned that in the original question. It would have been good information.
Using the registry is a possibility but the registry path mentioned, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, does not exist. It does sound kludgy to have the application check it's own icon in the registry all the time. It sounds like an installer function to me.
A post on the Microsoft Developer Network provided an answer. It also modifies the registry. I enhanced it by removing hard-coded values for the application name and the icon file.
// These references are needed:
// using System.Reflection;
// using System.Deployment.Application;
// using System.IO;
// using Microsoft.Win32;
private static void SetAddRemoveProgramsIcon(string iconName)
{
// only run if deployed
if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed && ApplicationDeployment.CurrentDeployment.IsFirstRun)
{
try
{
string assemblyTitle="";
object[] titleAttributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), true);
if (titleAttributes.Length > 0 && titleAttributes[0] is AssemblyTitleAttribute)
{
assemblyTitle = (titleAttributes[0] as AssemblyTitleAttribute).Title;
}
string iconSourcePath = Path.Combine(System.Windows.Forms.Application.StartupPath, iconName);
if (!File.Exists(iconSourcePath))
{
return;
}
RegistryKey myUninstallKey = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\Uninstall");
string[] mySubKeyNames = myUninstallKey.GetSubKeyNames();
for (int i = 0; i < mySubKeyNames.Length; i++)
{
RegistryKey myKey = myUninstallKey.OpenSubKey(mySubKeyNames[i], true);
object myValue = myKey.GetValue("DisplayName");
if (myValue != null && myValue.ToString() == assemblyTitle)
{
myKey.SetValue("DisplayIcon", iconSourcePath);
break;
}
}
}
catch (Exception) { }
}
return;
}
The original article by Robin Shahan is here: RobinDotNet
For WPF application we need to replace the following code
string iconSourcePath = Path.Combine(System.Windows.Forms.Application.StartupPath, iconName);
Replace with below code
string iconSourcePath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.Startup), "TestIcon.ico");
I know you want 2015 but others may be looking for this in newer versions, like I was.
In Visual Studio 2019 Community we can go to the properties panel for the main setup project and the top property is AddRemoveProgramsIcon.
I have just come through this case today. I know it is old but will be useful for new seekers. To expose icon in Control Panel do the following:
Make a folder in [solution Folder][Project Folder]\bin\debug\images
Copy your icon in the new folder
In Set Up project always refer to the icon in the new created folder.
Solved my problem easily