2013-05-24 33 views
56

Tôi tìm kiếm lời khuyên về cách tiếp cận tốt nhất để triển khai trình xem nhật ký bảng điều khiển với WPF.Triển khai trình xem nhật ký với WPF

Nó phải phù hợp với các tiêu chí sau:

  • nhanh di chuyển với 100.000+ dòng
  • Một số mục (như stacktraces) nên có thể gập lại
  • mục dài quấn
  • danh sách có thể được lọc bởi các tiêu chí khác nhau (tìm kiếm, thẻ, v.v.)
  • khi ở cuối, nó sẽ tiếp tục cuộn khi các mục mới được thêm
  • Các phần tử đường có thể chứa một số loại định dạng bổ sung như siêu liên kết và số lần truy cập xuất hiện

Nói chung tôi có điều gì đó trong đầu như cửa sổ bảng điều khiển của FireBug và Chrome.

tôi đã chơi xung quanh với this nhưng tôi đã không làm cho nhiều tiến bộ, bởi vì ... - DataGrid không thể xử lý đỉnh cao mục khác nhau - vị trí cuộn chỉ được cập nhật sau khi phát hành scrollbar (đó là hoàn toàn không thể chấp nhận).

Tôi khá chắc chắn, tôi cần một số hình thức ảo hóa và rất thích theo dõi mẫu MVVM.

Mọi trợ giúp hoặc con trỏ đều được chào đón.

+0

Bạn có chắc chắn cần triển khai trình xem nhật ký của riêng mình không? Đây là loại phát minh lại bánh xe ... Bạn có thể sử dụng công cụ của bên thứ 3 để xem nhật ký của mình không? Ví dụ, bạn có thể mở [DbgView] (http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx) và nó sẽ ghi lại các bản ghi được gửi qua Windows API. sau đó bạn có thể phát các bản ghi sẽ được ghi lại trong công cụ, để duyệt và lọc dễ dàng – Liel

+0

Câu hỏi hay. Tôi cần thành phần này như là một phần của một ứng dụng WPF hiện có. Chúng tôi đã có một "giao diện điều khiển" được thực hiện như một TextBox bực bội chậm. Nhưng bây giờ chúng tôi cần các tính năng bổ sung mà tôi đã mô tả. Tôi rất vui khi sử dụng lại các thành phần không phải GPL thương mại hoặc miễn phí hiện có. – pixtur

Trả lời

145

Tôi nên bắt đầu bán các mẫu WPF này thay vì tặng chúng miễn phí. = P

enter image description here

  • Ảo hóa giao diện người dùng (Sử dụng VirtualizingStackPanel) mà cung cấp hiệu suất cực kỳ tốt (ngay cả với 200000+ bài)
  • Hoàn toàn MVVM thân thiện.
  • DataTemplate s cho mỗi loại loại LogEntry. Chúng cung cấp cho bạn khả năng tùy chỉnh theo số nhiều như bạn muốn. Tôi chỉ thực hiện 2 loại LogEntries (cơ bản và lồng nhau), nhưng bạn có được ý tưởng. Bạn có thể phân lớp LogEntry nhiều như bạn cần. Bạn thậm chí có thể hỗ trợ văn bản hoặc hình ảnh phong phú.
  • Các mục có thể mở rộng (Lồng nhau).
  • Từ gói.
  • Bạn có thể triển khai lọc, v.v. bằng cách sử dụng CollectionView.
  • Đá WPF, chỉ cần sao chép và dán mã của tôi vào một File -> New -> WPF Application và xem kết quả cho chính bạn.

    <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> 
    

Mã Đằng sau: (Chú ý rằng hầu hết nó chỉ được boileplate để hỗ trợ ví dụ (tạo mục ngẫu nhiên)

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() 
         }; 

     } 
    } 

Các mục dữ liệu:

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)); 
                    })); 
     } 
    } 
+7

Wow! Bạn vừa viết cái này à ?! Điều này thực sự tuyệt vời. Tôi chỉ thử nghiệm nó và nó khá nhiều câu trả lời hoàn hảo cho câu hỏi của tôi. Nó trông giống như thịt ra các chi tiết nên được thẳng foward. Tôi bị thổi bay đi. Cảm ơn rất nhiều! – pixtur

+0

Nếu bạn sẽ cho thuê để trả lời các câu hỏi như vậy một lần trong một thời gian, tôi sẽ rất vui khi được trả tiền. :-) – pixtur

+0

Một vấn đề tôi đã tìm thấy là khi tôi di chuyển con lăn chuột trên một mục lồng nhau, chế độ xem không cuộn. –

15

Câu trả lời HighCore là hoàn hảo, nhưng tôi đoán nó thiếu yêu cầu này: "khi ở cuối, nó sẽ tiếp tục cuộn khi các mục mới được thêm vào".

Theo this câu trả lời, bạn có thể làm điều này:

Trong ScrollViewer chính (bên DockPanel), thêm sự kiện:

<ScrollViewer CanContentScroll="True" ScrollChanged="ScrollViewer_ScrollChanged"> 

Cast nguồn sự kiện để làm cuộn tự động:

private bool AutoScroll = true; 
    private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) 
    { 
     // User scroll event : set or unset autoscroll mode 
     if (e.ExtentHeightChange == 0) 
     { // Content unchanged : user scroll event 
      if ((e.Source as ScrollViewer).VerticalOffset == (e.Source as ScrollViewer).ScrollableHeight) 
      { // Scroll bar is in bottom 
       // Set autoscroll mode 
       AutoScroll = true; 
      } 
      else 
      { // Scroll bar isn't in bottom 
       // Unset autoscroll mode 
       AutoScroll = false; 
      } 
     } 

     // Content scroll event : autoscroll eventually 
     if (AutoScroll && e.ExtentHeightChange != 0) 
     { // Content changed and autoscroll mode set 
      // Autoscroll 
      (e.Source as ScrollViewer).ScrollToVerticalOffset((e.Source as ScrollViewer).ExtentHeight); 
     } 
    } 
} 
+1

Biến AutoScroll là một ngoại lệ – alerya

+1

Cảm ơn. Đã sửa lỗi. – drizin

+0

Điều này không hoạt động và đôi khi mang lại cho tôi một ngoại lệ không có trên e.source – rolls

Các vấn đề liên quan