プロフィール

髭山髭人(ひげひと)

自分の書いた記事が、一人でも誰かの役に立てば...
活動信条の一つとして「貴方のメモは、誰かのヒント」というのがあります。

このサイトについて

本家HP packetroom.net から切り離した いわゆる技術メモ用のブログで、無料レンタルサーバーにて運用しています。広告表示はその義務なのでご容赦。
XREA さんには長年お世話になっています

C# WPF ちょっとしたログ表示用機構

趣旨

コードビハインド側で ObservableCollection にログを持ってもらい、xaml 側の ScrollViewer 内部で表示させる。
この時、xaml 側では ItemsSource 属性で Binding させておくのがクソデカポイント
( 紐づけされないと、いくらログを増やしてもフォーム反映されない )

また、以下条件を整えないと Binding がうまくいかず、フォーム上のログが更新されなかったので注意

個人的オススメ

static class で独立気味に。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Log.Add("ログ");    // 使い方
        Log.LogCollection.Add("Hoge");  // 何ならこっちでも良い
    }

    // ログ表示用
    public static class Log
    {
        private static ObservableCollection<string> _logCollection = new ObservableCollection<string>();
        public static ObservableCollection<string> LogCollection { get { return _logCollection; } }
        public static void Add(string _text) { LogCollection.Add(_text); }
    }
}
<Grid>
    <DockPanel LastChildFill="True">
        <ScrollViewer CanContentScroll="True">
            <ItemsControl ItemsSource="{Binding Path=(local:Log.LogCollection)}"/>
        </ScrollViewer>
    </DockPanel>
</Grid>

一緒で良いなら

事前に DataContext へ登録しておく必要アリ

public partial class MainWindow : Window
{
    // フィールドではなくプロパティ かつ public で持つこと
    public ObservableCollection<string> LogCollection { get; set; }
    public MainWindow()
    {
        LogCollection = new ObservableCollection<string>();
        // ※ データコンテキストへ丸ごと登録しておくこと
        DataContext = this;
        InitializeComponent();
        // 以下要領でログを増やせば、任意タイミングでフォーム上でも更新される
        LogCollection.Add("ログ表示1");
        LogCollection.Add("ログ表示2");
    }
}
<Grid>
    <DockPanel LastChildFill="True">
        <StackPanel Margin="0,0,5,0">
            <TextBlock>なんちゃらかんちゃら</TextBlock>
        </StackPanel>
        <DockPanel LastChildFill="True">
            <ScrollViewer CanContentScroll="True">
                <ItemsControl ItemsSource="{Binding Path=LogCollection}" />
            </ScrollViewer>
        </DockPanel>
    </DockPanel>
</Grid>

DataContext 登録省略版

やってることは上記とほぼ変わらない
xaml 側で行ってたログデータとの紐づけ相当を、コードビハインド側に移しただけ
x:Name で引っぱり出せるように名前付けしたりしなきゃなので、さっきのほうがスッキリしてて良さそう

public partial class MainWindow : Window
{
    // private でもOK フィールドでもOK
    private readonly ObservableCollection<string> logCollection = new ObservableCollection<string>();

    public MainWindow()
    {
        InitializeComponent();

        var itemsControl = new ItemsControl();
        itemsControl.ItemsSource = logCollection;
        ScrollViewer scrollViewer = (ScrollViewer)FindName("LogScrollViewer");
        scrollViewer.Content = itemsControl;

        logCollection.Add("ログ表示1");
        logCollection.Add("ログ表示2");
    }
}
<Grid>
    <DockPanel LastChildFill="True">
        <StackPanel Margin="0,0,5,0">
            <TextBlock>なんちゃらかんちゃら</TextBlock>
        </StackPanel>
        <DockPanel LastChildFill="True">
            <ScrollViewer x:Name="LogScrollViewer" CanContentScroll="True"/>
        </DockPanel>
    </DockPanel>
</Grid>

余談:DataContext への丸ごと登録

上記で述べた DataContext へのまるごと登録はざっくり2種あって、
まずは以下のコードビハインド側で代入した処理。
これは1つめの例で用いられてたもの

public MainWindow()
{
    DataContext = this; // 登録!
    InitializeComponent();
    // 以下略
}

もう一つが xaml 側で登録する方法。

<Window x:Class="HogeProject.MainWindow"
        ...中略...
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        ...中略...
    </Grid>
</Window>

お好みでどうぞ

動作イメージ

参考リンクなど

teratail - WPFでログ出力のようなことをしたい
Stack Overflow - Make ScrollViewer fill dynamic area