WPF 读取注册表实现右键新建文件菜单

WPF 工具类NewMenuUtility的实现,核心是读取 Windows 系统注册表中 “新建文件” 相关配置,在 WPF 应用中生成并绑定系统风格的右键新建文件菜单,支持创建对应类型文件。

核心点

  • 注册表读取:从HKEY_CLASSES_ROOT下的*\ShellNew键、文件扩展名子键(如.txt),获取新建文件的类型、图标、默认内容等配置;
  • 菜单数据处理:整理文件类型描述、图标路径、默认文件名,补充 “文件夹” 和 Office 文件(.docx等)特殊项,去重生成NewMenuItem列表;
  • WPF 菜单绑定:将NewMenuItem转为 WPF 的MenuItem(加载图标),绑定到ContextMenu
  • 文件创建功能:点击菜单项时,在桌面生成唯一命名的文件(含内容或空文件),处理文件重名,提示创建结果。

一、添加NewMenuItem.cs 右键菜单类

点击查看代码
 /// <summary>
 /// 新建菜单项的类,包含文件的基本信息
 /// </summary>
 public class NewMenuItem : INotifyPropertyChanged
 {
     // INotifyPropertyChanged 实现
     public event PropertyChangedEventHandler PropertyChanged;

     protected void OnPropertyChanged(string propertyName)
     {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
     }

     private string _header;
     /// <summary>
     /// 菜单名称
     /// </summary>
     public string Header
     {
         get { return _header; }
         set
         {
             _header = value;
             OnPropertyChanged(nameof(Header));
         }
     }

     private string _extension;
     /// <summary>
     /// 文件扩展名
     /// </summary>
     public string Extension
     {
         get { return _extension; }
         set
         {
             _extension = value;
             OnPropertyChanged(nameof(Extension));
         }
     }

     private string _iconPath;
     /// <summary>
     /// 图标路径
     /// </summary>
     public string IconPath
     {
         get { return _iconPath; }
         set
         {
             _iconPath = value;
             OnPropertyChanged(nameof(IconPath));
         }
     }

     private string _fileName;
     /// <summary>
     /// 默认文件名
     /// </summary>
     public string FileName
     {
         get { return _fileName; }
         set
         {
             _fileName = value;
             OnPropertyChanged(nameof(FileName));
         }
     }

     private byte[] _fileContents;
     /// <summary>
     /// 文件内容
     /// </summary>
     public byte[] FileContents
     {
         get { return _fileContents; }
         set
         {
             _fileContents = value;
             OnPropertyChanged(nameof(FileContents));
         }
     }
 }

二、添加 NewMenuUtility 菜单工具类

点击查看代码
/// <summary>
 /// 工具类
 /// </summary>
 public class NewMenuUtility
 {
     /// <summary>
     /// 获取所有新建菜单项
     /// </summary>
     /// <returns></returns>
     public static List<NewMenuItem> GetNewMenuItems()
     {
         List<NewMenuItem> items = new List<NewMenuItem>();

         // 检查 HKEY_CLASSES_ROOT\* 下的 ShellNew 键
         using (RegistryKey shellNewKey = Registry.ClassesRoot.OpenSubKey(@"*\ShellNew"))
         {
             if (shellNewKey != null)
             {
                 foreach (string valueName in shellNewKey.GetValueNames())
                 {
                     if (valueName != "Classes")
                     {
                         AddNewMenuItem(items, valueName);
                     }
                 }
             }
         }
         // 检查 HKEY_CLASSES_ROOT 下所有以点开头的子键
         using (RegistryKey classesRoot = Registry.ClassesRoot)
         {
             foreach (string keyName in classesRoot.GetSubKeyNames().Where(k => k.StartsWith(".")))
             {
                 using (RegistryKey extensionKey = classesRoot.OpenSubKey(keyName + @"\ShellNew"))
                 {
                     if (extensionKey != null)
                     {
                         AddNewMenuItem(items, keyName);
                     }
                 }
             }
         }

         // 添加特殊项目,如文件夹
         items.Add(new NewMenuItem
         {
             Header = "文件夹",
             Extension = "",
             IconPath = @"%SystemRoot%\System32\shell32.dll,3"
         });

         // 处理 Office 文件类型
         HandleOfficeFileTypes(items);

         return items.Distinct(new NewMenuItemComparer()).ToList();
     }

     /// <summary>
     /// 从 ShellNew 键添加新菜单项
     /// </summary>
     /// <param name="items"></param>
     /// <param name="subKeyPath"></param>
     private static void AddShellNewItems(List<NewMenuItem> items, string subKeyPath)
     {
         // 打开指定的注册表子键
         using (RegistryKey shellNewKey = Registry.ClassesRoot.OpenSubKey(subKeyPath))
         {
             if (shellNewKey != null) // 若子键存在
             {
                 // 遍历子键中的所有值
                 foreach (string valueName in shellNewKey.GetValueNames())
                 {
                     // 排除 Classes 值
                     if (valueName != "Classes")
                     {
                         // 添加新菜单项
                         AddNewMenuItem(items, valueName);
                     }
                 }
             }
         }
     }

     /// <summary>
     /// 从扩展名添加新菜单项
     /// </summary>
     /// <param name="items"></param>
     private static void AddExtensionItems(List<NewMenuItem> items)
     {
         // 打开 HKEY_CLASSES_ROOT 注册表根
         using (RegistryKey classesRoot = Registry.ClassesRoot)
         {
             // 遍历所有以点开头的子键(即文件扩展名)
             foreach (string keyName in classesRoot.GetSubKeyNames().Where(k => k.StartsWith(".")))
             {
                 // 添加新菜单项
                 AddNewMenuItem(items, keyName);
             }
         }
     }

     /// <summary>
     /// 添加新菜单项
     /// </summary>
     /// <param name="items"></param>
     /// <param name="extension"></param>
     private static void AddNewMenuItem(List<NewMenuItem> items, string extension)
     {
         // 获取文件类型描述
         string fileTypeName = GetFileTypeDescription(extension);
         // 获取图标路径
         string iconPath = GetIconPath(extension);
         // 获取新建文件的内容
         byte[] fileContents = GetNewFileContents(extension);
         // 获取新建文件的默认名称
         string fileName = GetNewFileName(extension);

         // 创建新菜单项并添加到列表
         items.Add(new NewMenuItem
         {
             Header = fileTypeName, // 设置菜单项名称
             Extension = extension, // 设置文件扩展名
             IconPath = iconPath, // 设置图标路径
             FileName = fileName, // 设置默认文件名
             FileContents = fileContents // 设置文件内容
         });
     }

     /// <summary>
     /// 获取文件类型描述
     /// </summary>
     /// <param name="extension"></param>
     /// <returns></returns>
     private static string GetFileTypeDescription(string extension)
     {
         // 打开与扩展名对应的注册表子键
         using (RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(extension))
         {
             if (extensionKey != null) // 若子键存在
             {
                 // 获取默认值(文件类型名称)
                 object defaultValue = extensionKey.GetValue("");
                 if (defaultValue != null)
                 {
                     // 打开文件类型对应的注册表子键
                     using (RegistryKey typeKey = Registry.ClassesRoot.OpenSubKey(defaultValue.ToString()))
                     {
                         // 若子键存在
                         if (typeKey != null)
                         {
                             // 获取文件类型的描述
                             object description = typeKey.GetValue("");
                             if (description != null)
                             {
                                 return description.ToString();
                             }
                         }
                     }
                 }
             }
         }
         // 若没有找到描述,返回默认格式
         return $"New {extension.TrimStart('.')} File";
     }

     /// <summary>
     /// 获取新建文件的内容
     /// </summary>
     /// <param name="extension"></param>
     /// <returns></returns>
     private static byte[] GetNewFileContents(string extension)
     {
         // 打开与扩展名对应的 ShellNew 注册表子键
         using (RegistryKey shellNewKey = Registry.ClassesRoot.OpenSubKey($@"{extension}\ShellNew"))
         {
             if (shellNewKey != null) // 若子键存在
             {
                 // 若存在 Data 值,返回其内容
                 if (shellNewKey.GetValue("Data") is byte[] data)
                 {
                     return data;
                 }
                 // 若存在 FileName 值,读取文件内容
                 else if (shellNewKey.GetValue("FileName") is string fileName)
                 {
                     // 扩展环境变量
                     string fullPath = Environment.ExpandEnvironmentVariables(fileName);
                     // 若文件存在
                     if (File.Exists(fullPath))
                     {
                         return File.ReadAllBytes(fullPath);
                     }
                 }
             }
         }
         return null; // 若没有内容,返回 null
     }

     /// <summary>
     /// 获取新建文件的默认名称
     /// </summary>
     /// <param name="extension"></param>
     /// <returns></returns>
     private static string GetNewFileName(string extension)
     {
         // 返回默认文件名
         return $"New File{extension}";
     }

     // 处理 Office 文件类型
     private static void HandleOfficeFileTypes(List<NewMenuItem> items)
     {
         // 定义 Office 文件类型及其名称
         Dictionary<string, string> officeApps = new Dictionary<string, string>
         {
             {".docx", "Microsoft Word 文档"},
             {".xlsx", "Microsoft Excel 工作表"},
             {".pptx", "Microsoft PowerPoint 演示文稿"}
         };

         // 遍历 Office 文件类型
         foreach (var app in officeApps)
         {
             // 获取图标路径
             string iconPath = GetIconPath(app.Key);
             // 添加新菜单项
             items.Add(new NewMenuItem
             {
                 Header = app.Value, // 设置菜单项名称
                 Extension = app.Key, // 设置文件扩展名
                 IconPath = iconPath, // 设置图标路径
                 FileName = app.Value,
             });
         }
     }

     /// <summary>
     /// 获取图标路径
     /// </summary>
     /// <param name="extension"></param>
     /// <returns></returns>
     private static string GetIconPath(string extension)
     {
         // 打开与扩展名对应的注册表子键
         using (RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(extension))
         {
             if (extensionKey != null) // 若子键存在
             {
                 // 获取文件类型
                 string fileType = (string)extensionKey.GetValue("");
                 // 若文件类型存在
                 if (!string.IsNullOrEmpty(fileType))
                 {
                     // 打开文件类型的 DefaultIcon 子键
                     using (RegistryKey typeKey = Registry.ClassesRoot.OpenSubKey(fileType + @"\DefaultIcon"))
                     {
                         // 若子键存在
                         if (typeKey != null)
                         {
                             // 返回图标路径
                             return (string)typeKey.GetValue("");
                         }
                     }
                 }
             }
         }
         return null; // 若没有找到图标路径,返回 null
     }

     /// <summary>
     /// 比较器用于去重
     /// </summary>
     private class NewMenuItemComparer : IEqualityComparer<NewMenuItem>
     {
         // 判断两个菜单项是否相等
         public bool Equals(NewMenuItem x, NewMenuItem y)
         {
             // 根据扩展名判断
             return x.Extension == y.Extension;
         }

         // 获取菜单项的哈希码
         public int GetHashCode(NewMenuItem obj)
         {
             // 返回扩展名的哈希码
             return obj.Extension.GetHashCode();
         }
     }

     /// <summary>
     /// 绑定新菜单项到上下文菜单
     /// </summary>
     /// <param name="contextMenu"></param>
     public static void BindNewMenuItems(ContextMenu contextMenu)
     {
         // 获取新菜单项列表
         List<NewMenuItem> newMenuItems = GetNewMenuItems();

         // 遍历新菜单项
         foreach (var item in newMenuItems)
         {
             MenuItem menuItem = new MenuItem
             {
                 Header = item.Header // 设置菜单项标题
             };

             // 若图标路径不为空
             if (!string.IsNullOrEmpty(item.IconPath))
             {
                 try
                 {
                     // 分割图标路径和索引
                     string[] iconInfo = item.IconPath.Split(',');
                     // 扩展环境变量
                     string iconPath = Environment.ExpandEnvironmentVariables(iconInfo[0]);
                     // 获取图标索引
                     int iconIndex = iconInfo.Length > 1 ? int.Parse(iconInfo[1]) : 0;

                     // 加载图标为 BitmapSource
                     BitmapSource iconSource = LoadIconAsBitmapSource(iconPath, iconIndex);
                     // 若图标加载成功
                     if (iconSource != null)
                     {
                         // 设置菜单项图标
                         menuItem.Icon = new System.Windows.Controls.Image { Source = iconSource };
                     }
                 }
                 catch
                 {
                     // 若加载图标失败,忽略错误
                 }
             }

             // 为菜单项添加点击事件
             menuItem.Click += (sender, e) => CreateNewFile(item);
             // 将菜单项添加到上下文菜单
             contextMenu.Items.Add(menuItem);
         }
     }


     /// <summary>
     /// 加载图标为 BitmapSource
     /// </summary>
     /// <param name="filePath"></param>
     /// <param name="iconIndex"></param>
     /// <returns></returns>
     private static BitmapSource LoadIconAsBitmapSource(string filePath, int iconIndex)
     {
         // 初始化图标句柄
         IntPtr hIcon = IntPtr.Zero;
         try
         {
             // 从文件中提取图标
             hIcon = NativeMethods.ExtractIcon(IntPtr.Zero, filePath, iconIndex);
             // 若图标提取成功
             if (hIcon != IntPtr.Zero)
             {
                 // 创建 BitmapSource
                 return Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
             }
         }
         finally
         {
             // 若图标句柄有效
             if (hIcon != IntPtr.Zero)
             {
                 // 销毁图标句柄
                 NativeMethods.DestroyIcon(hIcon);
             }
         }
         return null; // 若没有图标,返回 null
     }

     // 本地方法调用
     private static class NativeMethods
     {
         // 从指定文件提取图标
         [System.Runtime.InteropServices.DllImport("shell32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
         public static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex);

         // 销毁图标句柄
         [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
         public static extern bool DestroyIcon(IntPtr handle);
     }


     /// <summary>
     /// 创建新文件
     /// </summary>
     /// <param name="item"></param>
     private static void CreateNewFile(NewMenuItem item)
     {
         try
         {
             // 获取桌面路径
             string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
             // 组合文件路径
             string filePath = Path.Combine(desktopPath, item.FileName);

             // 确保文件名是唯一的
             int counter = 1;
             // 若文件已存在
             while (File.Exists(filePath))
             {
                 // 生成新的文件名
                 string fileName = $"{Path.GetFileNameWithoutExtension(item.FileName)} ({counter}){item.Extension}";
                 // 更新文件路径
                 filePath = Path.Combine(desktopPath, fileName);
                 // 计数器递增
                 counter++;
             }

             // 创建新文件
             // 若文件内容不为空
             if (item.FileContents != null)
             {
                 // 写入文件内容
                 File.WriteAllBytes(filePath, item.FileContents);
             }
             else
             {
                 // 创建空文件并立即释放资源
                 File.Create($"{filePath}{item.Extension}").Dispose();
             }

             // 显示成功消息
             MessageBox.Show($"文件创建成功:{filePath}", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
         }
         catch (Exception ex)
         {
             // 显示错误消息
             MessageBox.Show($"创建文件时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
         }
     }

 }

三、MainWindow.xaml 文件中添加右键菜单样式

点击查看代码
<Window
    x:Class="SystemNewFileMenuDemo.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="http://prismlibrary.com/"
    Title="{Binding Title}"
    Width="525"
    Height="350"
    prism:ViewModelLocator.AutoWireViewModel="True">

    <Window.Resources>
        <!--  菜单项  -->
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type MenuItem}">
                        <Border
                            x:Name="Border"
                            Background="Transparent"
                            BorderBrush="Transparent"
                            BorderThickness="1">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" SharedSizeGroup="Icon" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>

                                <!--  图标  -->
                                <ContentPresenter
                                    x:Name="Icon"
                                    Width="16"
                                    MaxHeight="16"
                                    Margin="6,0,6,0"
                                    VerticalAlignment="Center"
                                    ContentSource="Icon" />

                                <!--  文本  -->
                                <ContentPresenter
                                    Grid.Column="1"
                                    MinWidth="80"
                                    Margin="0,6,6,6"
                                    VerticalAlignment="Center"
                                    ContentSource="Header" />
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <!--  鼠标悬停效果  -->
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="Border" Property="Background" Value="#F2F2F2" />
                                <Setter TargetName="Border" Property="BorderBrush" Value="#F2F2F2" />
                            </Trigger>
                            <!--  选中效果  -->
                            <Trigger Property="IsPressed" Value="True">
                                <Setter TargetName="Border" Property="Background" Value="#D0D0D0" />
                                <Setter TargetName="Border" Property="BorderBrush" Value="#CCCEDB" />
                            </Trigger>
                            <!--  禁用效果  -->
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Foreground" Value="#888888" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--  右键菜单样式  -->
        <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#E7E8EC" />
        <SolidColorBrush x:Key="SolidBorderBrush" Color="#CCCEDB" />
        <Style TargetType="{x:Type ContextMenu}">
            <Setter Property="SnapsToDevicePixels" Value="True" />
            <Setter Property="OverridesDefaultStyle" Value="True" />
            <Setter Property="FontFamily" Value="Segoe UI" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ContextMenu}">
                        <Border
                            Name="Border"
                            Margin="9"
                            Background="{StaticResource WindowBackgroundBrush}"
                            BorderBrush="{StaticResource SolidBorderBrush}"
                            BorderThickness="0">
                            <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
                            <Border.Effect>
                                <DropShadowEffect
                                    BlurRadius="8"
                                    ShadowDepth="1"
                                    Color="#E8E8E8" />
                            </Border.Effect>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <!--  添加右键菜单交互  -->
    <Grid
        Width="500"
        Height="300"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Background="#F2F2F2">
        <TextBlock
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Text="鼠标点击右键调用菜单" />
        <Grid.ContextMenu>
            <ContextMenu x:Name="myContextMenu" />
        </Grid.ContextMenu>
    </Grid>
</Window>

四、MainWindow.xaml.cs 文件中绑定右键菜单

点击查看代码
/// <summary>
 /// Interaction logic for MainWindow.xaml
 /// </summary>
 public partial class MainWindow : Window
 {

     public MainWindow()
     {
         InitializeComponent();

         // 绑定右键菜单
         NewMenuUtility.BindNewMenuItems(myContextMenu);
     }
 }

五、效果图展示

微信图片_2025-09-19_173001_938

posted @ 2025-09-19 17:31  笺上知微  阅读(26)  评论(0)    收藏  举报