WPF Custom Control binding collection with command, and detailed data service provider ASP.Net Core WebAPI code

Install-Package Newtonsoft.json

  

//D:\C\WpfApp16\Models\Book.cs
using System;
using System.Collections.Generic;
using System.Text;

namespace WpfApp16.Models
{
    public class Book
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string ISBN { 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; }
        public string CategoryName { get; set; }
    }
}

//D:\C\WpfApp16\Models\DelegateCommand.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace WpfApp16.Models
{
    public class DelegateCommand : ICommand
    {
        private readonly Action<object?> execute;
        private readonly Func<object?, bool>? canExecute;

        public DelegateCommand(Action<Object?> executeValue, Func<object?, bool>? canExecuteValue = null)
        {
            execute = executeValue;
            canExecute = canExecuteValue;
        }

        public event EventHandler? CanExecuteChanged;
        public void RaiseCanExecuteChanged()
        {
            var handler = Volatile.Read(ref CanExecuteChanged);
            if (handler != null)
            {
                var dispatcher = Application.Current?.Dispatcher;
                if (dispatcher != null)
                {
                    if (dispatcher.CheckAccess())
                    {
                        handler(this, EventArgs.Empty);
                    }
                    else
                    {
                        dispatcher.Invoke(() =>
                        {
                            handler(this, EventArgs.Empty);
                        }, System.Windows.Threading.DispatcherPriority.Background);
                    }
                }
            }
        }

        public bool CanExecute(object? parameter)
        {
            return canExecute == null ? true : canExecute(parameter);
        }

        public void Execute(object? parameter)
        {
            execute?.Invoke(parameter);
        }
    }
}


//D:\C\WpfApp16\DGControl1.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
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;
using WpfApp16.Models;

namespace WpfApp16
{
    /// <summary>
    /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
    ///
    /// Step 1a) Using this custom control in a XAML file that exists in the current project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WpfApp16"
    ///
    ///
    /// Step 1b) Using this custom control in a XAML file that exists in a different project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WpfApp16;assembly=WpfApp16"
    ///
    /// You will also need to add a project reference from the project where the XAML file lives
    /// to this project and Rebuild to avoid compilation errors:
    ///
    ///     Right click on the target project in the Solution Explorer and
    ///     "Add Reference"->"Projects"->[Browse to and select this project]
    ///
    ///
    /// Step 2)
    /// Go ahead and use your control in the XAML file.
    ///
    ///     <MyNamespace:DGControl1/>
    ///
    /// </summary>
    public class DGControl1 : Control
    {
        static DGControl1()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DGControl1), new FrameworkPropertyMetadata(typeof(DGControl1)));
        }



        public ICommand LoadDataCommand
        {
            get { return (ICommand)GetValue(LoadDataCommandProperty); }
            set { SetValue(LoadDataCommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LoadDataCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LoadDataCommandProperty =
            DependencyProperty.Register(nameof(LoadDataCommand),
                typeof(ICommand), 
                typeof(DGControl1),
                new PropertyMetadata(null));





        public ICommand ClearDataCommand
        {
            get { return (ICommand)GetValue(ClearDataCommandProperty); }
            set { SetValue(ClearDataCommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ClearDataCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ClearDataCommandProperty =
            DependencyProperty.Register(nameof(ClearDataCommand), 
                typeof(ICommand), 
                typeof(DGControl1), 
                new PropertyMetadata(null));







        public ObservableCollection<Book> BksCollection
        {
            get { return (ObservableCollection<Book>)GetValue(BksCollectionProperty); }
            set { SetValue(BksCollectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BksCollection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BksCollectionProperty =
            DependencyProperty.Register(nameof(BksCollection), 
                typeof(ObservableCollection<Book>), 
                typeof(DGControl1), 
                new PropertyMetadata(null));



    }
}

//D:\C\WpfApp16\Themes\Generic.xaml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApp16">


    <Style TargetType="{x:Type local:DGControl1}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:DGControl1}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Border Margin="5">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <StackPanel Orientation="Horizontal" Grid.Row="0">
                                    <StackPanel.Resources>
                                        <Style TargetType="Button">
                                            <Setter Property="Width" Value="150"/>
                                            <Setter Property="FontSize" Value="30"/>
                                            <Setter Property="Margin" Value="10,0"/>
                                        </Style>
                                    </StackPanel.Resources>
                                    <Button Content="Load"
                                            Command="{TemplateBinding LoadDataCommand}" />
                                    <Button Content="Clear"
                                            Command="{TemplateBinding ClearDataCommand}" />
                                </StackPanel>

                                <DataGrid Grid.Row="1"
                                          ItemsSource="{Binding BksCollection,RelativeSource={RelativeSource AncestorType=local:DGControl1}}"
                                          VirtualizingPanel.IsVirtualizing="True"
                                          VirtualizingPanel.VirtualizationMode="Recycling"
                                          VirtualizingPanel.CacheLength="5,5"
                                          VirtualizingPanel.CacheLengthUnit="Item"
                                          ScrollViewer.CanContentScroll="True"
                                          ScrollViewer.IsDeferredScrollingEnabled="True"
                                          UseLayoutRounding="True"
                                          HeadersVisibility="Column"
                                          BorderBrush="Cyan"
                                          BorderThickness="2"
                                          Margin="5"
                                          AutoGenerateColumns="True"
                                          CanUserAddRows="False"
                                          FontSize="20"/>
                            </Grid>
                        </Border>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>


//D:\C\WpfApp16\MainWindow.xaml
<Window x:Class="WpfApp16.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:WpfApp16"
        mc:Ignorable="d"
        Title="{Binding MainTitle}" 
        WindowState="Maximized">
    <Window.DataContext>
        <local:MainVM/>
    </Window.DataContext>
    <Grid>
        <local:DGControl1 BksCollection="{Binding BooksCollection}"
                          LoadDataCommand="{Binding LoadCommand}"
                          ClearDataCommand="{Binding ClearCommand}"/>
    </Grid>
</Window>

//D:\C\WpfApp16\MainWindow.xaml.cs
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
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;
using WpfApp16.Models;

namespace WpfApp16
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class MainVM : INotifyPropertyChanged
    {
        private static string url = "http://192.168.1.142:5000/getbooks/1000000";
        private static HttpClient client = new HttpClient()
        {
            Timeout = TimeSpan.FromMinutes(1)
        };

        private static bool isLoading = false;


        public MainVM()
        {
            if(!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                _ = LoadDataAsync();
            }
        }

        private ICommand clearCommand;
        public ICommand ClearCommand
        {
            get
            {
                if (clearCommand == null)
                {
                    clearCommand = new DelegateCommand(ClearCommandExecuted);
                }
                return clearCommand;
            }
        }

        private void ClearCommandExecuted(object? obj)
        {
            BooksCollection?.Clear();
            MainTitle = $"{DateTime.Now},cleared data!";
        }

        private ICommand loadCommand;
        public ICommand LoadCommand
        {
            get
            {
                if(loadCommand==null)
                {
                    loadCommand = new DelegateCommand(LoadCommandExecuted);
                }
                return loadCommand;
            }
        }

        private void LoadCommandExecuted(object? obj)
        {
            _ = LoadDataAsync();
        }

        private async Task LoadDataAsync()
        {
            if(isLoading)
            {
                return;
            }
            isLoading = true;
            MainTitle = $"{DateTime.Now},loading...";
            try
            {
                string jsonStr=await client.GetStringAsync(url);
                if(!string.IsNullOrWhiteSpace(jsonStr))
                {
                    var bks = JsonConvert.DeserializeObject<List<Book>>(jsonStr);
                    if(bks!=null && bks.Any())
                    {
                        BooksCollection = new ObservableCollection<Book>(bks);
                        MainTitle = $"{DateTime.Now},FirstId:{bks.FirstOrDefault()?.Id}," +
                            $"LastId:{bks.LastOrDefault()?.Id}";
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex?.Message);
            }
            finally
            {
                isLoading = false;
            }
        }

        private string mainTitle = $"{DateTime.Now}";
        public string MainTitle
        {
            get
            {
                return mainTitle;
            }
            set
            {
                if(value!=mainTitle)
                {
                    mainTitle = value;
                    OnPropertyChanged();
                }
            }
        }

        private ObservableCollection<Book> bookksCollection;
        public ObservableCollection<Book> BooksCollection
        {
            get
            {
                return bookksCollection;
            }
            set
            {
                if(value!=bookksCollection)
                {
                    bookksCollection = value;
                    OnPropertyChanged();
                }
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "")
        {
            var handler = Volatile.Read(ref PropertyChanged);
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propName));
            }
        }
    }

   
}

  

 

 

 

image

 

 

 

 

 

 

image

 

 

 

image

 

 

//ASP.Net Core WebAPI

//D:\C\WebApplication8\Models\Book.cs
namespace WebApplication8.Models
{
    public class Book
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string ISBN { 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; }
        public string CategoryName { get; set; }
    }

    enum BookCategory
    {
        Science, Technology, Engineering, Math
    }
}



//D:\C\WebApplication8\Controllers\BooksController.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net;
using WebApplication8.Models;

namespace WebApplication8.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        private static string[] enumNames = Enum.GetNames(typeof(BookCategory));
        private static int enumLen = enumNames.Length;
        private static long id = 1;

        private static (long, long) GetStartEnd(int cnt = 1000000)
        {
            long end = Interlocked.Add(ref id, cnt);
            long start = end - cnt;
            return (start, end);
        }

        [HttpGet("/getbooks/{cnt}")]
        public List<Book> GetBooks(int cnt = 1000000)
        {
            List<Book> bksList = new List<Book>(cnt);
            var (start, end) = GetStartEnd(cnt);
            for (long i = start; i < end; i++)
            {
                bksList.Add(new Book()
                {
                    Id = i,
                    Name = $"Name_{i}",
                    ISBN = $"ISBN_{i}_{Guid.NewGuid():N}",
                    CategoryName = $"{enumNames[i % enumLen]}",
                    Comment = $"Comment_{i}",
                    Content = $"Content_{i}",
                    Summary = $"Summary_{i}",
                    Author = $"Author_{i}",
                    Title = $"Title_{i}",
                    Topic = $"Topic_{i}"
                });
            }
            Console.WriteLine($"In WebAPI,{DateTime.Now},FirstId:{start},last:{end - 1}");
            return bksList;
        }
    }
}


//D:\C\WebApplication8\Program.cs

namespace WebApplication8
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            builder.WebHost.UseKestrel(x =>
            {
                x.ListenAnyIP(5000);
                x.ListenAnyIP(5001, o =>
                {
                    o.UseHttps();
                });
            });
            // Add services to the container.

            builder.Services.AddControllers();
            // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
            builder.Services.AddOpenApi();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.MapOpenApi();
            }

            //app.UseHttpsRedirection();

            app.UseAuthorization();


            app.MapControllers();

            app.Run();
        }
    }
}

 

http://192.168.1.142:5000/getbooks/100000

  

 

image

 

 

 

image

 

 

//Consumer

using Newtonsoft.Json;
using System.Collections.ObjectModel;

namespace ConsoleApp5
{
    internal class Program
    {
        private static string url = "http://192.168.1.142:5000/getbooks/1000000";
        private static HttpClient client = new HttpClient()
        {
            Timeout = TimeSpan.FromMinutes(1)
        };

        private static bool isLoading = false;
        static void Main(string[] args)
        {
            _ = AutoLoadDataAsync();
            Console.ReadLine();
        }

        private static async Task AutoLoadDataAsync()
        {
            while(true)
            {
                _ = LoadDataAsync();
                await Task.Delay(1000);
            }
        }

        private static async Task LoadDataAsync()
        {
            if (isLoading)
            {
                return;
            }
            isLoading = true;
            Console.WriteLine($"{DateTime.Now},loading...");

            try
            {
                string jsonStr = await client.GetStringAsync(url);
                if (!string.IsNullOrWhiteSpace(jsonStr))
                {
                    var bks = JsonConvert.DeserializeObject<List<Book>>(jsonStr);
                    if (bks != null && bks.Any())
                    {                        
                       string MainTitle = $"{DateTime.Now},FirstId:{bks.FirstOrDefault()?.Id}," +
                            $"LastId:{bks.LastOrDefault()?.Id}";
                        Console.WriteLine(MainTitle);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex?.Message);
            }
            finally
            {
                isLoading = false;
            }
        }
    }

    public class Book
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string ISBN { 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; }
        public string CategoryName { get; set; }
    }
}

  

 

 

image

 

 

image

 

posted @ 2026-10-05 21:41  FredGrit  阅读(2)  评论(0)    收藏  举报