Install-Package Newtonsoft.Json
<Window.Resources>
<DataTemplate x:Key="DGDataTemplate"
DataType="{x:Type local:Book}">
<Border BorderBrush="LightGray"
BorderThickness="2"
Margin="5">
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="30"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Id:<Run Text="{Binding Id}"/></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1">Name:<Run Text="{Binding Name}"/></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0">ISBN:<Run Text="{Binding ISBN}"/></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1">CategoryName:<Run Text="{Binding CategoryName}"/></TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">Author:<Run Text="{Binding Author}"/></TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1">Comment:<Run Text="{Binding Comment}"/></TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0">Content:<Run Text="{Binding Content}"/></TextBlock>
<TextBlock Grid.Row="3" Grid.Column="1">Summary:<Run Text="{Binding Summary}"/></TextBlock>
<TextBlock Grid.Row="4" Grid.Column="0">Title:<Run Text="{Binding Title}"/></TextBlock>
<TextBlock Grid.Row="4" Grid.Column="1">Topic:<Run Text="{Binding Topic}"/></TextBlock>
</Grid>
</Border>
</DataTemplate>
<ControlTemplate x:Key="DGControlTemplate" TargetType="ContentControl">
<DataGrid ItemsSource="{Binding Books}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.CacheLength="5,5"
VirtualizingPanel.CacheLengthUnit="Item"
ScrollViewer.CanContentScroll="True"
ScrollViewer.IsDeferredScrollingEnabled="True"
AutoGenerateColumns="False"
CanUserAddRows="False"
SnapsToDevicePixels="True"
UseLayoutRounding="True">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<Binding Source="{StaticResource DGDataTemplate}"/>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ControlTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Template="{StaticResource DGControlTemplate}" DataContext="{Binding}"
Content="{Binding}"/>
<ContentControl Grid.Column="1" Template="{StaticResource DGControlTemplate}"/>
![image]()
![image]()
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
WindowState="Maximized">
<Window.DataContext>
<local:MainVM/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="DGDataTemplate"
DataType="{x:Type local:Book}">
<Border BorderBrush="LightGray"
BorderThickness="2"
Margin="5">
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="30"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Id:<Run Text="{Binding Id}"/></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1">Name:<Run Text="{Binding Name}"/></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0">ISBN:<Run Text="{Binding ISBN}"/></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1">CategoryName:<Run Text="{Binding CategoryName}"/></TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">Author:<Run Text="{Binding Author}"/></TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1">Comment:<Run Text="{Binding Comment}"/></TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0">Content:<Run Text="{Binding Content}"/></TextBlock>
<TextBlock Grid.Row="3" Grid.Column="1">Summary:<Run Text="{Binding Summary}"/></TextBlock>
<TextBlock Grid.Row="4" Grid.Column="0">Title:<Run Text="{Binding Title}"/></TextBlock>
<TextBlock Grid.Row="4" Grid.Column="1">Topic:<Run Text="{Binding Topic}"/></TextBlock>
</Grid>
</Border>
</DataTemplate>
<ControlTemplate x:Key="DGControlTemplate" TargetType="ContentControl">
<DataGrid ItemsSource="{Binding Books}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.CacheLength="5,5"
VirtualizingPanel.CacheLengthUnit="Item"
ScrollViewer.CanContentScroll="True"
ScrollViewer.IsDeferredScrollingEnabled="True"
AutoGenerateColumns="False"
CanUserAddRows="False"
SnapsToDevicePixels="True"
UseLayoutRounding="True">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<Binding Source="{StaticResource DGDataTemplate}"/>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ControlTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Template="{StaticResource DGControlTemplate}" DataContext="{Binding}"
Content="{Binding}"/>
<ContentControl Grid.Column="1" Template="{StaticResource DGControlTemplate}"/>
<!--<DataGrid ItemsSource="{Binding Books}"
Grid.Column="1"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.CacheLength="5,5"
VirtualizingPanel.CacheLengthUnit="Item"
ScrollViewer.CanContentScroll="True"
ScrollViewer.IsDeferredScrollingEnabled="True"
AutoGenerateColumns="False"
CanUserAddRows="False"
SnapsToDevicePixels="True"
UseLayoutRounding="True">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<Binding Source="{StaticResource DGDataTemplate}"/>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>-->
</Grid>
</Window>
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainVM : INotifyPropertyChanged
{
static HttpClient client = new HttpClient();
static string originUrl = "http://localhost:51436/BookService.svc/getbooks?cnt=";
public MainVM()
{
if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
_ = InitBooksAsync(1000000);
}
}
private async Task InitBooksAsync(int len)
{
string jsonStr = await client.GetStringAsync($"{originUrl}{len}");
var bks = JsonConvert.DeserializeObject<List<Book>>(jsonStr);
Books = new ObservableCollection<Book>(bks);
}
private ObservableCollection<Book> books;
public ObservableCollection<Book> Books
{
get
{
return books;
}
set
{
if (value != books)
{
books = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "")
{
var handler = Volatile.Read(ref this.PropertyChanged);
if (handler == null)
{
return;
}
handler.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public class Book
{
public long Id { get; set; }
public string Name { get; set; }
public string ISBN { get; set; }
public string CategoryName { get; set; }
public string Author { get; set; }
public string Comment { get; set; }
public string Content { get; set; }
public string Summary { get; set; }
public string Title { get; set; }
public string Topic { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfService1
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IBookService" in both code and config file together.
[ServiceContract]
public interface IBookService
{
[OperationContract]
[WebGet(UriTemplate = "/getbooks?cnt={cnt}",RequestFormat =WebMessageFormat.Json,ResponseFormat =WebMessageFormat.Json)]
List<Book> GetBooks(int cnt);
}
public class Book
{
public long Id { get; set; }
public string Name { get; set; }
public string ISBN { get; set; }
public string CategoryName { get; set; }
public string Author { get; set; }
public string Comment { get; set; }
public string Content { get; set; }
public string Summary { get; set; }
public string Title { get; set; }
public string Topic { get; set; }
}
enum BookCategoryEnum
{
Science,
Technology,
Engineering,
Math
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
namespace WcfService1
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "BookService" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select BookService.svc or BookService.svc.cs at the Solution Explorer and start debugging.
public class BookService : IBookService
{
private static long id = 1;
private static string[] enumNames = Enum.GetNames(typeof(BookCategoryEnum));
private static Random rnd=new Random();
int enumsCnt = enumNames.Count();
private static (long,long) GetStartEnd(long interval)
{
long end=Interlocked.Add(ref id, interval);
long start = end - interval;
return (start,end);
}
public List<Book> GetBooks(int cnt)
{
List<Book> bks = new List<Book>(cnt);
var(start,end) = GetStartEnd(cnt);
for(long i=start;i<end;i++)
{
bks.Add(new Book()
{
Id = i,
ISBN = $"ISBN_{i}_{Guid.NewGuid():N}",
CategoryName = $"{enumNames[i%enumsCnt]}",
Name = $"Name_{i}",
Author = $"Author_{i}",
Comment = $"Comment_{i}",
Content = $"Content_{i}",
Summary = $"Summary_{i}",
Title = $"Title_{i}",
Topic = $"Topic_{i}"
});
}
return bks;
}
}
}
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.8" />
<httpRuntime targetFramework="4.8"/>
</system.web>
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="BookServiceWebHttpBinding"
openTimeout="01:00:00"
closeTimeout="01:00:00"
sendTimeout="01:00:00"
receiveTimeout="01:00:00"
maxBufferPoolSize="2147483647"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<readerQuotas maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxDepth="2147483647"
maxNameTableCharCount="2147483647"
maxStringContentLength="2147483647"/>
<security mode="None"/>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="BookServiceBehavior" >
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="BookServiceEndPointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="WcfService1.BookService"
behaviorConfiguration="BookServiceBehavior">
<endpoint address=""
binding="webHttpBinding"
contract="WcfService1.IBookService"
behaviorConfiguration="BookServiceEndPointBehavior"
bindingConfiguration="BookServiceWebHttpBinding"/>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>