C# WPF Http Tool

By jerryxjr1220 at 2023-10-10 • 0人收藏 • 401人看过

练手项目,涉及MVVM数据绑定,RelayCommand命令绑定,跨线程Messenger传参等,Html解析用了HtmlAgility库,递归读取DocumentNode后画到TreeView里。

屏幕截图 2023-10-10 145514.png

using System;
using System.Net;
using CommunityToolkit.Mvvm.ComponentModel;

namespace WPFHttpTool.Models;

public partial class HttpClientModel : ObservableObject
{
    [ObservableProperty] private WebClient _client;

    [Obsolete("Obsolete")]
    public HttpClientModel()
    {
        Client = new WebClient();
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http.Json;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using WPFHttpTool.Models;
using WPFHttpTool.Views;

namespace WPFHttpTool.ViewModels;

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty] private HttpClientModel _clientModel;
    [ObservableProperty] private string? _method;
    [ObservableProperty] private BindingList<Tuple<string, string>> _requestHeaders;
    [ObservableProperty] private BindingList<Tuple<string, string>> _requestParameters;
    [ObservableProperty] private ResultPage _resultPage = new();
    [ObservableProperty] private string? _url;

    [Obsolete("Obsolete")]
    public MainViewModel()
    {
        ClientModel = new HttpClientModel();
        Url = string.Empty;
        Method = "GET";
        RequestHeaders = new BindingList<Tuple<string, string>>();
        RequestParameters = new BindingList<Tuple<string, string>>();

        RequestHeaders.Add(new Tuple<string, string>("User-Agent",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/"));

        #region 注册并接受消息

        WeakReferenceMessenger.Default.Register<AddRequestPage.CustomizedMessage>(this, (o, m) =>
        {
            RequestHeaders = m.viewmodel.RequestHeaders;
            RequestParameters = m.viewmodel.RequestParameters;
        });

        #endregion
    }

    [RelayCommand]
    public async Task RecievedAsString()
    {
        ClientModel.Client.Headers.Clear();
        switch (Method)
        {
            case "GET":
                foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2);

                string? parStr = null;
                var sb = new StringBuilder();
                foreach (var par in RequestParameters) sb.Append(par.Item1 + "=" + par.Item2 + "&");

                if (sb.Length > 1) parStr = sb.ToString()[..^1];
                var resp = await ClientModel.Client.DownloadStringTaskAsync(Url + "?" + parStr);
                ResultPage = new ResultPage();
                ResultPage.RenderPage(ClientModel, resp, null);
                ResultPage.Show();
                break;
            case "POST":
                foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2);

                var pars = new List<string>();
                foreach (var par in RequestParameters) pars.Add(par.Item2);

                var jObject = JsonContent.Create(pars);
                var resp2 = await ClientModel.Client.UploadStringTaskAsync(Url!, jObject?.ToString() ?? string.Empty);
                ResultPage = new ResultPage();
                ResultPage.RenderPage(ClientModel, resp2, null);
                ResultPage.Show();
                break;
        }
    }

    [RelayCommand]
    public async Task RecievedAsBytes()
    {
        ClientModel.Client.Headers.Clear();
        switch (Method)
        {
            case "GET":
                foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2);

                string? parStr = null;
                var sb = new StringBuilder();
                foreach (var par in RequestParameters) sb.Append(par.Item1 + "=" + par.Item2 + "&");

                if (sb.Length > 1) parStr = sb.ToString()[..^1];
                var resp = await ClientModel.Client.DownloadDataTaskAsync(new Uri(Url + "?" + parStr));
                ResultPage = new ResultPage();
                ResultPage.RenderPage(ClientModel, null, resp);
                ResultPage.Show();
                break;
            case "POST":
                foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2);

                var pars = new List<string>();
                foreach (var par in RequestParameters) pars.Add(par.Item2);

                var jObject = JsonContent.Create(pars);
                var resp2 = await ClientModel.Client.UploadStringTaskAsync(Url!, jObject?.ToString() ?? string.Empty);
                ResultPage = new ResultPage();
                ResultPage.RenderPage(ClientModel, resp2, null);
                ResultPage.Show();
                break;
        }
    }
}
<hc:GlowWindow x:Class="WPFHttpTool.Views.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:vm="clr-namespace:WPFHttpTool.ViewModels"
               xmlns:hc="https://handyorg.github.io/handycontrol"
               d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
               mc:Ignorable="d"
               Title="				
4 个回复 | 最后更新于 2023-10-11
2023-10-10   #1
<hc:GlowWindow x:Class="WPFHttpTool.Views.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:vm="clr-namespace:WPFHttpTool.ViewModels"
               xmlns:hc="https://handyorg.github.io/handycontrol"
               d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
               mc:Ignorable="d"
               Title="Http Tool" MinHeight="500" MinWidth="800" Width="900" Height="500"
               FontFamily="JetBrains Mono"
               FontSize="{StaticResource TextFontSize}">
    <Window.Resources>
    </Window.Resources>
    <hc:SimplePanel>
        <ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="6*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <WrapPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                           HorizontalAlignment="Center" VerticalAlignment="Center"
                           Width="800">
                    <hc:ComboBox x:Name="cbMethod" AutoComplete="True" SelectedIndex="0" FontSize="20" Width="100"
                                 Style="{StaticResource ComboBoxExtend}"
                                 SelectionChanged="CbMethod_OnSelectionChanged">
                        <ComboBoxItem Content="GET" Style="{StaticResource ComboBoxItemCapsuleVerticalFirst}" />
                        <ComboBoxItem Content="POST" Style="{StaticResource ComboBoxItemCapsuleVerticalLast}" />
                    </hc:ComboBox>
                    <hc:TextBox hc:InfoElement.Title="URL:" hc:InfoElement.TitlePlacement="Left"
                                hc:InfoElement.Necessary="True" hc:InfoElement.Placeholder="Http(s)://"
                                Text="{Binding Url}"
                                FontSize="20" Width="480" Margin="16,0" Style="{StaticResource TextBoxExtend}" />
                    <Button Content="As String" Style="{StaticResource ButtonPrimary}" Margin="5"
                            Command="{Binding RecievedAsStringCommand}" />
                    <Button Content="As Bytes" Style="{StaticResource ButtonPrimary}" Margin="5"
                            Command="{Binding RecievedAsBytesCommand}" />
                </WrapPanel>
                <ScrollViewer Grid.Row="1" Grid.Column="0" VerticalScrollBarVisibility="Auto">
                    <WrapPanel>
                        <TextBlock Text="请求头" VerticalAlignment="Center" Margin="10"
                                   Style="{StaticResource TextBlockSubTitle}" />
                        <Button Content="新增请求表头" Style="{StaticResource ButtonPrimary}" VerticalAlignment="Center"
                                Click="ButtonAddRequest_OnClick" x:Name="ButtonAddHeader" />
                        <DataGrid Style="{StaticResource DataGridBaseStyle}" HorizontalContentAlignment="Center"
                                  MinWidth="450" ItemsSource="{Binding RequestHeaders}" AutoGenerateColumns="False">
                            <DataGrid.Columns>
                                <DataGridTextColumn Header="Keys" Width="150" Binding="{Binding Item1}"
                                                    IsReadOnly="True" />
                                <DataGridTextColumn Header="Values" Binding="{Binding Item2}" IsReadOnly="True" />
                            </DataGrid.Columns>
                        </DataGrid>
                    </WrapPanel>

                </ScrollViewer>
                <ScrollViewer Grid.Row="1" Grid.Column="1" VerticalScrollBarVisibility="Auto">
                    <WrapPanel>
                        <TextBlock Text="请求参数" VerticalAlignment="Center" Margin="10"
                                   Style="{StaticResource TextBlockSubTitle}" />
                        <Button Content="新增请求参数" Style="{StaticResource ButtonPrimary}" VerticalAlignment="Center"
                                Click="ButtonAddRequest_OnClick" x:Name="ButtonAddParameter" />
                        <DataGrid Style="{StaticResource DataGridBaseStyle}" HorizontalContentAlignment="Center"
                                  ItemsSource="{Binding RequestParameters}"
                                  MinWidth="450" AutoGenerateColumns="False">
                            <DataGrid.Columns>
                                <DataGridTextColumn Header="Key" Width="150" Binding="{Binding Item1}"
                                                    IsReadOnly="True" />
                                <DataGridTextColumn Header="Value" Binding="{Binding Item2}" IsReadOnly="True" />
                            </DataGrid.Columns>
                        </DataGrid>
                    </WrapPanel>
                </ScrollViewer>
            </Grid>
        </ScrollViewer>
    </hc:SimplePanel>
</hc:GlowWindow>
using System;
using System.Windows;
using System.Windows.Controls;
using HandyControl.Controls;
using WPFHttpTool.ViewModels;

namespace WPFHttpTool.Views;

/// <summary>
///     Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : GlowWindow
{
    private readonly MainViewModel mainViewModel;

    public MainWindow()
    {
        InitializeComponent();

        mainViewModel = new MainViewModel();

        DataContext = mainViewModel;
    }

    private void ButtonAddRequest_OnClick(object sender, RoutedEventArgs e)
    {
        var b = sender as Button;
        var addRequestPage = new AddRequestPage(mainViewModel, b.Name);
        addRequestPage.Show();
    }


    private void CbMethod_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        try
        {
            switch (cbMethod.SelectedIndex)
            {
                case 0:
                    mainViewModel.Method = "GET";
                    break;
                case 1:
                    mainViewModel.Method = "POST";
                    break;
            }
        }
        catch (Exception ex)
        {
            //ignored
        }
    }
}


2023-10-10   #2
<Window x:Class="WPFHttpTool.Views.AddRequestPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        xmlns:vm="clr-namespace:WPFHttpTool.ViewModels"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
        Title="Add..." Height="280" Width="400" ResizeMode="NoResize">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBox x:Name="tbKey" Width="300" Margin="10"
                 hc:InfoElement.Title="Key" hc:InfoElement.TitlePlacement="Left" />
        <TextBox x:Name="tbValue" Width="300" Height="150" MaxLines="6" VerticalContentAlignment="Top"
                 hc:InfoElement.Title="Value" hc:InfoElement.TitlePlacement="Left" />
        <Button Content="Submit" Style="{StaticResource ButtonPrimary}" Margin="5"
                Click="ButtonSubmit_OnClick" />
    </StackPanel>
</Window>
using System;
using System.Windows;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using WPFHttpTool.ViewModels;

namespace WPFHttpTool.Views;

public partial class AddRequestPage : Window
{
    private readonly string _buttonName;
    private readonly MainViewModel _mainViewModel;

    public AddRequestPage(MainViewModel _viewModel, string _name)
    {
        InitializeComponent();
        _mainViewModel = _viewModel;
        _buttonName = _name;
    }

    private void ButtonSubmit_OnClick(object sender, RoutedEventArgs e)
    {
        if (_buttonName == "ButtonAddHeader")
            _mainViewModel.RequestHeaders.Add(new Tuple<string, string>(tbKey.Text, tbValue.Text));
        else
            _mainViewModel.RequestParameters.Add(new Tuple<string, string>(tbKey.Text, tbValue.Text));

        var cm = new CustomizedMessage(_mainViewModel);
        WeakReferenceMessenger.Default.Send(cm);
        Close();
    }

    //定义消息
    public class
        CustomizedMessage : ValueChangedMessage<MainViewModel> // PropertyChangedMessage // RequestMessage ( Async + ..)
    {
        public MainViewModel viewmodel;

        public CustomizedMessage(MainViewModel value) : base(value)
        {
            viewmodel = value;
        }
    }
}


2023-10-10   #3
<Window x:Class="WPFHttpTool.Views.ResultPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WPFHttpTool.Views"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance Type=local:ResultPage}"
        Title="Received Response" Height="850" Width="800" ResizeMode="NoResize">
    <StackPanel>
        <hc:Shield Status="Headers" Subject="Response" Color="Green" Margin="10,5" />
        <DataGrid ItemsSource="{Binding RespHeaders}" AutoGenerateColumns="False" IsReadOnly="True"
                  VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Height="350" Width="780">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Key" Binding="{Binding  Item1 }" />
                <DataGridTextColumn Header="Value" Binding="{Binding Item2 }" />
            </DataGrid.Columns>
        </DataGrid>
        <WrapPanel>
            <hc:Shield Status="Html" Subject="Response" Color="Green" Margin="10,5" />
            <Button Content="Parse" Style="{StaticResource ButtonSuccess.Small}"
                    Command="{Binding ParseHtmlCommand}" />
        </WrapPanel>

        <TextBox MaxLines="21" VerticalScrollBarVisibility="Auto" IsReadOnly="True" Text="{Binding ReceivedString}"
                 TextWrapping="Wrap" />

        <hc:Shield Status="Bytes Length" Subject="Response" Color="Green" Margin="10,5" />
        <TextBox IsReadOnly="True" Text="{Binding ReceivedBytesLength}" />
    </StackPanel>
</Window>
using System;
using System.Collections.ObjectModel;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HtmlAgilityPack;
using WPFHttpTool.Models;

namespace WPFHttpTool.Views;

[ObservableObject]
public partial class ResultPage : Window
{
    [ObservableProperty] private HttpClientModel? _clientModel;
    [ObservableProperty] private byte[] _receivedBytes = Array.Empty<byte>();
    [ObservableProperty] private int _receivedBytesLength;
    [ObservableProperty] private string? _receivedString;
    [ObservableProperty] private ObservableCollection<Tuple<string, string>> _respHeaders;

    public ResultPage()
    {
        InitializeComponent();
        RespHeaders = new ObservableCollection<Tuple<string, string>>();

        DataContext = this;
    }

    public void RenderPage(HttpClientModel _httpClientModel, string? receivedString, byte[]? receivedBytes)
    {
        ClientModel = _httpClientModel;
        ReceivedString = receivedString;
        if (receivedBytes is not null) ReceivedBytes = receivedBytes;
        ReceivedBytesLength = ReceivedBytes.Length;
        var keys = ClientModel.Client.ResponseHeaders?.AllKeys;
        foreach (var key in keys)
        {
            var value = ClientModel.Client.ResponseHeaders?.GetValues(key)?[0];
            if (value is not null) RespHeaders.Add(new Tuple<string, string>(key, value));
        }
    }

    [RelayCommand]
    public void ParseHtml()
    {
        var htmlDocument = new HtmlDocument();
        htmlDocument.LoadHtml(ReceivedString);
        var treeViewPage = new TreeViewPage(htmlDocument);
        treeViewPage.Show();
    }
}
<Window x:Class="WPFHttpTool.Views.TreeViewPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        mc:Ignorable="d"
        Title="TreeViewPage" Height="800" Width="800">
    <Grid>
        <TreeView x:Name="_treeView" />
    </Grid>
</Window>
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using HtmlAgilityPack;

namespace WPFHttpTool.Views;

[ObservableObject]
public partial class TreeViewPage : Window
{
    [ObservableProperty] private HtmlDocument _htmlDocument;

    public TreeViewPage(HtmlDocument htmlDocument)
    {
        InitializeComponent();
        _htmlDocument = htmlDocument;
        MapHtmlToTreeView(_htmlDocument.DocumentNode);
    }

    public void MapHtmlToTreeView(HtmlNode htmlNode, TreeViewItem parentItem = null)
    {
        foreach (var childNode in htmlNode.ChildNodes)
            if (childNode.NodeType == HtmlNodeType.Element)
            {
                var treeViewItem = new TreeViewItem();
                treeViewItem.Header = childNode.Name;
                foreach (var attr in childNode.Attributes) treeViewItem.Header += $" {attr.Name} : {attr.Value} ;";

                if (parentItem != null)
                    parentItem.Items.Add(treeViewItem);
                else
                    _treeView.Items.Add(treeViewItem);

                MapHtmlToTreeView(childNode, treeViewItem);
            }
            else if (childNode.NodeType == HtmlNodeType.Text)
            {
                var text = childNode.InnerHtml.Trim();
                if (!string.IsNullOrEmpty(text))
                {
                    var textBlock = new TextBlock();
                    textBlock.Text = text;

                    if (parentItem != null)
                        parentItem.Items.Add(textBlock);
                    else
                        _treeView.Items.Add(textBlock);
                }
            }
    }
}


2023-10-11   #4

增加了Json字符串解析功能,可以抓取Json API的返回值,直接解析,自动判断类型,并绘制TreeView展示。

用了NewtonSoft.Json库进行Json字符串的解析。

屏幕截图 2023-10-11 084414.png

using System;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using HtmlAgilityPack;
using Newtonsoft.Json.Linq;

namespace WPFHttpTool.Views;

[ObservableObject]
public partial class TreeViewPage : Window
{
    [ObservableProperty] private HtmlDocument? _htmlDocument;

    public TreeViewPage(HtmlDocument htmlDocument)
    {
        InitializeComponent();
        _htmlDocument = htmlDocument;
        MapHtmlToTreeView(_htmlDocument.DocumentNode);
    }

    public TreeViewPage(JObject jObject)
    {
        InitializeComponent();
        MapJsonToTreeView(jObject);
    }
    
    public TreeViewPage(JArray jArray)
    {
        InitializeComponent();
        MapJsonToTreeView(jArray);
    }

    public void MapHtmlToTreeView(HtmlNode htmlNode, TreeViewItem parentItem = null)
    {
        foreach (var childNode in htmlNode.ChildNodes)
        {
            if (childNode.NodeType == HtmlNodeType.Element)
            {
                var treeViewItem = new TreeViewItem();
                treeViewItem.Header = childNode.Name;
                foreach (var attr in childNode.Attributes) treeViewItem.Header += $" {attr.Name} : {attr.Value} ;";

                if (parentItem != null)
                    parentItem.Items.Add(treeViewItem);
                else
                    _treeView.Items.Add(treeViewItem);

                MapHtmlToTreeView(childNode, treeViewItem);
            }
            else if (childNode.NodeType == HtmlNodeType.Text)
            {
                var text = childNode.InnerHtml.Trim();
                if (!string.IsNullOrEmpty(text))
                {
                    var textBlock = new TextBlock();
                    textBlock.Text = text;

                    if (parentItem != null)
                        parentItem.Items.Add(textBlock);
                    else
                        _treeView.Items.Add(textBlock);
                }
            }
        }
    }

    public void MapJsonToTreeView(JObject jObject, TreeViewItem parentItem = null)
    {
        foreach (var (key, value) in jObject)
        {
            var treeViewItem = new TreeViewItem();
            treeViewItem.Header = key;

            try
            {
                var children = value!.ToObject<JObject>();
                MapJsonToTreeView(children!, treeViewItem);
            }
            catch (Exception ex)
            {
                try
                {
                    var children = value!.ToObject<JArray>();
                    MapJsonToTreeView(children!, treeViewItem);
                }
                catch (Exception ex2)
                {
                    var child = value!.ToObject<string>();

                    treeViewItem.Header += $" : {child}, ";
                }
                
            }
            if (parentItem != null)
                parentItem.Items.Add(treeViewItem);
            else
                _treeView.Items.Add(treeViewItem);
        }
    }
    
    public void MapJsonToTreeView(JArray jArray, TreeViewItem parentItem = null)
    {
        foreach (var jtoken in jArray)
        {
            try
            {
                var children = jtoken.ToObject<JObject>();
                MapJsonToTreeView(children!, parentItem);
            }
            catch (Exception ex)
            {
                try
                {
                    var children = jtoken.ToObject<JArray>();
                    MapJsonToTreeView(children!, parentItem);
                }
                catch (Exception ex2)
                {
                    var child = jtoken.ToObject<string>();

                    if (parentItem != null)
                        parentItem.Header += $" {child},";
                    else
                        _treeView.Items.Add(new TextBlock { Text = child });
                }
            }
        }
    }
    
}


登录后方可回帖

登 录
信息栏
 私人小站

本站域名

ChengXu.XYZ

投诉联系:  popdes@126.com



快速上位机开发学习,本站主要记录了学习过程中遇到的问题和解决办法及上位机代码分享

这里主要专注于学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.
如果侵权,联系 Popdes@126.com

友情链接
Aardio官方
Aardio资源网


才仁机械


网站地图SiteMap

Loading...