WPFでログビューアを実装する
2023-10-22 07:28:16
質問
WPFでコンソールログビューアを実装するための最良の方法について助言を求めます。
それは以下の基準に一致する必要があります。
- 100.000 行以上の高速スクロール
- いくつかのエントリ (スタックトレースなど) は折りたたみ可能であるべきです。
- 長い項目は折り返す
- リストはさまざまな基準でフィルタリングできる(検索、タグなど)。
- 最後に新しいアイテムが追加されたとき、スクロールし続ける必要があります。
- 行要素は、ハイパーリンクや発生カウンターのような何らかの追加フォーマットを含むことができます。
一般的には、FireBugやChromeのコンソールウィンドウのようなものを考えています。
で遊んでみたのですが この を使ってみましたが、あまり進歩がありませんでした。 - データグリッドは異なるアイテムの高さを処理できません。 - スクロール位置は、スクロールバーを解放した後にのみ更新されます (これは完全に受け入れられません)。
私はかなり確信しています、私は何らかの形の仮想化が必要で、MVVMパターンに従いたいと思っています。
どんなヘルプやポインタでも歓迎します。
どのように解決するのですか?
これらの WPF サンプルを無料で配布するのではなく、販売を開始する必要があります。
-
仮想化されたUI (使用
VirtualizingStackPanel
を使用)、これは信じられないほど良いパフォーマンスを提供します(200000以上のアイテムがある場合でも)。 - 完全にMVVMフレンドリーです。
-
DataTemplate
の種類に応じたLogEntry
のタイプに分かれています。これによって、いくらでもカスタマイズすることができます。今回は2種類のLogEntries(basicとnested)しか実装していませんが、イメージはつかめたと思います。サブクラスとしてLogEntry
を必要なだけサブクラス化することができます。リッチテキストや画像に対応することも可能です。 - 拡張可能な(ネストされた)アイテム。
- ワードラップ。
-
フィルタリングなどの実装は
CollectionView
. -
WPF Rocksでは、私のコードをコピー&ペーストで
File -> New -> WPF Application
にコピーして、その結果を自分で見てください。
<Window x:Class="MiscSamples.LogViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MiscSamples"
Title="LogViewer" Height="500" Width="800">
<Window.Resources>
<Style TargetType="ItemsControl" x:Key="LogViewerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type local:LogEntry}">
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
<ColumnDefinition SharedSizeGroup="Date" Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DateTime}" Grid.Column="0"
FontWeight="Bold" Margin="5,0,5,0"/>
<TextBlock Text="{Binding Index}" Grid.Column="1"
FontWeight="Bold" Margin="0,0,2,0" />
<TextBlock Text="{Binding Message}" Grid.Column="2"
TextWrapping="Wrap"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:CollapsibleLogEntry}">
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
<ColumnDefinition SharedSizeGroup="Date" Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding DateTime}" Grid.Column="0"
FontWeight="Bold" Margin="5,0,5,0"/>
<TextBlock Text="{Binding Index}" Grid.Column="1"
FontWeight="Bold" Margin="0,0,2,0" />
<TextBlock Text="{Binding Message}" Grid.Column="2"
TextWrapping="Wrap"/>
<ToggleButton x:Name="Expander" Grid.Row="1" Grid.Column="0"
VerticalAlignment="Top" Content="+" HorizontalAlignment="Right"/>
<ItemsControl ItemsSource="{Binding Contents}" Style="{StaticResource LogViewerStyle}"
Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
x:Name="Contents" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<Trigger SourceName="Expander" Property="IsChecked" Value="True">
<Setter TargetName="Contents" Property="Visibility" Value="Visible"/>
<Setter TargetName="Expander" Property="Content" Value="-"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<DockPanel>
<TextBlock Text="{Binding Count, StringFormat='{}{0} Items'}"
DockPanel.Dock="Top"/>
<ItemsControl ItemsSource="{Binding}" Style="{StaticResource LogViewerStyle}">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DockPanel>
</Window>
背後のコード。 (そのほとんどが例(ランダムなエントリーを生成する)をサポートするための定型的なものであることに注意してください。
public partial class LogViewer : Window
{
private string TestData = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
private List<string> words;
private int maxword;
private int index;
public ObservableCollection<LogEntry> LogEntries { get; set; }
public LogViewer()
{
InitializeComponent();
random = new Random();
words = TestData.Split(' ').ToList();
maxword = words.Count - 1;
DataContext = LogEntries = new ObservableCollection<LogEntry>();
Enumerable.Range(0, 200000)
.ToList()
.ForEach(x => LogEntries.Add(GetRandomEntry()));
Timer = new Timer(x => AddRandomEntry(), null, 1000, 10);
}
private System.Threading.Timer Timer;
private System.Random random;
private void AddRandomEntry()
{
Dispatcher.BeginInvoke((Action) (() => LogEntries.Add(GetRandomEntry())));
}
private LogEntry GetRandomEntry()
{
if (random.Next(1,10) > 1)
{
return new LogEntry
{
Index = index++,
DateTime = DateTime.Now,
Message = string.Join(" ", Enumerable.Range(5, random.Next(10, 50))
.Select(x => words[random.Next(0, maxword)])),
};
}
return new CollapsibleLogEntry
{
Index = index++,
DateTime = DateTime.Now,
Message = string.Join(" ", Enumerable.Range(5, random.Next(10, 50))
.Select(x => words[random.Next(0, maxword)])),
Contents = Enumerable.Range(5, random.Next(5, 10))
.Select(i => GetRandomEntry())
.ToList()
};
}
}
データ項目です。
public class LogEntry : PropertyChangedBase
{
public DateTime DateTime { get; set; }
public int Index { get; set; }
public string Message { get; set; }
}
public class CollapsibleLogEntry: LogEntry
{
public List<LogEntry> Contents { get; set; }
}
PropertyChangedBaseです。
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
関連
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み] UnityでOnCollisionEnterが呼ばれない
-
[解決済み】「namespace」なのに「type」のように使われる。
-
VSでscanfエラーを恒久的に解決するには、ソースファイルを作成し、自動的に#define _CRT_SECURE_NO_WARNINGS 1を追加してください。
-
[解決済み] jQueryで要素にスクロールする
-
[解決済み] INotifyPropertyChangedの実装 - より良い方法は存在するか?
-
[解決済み] RelativeSourceでWPFバインディングを使用するにはどうしたらいいですか?
-
[解決済み] WPFアプリケーションをプログラムで終了させるには?
-
[解決済み] WPF画像リソースの保存
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C#で四捨五入する方法
-
[解決済み] [Entity Framework 4.1でエンティティに関連オブジェクトを追加する際に、エンティティオブジェクトをIEntityChangeTracker.の複数のインスタンスから参照できない。
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み] [Solved] アセンブリ System.Web.Extensions dll はどこにありますか?
-
[解決済み】Socket.Selectがエラー "An operation was attempted on something that is not a socket" を返す。
-
[解決済み】HRESULTからの例外:0x800A03ECエラー
-
[解決済み】「...は'型'であり、与えられたコンテキストでは有効ではありません」を解決するにはどうすればよいですか?(C#)
-
[解決済み】値をNULLにすることはできません。パラメータ名:source
-
[解決済み】C#のequal to演算子でtextとvarcharのデータ型は互換性がない
-
[解決済み】データが存在しないのに読み込もうとする試みが無効である