XAMLを使わないWPF入門25「バインドしないListView」

コンピュータ

通常WPFでListViewを扱う場合、データソースのコレクションとバインドしますが、この記事ではWinFormの用に1件1件ListViewItemを追加してListViewを構築するサンプルコードになります。

実行結果のスクリーンショット
image

ファイル名:NoXAML25ListView.csproj


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

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

</Project>

ファイル名:App.cs


using System;
using System.Windows;

namespace NoXAML25ListView;

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

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var win = new MainWindow();
        win.Show();
    }
}

ファイル名:AssemblyInfo.cs


using System.Windows;

[assembly:ThemeInfo(
    ResourceDictionaryLocation.None,            //where theme specific resource dictionaries are located
                                                //(used if a resource is not found in the page,
                                                // or application resource dictionaries)
    ResourceDictionaryLocation.SourceAssembly   //where the generic resource dictionary is located
                                                //(used if a resource is not found in the page,
                                                // app, or any theme specific resource dictionaries)
)]

ファイル名:MainWindow.cs


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

namespace NoXAML25ListView;

public class MainWindow : Window
{
    public class ItemData
    {
        public object? Name { get; set; }
        public long Size { get; set; }
    }
    public MainWindow()
    {
        this.Title = "NoXAML ListView";
        this.Width = 400;
        this.Height = 300;

        var gridView = new GridView();
        gridView.Columns.Add(new GridViewColumn
        {
            Header = "Name",
            Width = 200,
            CellTemplate = CreateNameTemplate()
        });
        gridView.Columns.Add(new GridViewColumn
        {
            Header = "Size",
            Width = 80,
            CellTemplate = CreateSizeTemplate(),
        });

        var listView = new ListView
        {
            View = gridView,
        };

        // アイコン画像生成
        var folderIcon = CreateIconBitmap("\uE8B7", 20, Brushes.SteelBlue);
        var fileIcon = CreateIconBitmap("\uE8A5", 20, Brushes.Gray);

        // Header = StackPanel(icon, text)
        listView.Items.Add(new ItemData
        {
            Name = CreateHeaderPanel(folderIcon, "フォルダ"),
            Size = 0,
        });
        listView.Items.Add(new ItemData
        {
            Name = CreateHeaderPanel(fileIcon, "ファイル.txt"),
            Size = 12345,
        });

        this.Content = listView;
    }
    // 画像アイコン+テキストのStackPanel生成
    private static StackPanel CreateHeaderPanel(BitmapSource icon, string name)
    {
        var stack = new StackPanel
        {
            Orientation = Orientation.Horizontal,
            VerticalAlignment = VerticalAlignment.Center,
        };
        var img = new Image
        {
            Source = icon,
            Width = 20,
            Height = 20,
            Margin = new Thickness(0, 0, 4, 0),
            VerticalAlignment = VerticalAlignment.Center,
        };
        var text = new TextBlock
        {
            Text = name,
            VerticalAlignment = VerticalAlignment.Center,
        };
        stack.Children.Add(img);
        stack.Children.Add(text);
        return stack;
    }

    // DataTemplateでHeader(UIElement)を表示
    private static DataTemplate CreateNameTemplate()
    {
        var dt = new DataTemplate(typeof(ItemData));
        var factory = new FrameworkElementFactory(typeof(ContentPresenter));
        factory.SetBinding(ContentPresenter.ContentProperty, new System.Windows.Data.Binding("Name"));
        dt.VisualTree = factory;
        return dt;
    }

    // 文字→BitmapSource
    private BitmapSource CreateIconBitmap(string iconText, int size, Brush fg)
    {
        var font = new Typeface(new FontFamily("Segoe MDL2 Assets"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
        var formatted = new FormattedText(
            iconText,
            System.Globalization.CultureInfo.InvariantCulture,
            FlowDirection.LeftToRight,
            font,
            size,
            fg,
            VisualTreeHelper.GetDpi(this).PixelsPerDip);

        var width = (int)formatted.Width + 2;
        var height = (int)formatted.Height + 2;

        var dv = new DrawingVisual();
        using (var dc = dv.RenderOpen())
        {
            dc.DrawText(formatted, new Point(1, 1));
        }
        var bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
        bmp.Render(dv);
        return bmp;
    }
    // Size列用DataTemplate(右寄せTextBlock)
    private static DataTemplate CreateSizeTemplate()
    {
        var dt = new DataTemplate(typeof(MainWindow.ItemData));
        var dock = new FrameworkElementFactory(typeof(DockPanel));

        var factory = new FrameworkElementFactory(typeof(TextBlock));
        factory.SetBinding(TextBlock.TextProperty, new System.Windows.Data.Binding("Size"));
        factory.SetValue(TextBlock.TextAlignmentProperty, TextAlignment.Right);
        factory.SetValue(TextBlock.PaddingProperty, new Thickness(0,0,16,0)); // 右端に余白調整
        factory.SetValue(TextBlock.WidthProperty, 70.0); // 右端に余白調整
        factory.SetValue(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Right);
        factory.SetValue(TextBlock.VerticalAlignmentProperty, VerticalAlignment.Top);


        dock.AppendChild(factory);
        dt.VisualTree = dock;
        return dt;
    }
}

コメント