WPFでテキストからフォルダ・ファイルアイコン画像を生成する。

コンピュータ

WPFでファイルやフォルダアイコンが必要ですが、アイコンファイルを用意するのも面倒なので、テキストから生成する方法を調べてみました。

プロジェクト作成

cd (mkdir WpfFileIcon01)
dotnet new wpf -f net8.0
rm MainWindow.xaml.cs
rm *.xaml

ソースコード

ファイル名:WpfFileIcon01.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseWPF>true</UseWPF>
    <StartupObject>WpfFileIcon01.App</StartupObject>
  </PropertyGroup>

</Project>

ファイル名:App.xaml.cs

using System.Windows;

namespace WpfFileIcon01;

public partial class App : Application
{
    [STAThread]
    public static void Main()
    {
        var window = new MainWindow();
        var app = new App();
        app.Run(window);
    }
}

ファイル名:MainWindow.cs

using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace WpfFileIcon01;

public enum DefaultIconType
{
    Folder,
    File,
    Error
}

public class MainWindow : Window
{
    private static BitmapImage CreateDefaultIcon(DefaultIconType type)
    {
        var width = 256;
        var height = 256;

        var dv = new DrawingVisual();
        using (var dc = dv.RenderOpen())
        {
            Brush bg = type switch
            {
                DefaultIconType.Folder => Brushes.Goldenrod,
                DefaultIconType.File => Brushes.LightSlateGray,
                DefaultIconType.Error => Brushes.DarkRed,
                _ => Brushes.Gray
            };

            string text = type switch
            {
                DefaultIconType.Folder => "📁",
                DefaultIconType.File => "📄",
                DefaultIconType.Error => "❌",
                _ => "?"
            };

            // 背景
            dc.DrawRectangle(bg, null, new Rect(0, 0, width, height));

            // テキスト
            var formatted = new FormattedText(
                text,
                System.Globalization.CultureInfo.InvariantCulture,
                FlowDirection.LeftToRight,
                new Typeface("Segoe UI Emoji"),
                100,
                Brushes.White,
                VisualTreeHelper.GetDpi(dv).PixelsPerDip
            );
            dc.DrawText(formatted, new Point((width - formatted.Width) / 2, (height - formatted.Height) / 2));
        }

        var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
        rtb.Render(dv);

        // Encode to PNG in memory
        var encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(rtb));
        using var ms = new MemoryStream();
        encoder.Save(ms);
        ms.Position = 0;

        var image = new BitmapImage();
        image.BeginInit();
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.StreamSource = ms;
        image.EndInit();
        return image;
    }

    StackPanel _stackPanel = new()
    {
        Orientation = Orientation.Horizontal,
    };
    Image _fileImage = new()
    {
        Width = 256,
        Height = 256,
    };
    Image _folderImage = new()
    {
        Width = 256,
        Height = 256,
    };
    Image _errorImage = new()
    {
        Width = 256,
        Height = 256,
    };
    public MainWindow()
    {
        Title = "アイコンを表示";
        Width = 768;
        Height = 300;

        _fileImage.Source = CreateDefaultIcon(DefaultIconType.File);
        _folderImage.Source = CreateDefaultIcon(DefaultIconType.Folder);
        _errorImage.Source = CreateDefaultIcon(DefaultIconType.Error);

        _stackPanel.Children.Add(_fileImage);
        _stackPanel.Children.Add(_folderImage);
        _stackPanel.Children.Add(_errorImage);
        
        Content = new Grid { Children = { _stackPanel } };
    }
}

実行

dotnet run

アイコン用画像が表示されます。

コメント