Fork me on GitHub

WinForm(十一)用户控件

  winform+api是一种对接数据模式,在本例中,为了方便,开发两个天生具有与后台api对接的用例控件,并且把他们封闭成单独控件,以供多次使用。

  提示:此实例不是生产使用,仅作演示,有不完善地方,重点关注流程和自定义控件。

  本例为API Server,用户控件项目,使用控件项目。

  API Server端:

  api端主要是根据提交上来的请求,组装成一个sql,查询一个内存sqlite数据库(请注意这里没有做sql注入处理)

using Dapper;
using Microsoft.Data.Sqlite;
using System.Data;
using System.Data.Common;
using System.Net;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using System.Web;

var builder = WebApplication.CreateBuilder(args);

var connection = new SqliteConnection("Data Source=InMemorySample;Mode=Memory;Cache=Shared");

await InitDataAsync();
async Task InitDataAsync()
{
    await connection.OpenAsync();
    await connection.ExecuteAsync("create table type(ID INTEGER primary key,Name text,Category INTEGER);");
    await connection.ExecuteAsync("insert into main.type(ID,Name,Category) values(1,'AliPay',1),(2,'MiroPay',1),(3,'PayPay',2);");
    await connection.ExecuteAsync("create table [order](ID INTEGER primary key,Name text,Price real,Quantity integer);");
    await connection.ExecuteAsync("insert into [order](ID,Name,Price,Quantity) values(1,'产品A',21.15,12),(2,'产品B',32.45,23),(3,'产品C',43.45,23);");
}
builder.Services.AddSingleton<IDbConnection>(connection);
builder.Services.AddResponseCaching();
builder.Services.AddSingleton<IParameService, ParameService>();

var app = builder.Build();

app.UseResponseCaching();
app.Use(async (context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl =
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            Public = true,
            MaxAge = TimeSpan.FromHours(1)
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
        new string[] { "Accept-Encoding" };
    await next();
});

app.MapGet("/parame/{dataSource}", async (IParameService parameService, string dataSource, string fields, string conditions) =>
{
    var list = await parameService.GetParamesAsync(dataSource, fields, conditions);
    return TypedResults.Json(list, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = false });
});
app.Run();

public interface IParameService
{
    Task<IEnumerable<dynamic>> GetParamesAsync(string dataSource, string fields, string conditions);
}
public class ParameService : IParameService
{
    private readonly ILogger<ParameService> _logger;
    private readonly IDbConnection _db;
    public ParameService(ILogger<ParameService> logger, IDbConnection db)
    {
        _logger = logger;
        _db = db;
    }
    public async Task<IEnumerable<dynamic>> GetParamesAsync(string dataSource, string fields, string conditions)
    {  
        var sql = $"select {fields} from [{dataSource}]";
        if (conditions != null && conditions.Length > 0)
        {
            var whereBuilder = new StringBuilder(" where ");
            var conditionArr = conditions.Split(new string[] { "(", "),(", ")" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var condition in conditionArr)
            {
                var arr = condition.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                whereBuilder.Append($" {arr[0]} {arr[1]} '{arr[2]}' and");
            }
            sql += whereBuilder.ToString().Substring(0, whereBuilder.Length - 3);
        }
        _logger.LogInformation(sql);
        return await _db.QueryAsync<dynamic>(sql);   
    }
}

  用户控件端:

  用户控件有一个公共部分,分别处理两类控件ListControl和DataGridView控件,思路都是从控件上拿到相应的属性数据,然后组装http请求,取回数据,绑定到控件上。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing.Design;
using System.Linq;
using System.Net.Http;
using System.Security.Policy;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;

namespace GSWControls
{
    public static class ControlExpand
    {
        static HttpClient _httpClient = new HttpClient();
        public static async Task DBControlInit(this ListControl control, string url, string dataSourceName, List<DBCondition>? conditions)
        {
            if (!control.IsAncestorSiteInDesignMode)
            {
                if (!string.IsNullOrWhiteSpace(url) && !string.IsNullOrWhiteSpace(dataSourceName) && !string.IsNullOrWhiteSpace(control.DisplayMember) && !string.IsNullOrWhiteSpace(control.ValueMember))
                {
                    url = $"{url.TrimEnd('/', '\\')}/{dataSourceName}?fields={Uri.EscapeDataString(control.ValueMember)},{Uri.EscapeDataString(control.DisplayMember)}";
                    if (conditions != null && conditions.Count > 0)
                    {
                        var arr = conditions.Select(s => $"({s.Name},{s.Symbol},{s.Value})").ToArray();
                        url += "&conditions=" + Uri.EscapeDataString(string.Join(',', arr));
                    }
                    var content = await _httpClient.GetStringAsync(url);
                    var table = JsonToDataTable(content);
                    control.DataSource = table;
                }
            }
        }
        static DataTable JsonToDataTable(string json)
        {
            var table = new DataTable();
            var list = JsonSerializer.Deserialize<IList<Dictionary<string, dynamic>>>(json);
            var columns = list?.First().Select(d => d.Key);
            if (list != null && columns != null)
            {
                foreach (var item in columns)
                {
                    table.Columns.Add(item);
                }
                foreach (var item in list)
                {
                    table.Rows.Add(item.Values.ToArray());
                }
            }
            return table;
        }

        public static async Task DBGridInit(this DataGridView control, string url, string dataSourceName, List<DBCondition>? conditions)
        {
            if (!control.IsAncestorSiteInDesignMode)
            {
                if (!string.IsNullOrWhiteSpace(url) && !string.IsNullOrWhiteSpace(dataSourceName) && control.Columns.Count > 0)
                {
                    var fieldList = new List<string>();
                    foreach (DataGridViewColumn column in control.Columns)
                    {
                        fieldList.Add(column.DataPropertyName);
                    }
                    url = $"{url.TrimEnd('/', '\\')}/{dataSourceName}?fields={Uri.EscapeDataString(string.Join(',', fieldList))}";
                    if (conditions != null && conditions.Count > 0)
                    {
                        var arr = conditions.Select(s => $"({s.Name},{s.Symbol},{s.Value})").ToArray();
                        url += "&conditions=" + Uri.EscapeDataString(string.Join(',', arr));
                    }

                    var content = await _httpClient.GetStringAsync(url);
                    var table = JsonToDataTable(content);
                    control.DataSource = table;
                }
            }
        }
    }

    public class DBCondition
    {
        [Browsable(true)]
        [Description("查询条件名称"), Category("数据"), DefaultValue("")]
        [DisplayName]
        public string? Name { get; set; }
        [Browsable(true)]
        [Description("条件符号"), Category("数据"), DefaultValue("")]
        public string? Symbol { get; set; }
        [Browsable(true)]
        [Description("查询条件值"), Category("数据"), DefaultValue("")]
        public string? Value { get; set; }
    }
}

  DBComBox控件:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GSWControls
{
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(DBComBox), "gcom.png")]
    public class DBComBox : ComboBox
    {       
        [Browsable(true)]
        [Description("后端Url"), Category("远程数据"), DefaultValue("")]
        public string? Url { get; set; }

        [Browsable(true)]
        [Description("访问Url后端数据源名称"), Category("远程数据"), DefaultValue("")]
        public string? DataSourceName { get; set; }

        [Browsable(true)]
        [Description("查询数据源条件参数"), Category("远程数据"), DefaultValue("")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<DBCondition>? Conditions { get; set; } = new List<DBCondition>();

        protected async override void CreateHandle()
        {
            base.CreateHandle();
            if (!string.IsNullOrWhiteSpace(Url) && !string.IsNullOrWhiteSpace(DataSourceName) && Conditions != null)
            {
                await this.DBControlInit(Url, DataSourceName, Conditions);
            }
        }
    }
}

  DBDataGridView控件:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GSWControls
{
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(DBDataGridView), "ggrid.png")]
    public class DBDataGridView : DataGridView
    {        
        [Browsable(true)]
        [Description("后端Url"), Category("远程数据"), DefaultValue("")]
        public string? Url { get; set; }    
        
        [Browsable(true)]
        [Description("访问Url后端数据源名称"), Category("远程数据"), DefaultValue("")]
        public string? DataSourceName { get; set; }

        [Browsable(true)]
        [Description("查询数据源条件参数"), Category("远程数据"), DefaultValue("")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<DBCondition>? Conditions { get; set; } = new List<DBCondition>();

        protected async override void CreateHandle()
        {
            base.CreateHandle();
            if (!string.IsNullOrWhiteSpace(Url) && !string.IsNullOrWhiteSpace(DataSourceName) && Conditions != null)
            {
                await this.DBGridInit(Url, DataSourceName, Conditions);
            }
        }
    }
}

  上面的属性都是为了在VS的工具箱或在属性管理器里,让自定义控件看起起来与官方控件一样。

 

  使用控件项目:

  使用项目很简单,就是完成布局,设置一下属性就可以了。

  DBComBox的控件设置

查询条件设置(这里没有处理条件间的组合)

 

 DBDataGridView的属性设置,DataGridView的列是设置在控件本身的更中的,这里就不切图了。

 

   这个组合查询条件

 

 最后运行结果:

  想要更快更方便的了解相关知识,可以关注微信公众号

 

 

 

posted @ 2023-02-07 21:06  桂素伟  阅读(159)  评论(0编辑  收藏  举报