Je devrais commencer à vendre ces échantillons WPF au lieu de les donner gratuitement. =P
- Interface utilisateur virtualisée (Utilisation
VirtualizingStackPanel
) qui offre des performances incroyables (même avec plus de 200 000 éléments).
- Entièrement compatible avec MVVM.
-
DataTemplate
pour chaque type de LogEntry
type. Ceux-ci vous donnent la possibilité de les personnaliser autant que vous le souhaitez. Je n'ai implémenté que 2 types de LogEntries (basic et nested), mais vous comprenez l'idée. Vous pouvez sous-classer LogEntry
autant que vous le souhaitez. Vous pouvez même prendre en charge le texte riche ou les images.
- Éléments extensibles (imbriqués).
- Word Wrap.
- Vous pouvez mettre en œuvre le filtrage, etc. en utilisant un fichier
CollectionView
.
-
WPF Rocks, il suffit de copier et coller mon code dans une File -> New -> WPF Application
et voyez les résultats par vous-même.
<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>
Code Behind : (Remarquez que la plupart de ces éléments ne sont que du texte standard pour soutenir l'exemple (générer des entrées aléatoires).
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()
};
}
}
Éléments de données :
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));
}));
}
}
0 votes
Êtes-vous sûr de devoir mettre en place votre propre visionneuse de journaux ? C'est un peu réinventer la roue... Pouvez-vous utiliser des outils tiers pour visualiser vos journaux ? Par exemple, vous pouvez ouvrir DbgView et il capturera les journaux qui sont envoyés via l'API Windows. Vous pouvez ensuite diffuser les journaux qui seront capturés dans l'outil, pour une navigation et un filtrage faciles.
1 votes
Excellente question. J'ai besoin de ce composant dans le cadre d'une application WPF existante. Nous disposons déjà d'une "console" qui est implémentée sous la forme d'une TextBox d'une lenteur frustrante. Mais nous avons maintenant besoin des fonctionnalités supplémentaires que j'ai décrites. Je suis très heureux de réutiliser des composants commerciaux ou gratuits non-GPL existants.