【问题标题】:Is it possible to use the Windows icon picker dialog?是否可以使用 Windows 图标选择器对话框?
【发布时间】:2019-05-05 18:23:49
【问题描述】:

在我的代码中,我希望我的用户能够从他们计算机上的任何位置选择一个图标。该图标可以是独立的.ico,也可以是.exe.dll 中的图标——例如,不仅是.exe/dll 的默认显示图标,还包括其中包含的任何其他图标。

在理想情况下,我希望能够使用这个原生 Windows 图标选择器对话框:

但我不知道如何使用它 - 可以吗? 这个对话框对我来说是理想的,因为它似乎只允许用户浏览图标或.exes 和.dlls 作为标准。

如果我的用户只能使用独立的 .ico 文件,那么我将采取在 Windows Visa+ 的 Microsoft.WindowsAPICodePack-Shell nuget 包和旧系统的 System.Windows.Forms.FolderBrowserDialog 中找到的 CommonOpenFileDialog 类的路线 - 某事像这样:

private string SelectWinXPIcon()
{
    using (WinForms.OpenFileDialog ofd = new WinForms.OpenFileDialog()
    {
        Filter = "Icon files (*.ico)|*.ico",
    })
    {
        WinForms.DialogResult result = ofd.ShowDialog();
        switch (result)
        {
            case WinForms.DialogResult.OK:
            case WinForms.DialogResult.Yes:
                return ofd.FileName;

            default:
                return null;
        }
    }
}

private string SelectWinVistaIcon()
{
    using (CommonOpenFileDialog dialog = new CommonOpenFileDialog
    {
        DefaultDirectory = @"C:\",
        AllowNonFileSystemItems = false,
        EnsurePathExists = true,
        Multiselect = false,
        NavigateToShortcut = true
    })
    {
        dialog.Filters.Add(new CommonFileDialogFilter("Icon Files (*.ico)", ".ico"));
        CommonFileDialogResult result = dialog.ShowDialog();

        switch (result)
        {
            case CommonFileDialogResult.Ok:
                return dialog.FileName;

            default:
                return null;
        }
    }
}

但据我所知,这条路线不允许我的用户从.exe/dll 中选择图标?

如果无法使用图标选择器对话框,是否有另一种方法可以从.exe/dlls 中提取图标,这也允许选择独立的.ico 文件?

【问题讨论】:

    标签: c# wpf dialog


    【解决方案1】:

    这是通过 shell32 PickIconDlg 函数完成的,您可以使用 pinvoke site 轻松调用该函数以供参考。该函数将返回文件名和索引,然后您可以使用the shell32 ExtractIconEx function 提取图标句柄。然后,您可以将图标句柄转换为 GDI 图标或 WPF ImageSource。

    例如,在 XAML 中声明一个图像以显示用户选择的图标:

    <Image x:Name="myImage" Stretch="None" />
    

    然后在您的窗口加载处理程序中使用此代码来显示对话框,加载它,将其转换为 ImageSource 并显示它:

    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    private static extern int PickIconDlg(IntPtr hwndOwner, System.Text.StringBuilder lpstrFile, int nMaxFile, ref int lpdwIconIndex);
    
    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    private static extern uint ExtractIconEx(string szFileName, int nIconIndex, IntPtr[] phiconLarge, IntPtr[] phiconSmall, uint nIcons);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool DestroyIcon(IntPtr handle);
    
    private const int MAX_PATH = 0x00000104;
    
    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        // show the Pick Icon Dialog
        int index = 0;
        int retval;
        var handle = new WindowInteropHelper(this).Handle;
        var iconfile = Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\shell32.dll";
        var sb = new System.Text.StringBuilder(iconfile, MAX_PATH);
        retval = PickIconDlg(handle, sb, sb.MaxCapacity, ref index);
    
        if (retval != 0)
        {
            // extract the icon
            var largeIcons = new IntPtr[1];
            var smallIcons = new IntPtr[1];
            ExtractIconEx(sb.ToString(), index, largeIcons, smallIcons, 1);
    
            // convert icon handle to ImageSource
            this.myImage.Source = Imaging.CreateBitmapSourceFromHIcon(largeIcons[0],
                Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
    
            // clean up
            DestroyIcon(largeIcons[0]);
            DestroyIcon(smallIcons[0]);
        }
    }
    

    这将适用于 DLL/EXE 等以及独立的 .ICO 文件。

    【讨论】:

    • 效果非常好 - 谢谢。请问500是干什么用的?
    • 抱歉,我的编码有点草率。这是为StringBuilder 分配的字节数,它作为sb.MaxCapacity 传递给PickIconDlg,这样函数就不会浪费内存。 PickIconDlg 文档声明它应该设置为MAX_PATH,我已经相应地更新了代码。
    猜你喜欢
    • 2014-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多