WPF项目学习.三

时间:2023-01-23 21:12:59

工具代码记录

版权声明:本文为博主初学经验,未经博主允许不得转载。

一、前言

  记录在学习与制作WPF过程中遇到的解决方案。

   分页控件的制作,邮件发送,站点请求代码,excel导入导出等代码的实现过程;

二、配置

系统环境:win10

开发工具:Visual Studio 2017

开发语言:C#.WPF (MVVM框架)

三、功能

  1. 分页控件的制作

1.1 前端xaml代码

WPF项目学习.三

<UserControl x:Class="SCB.RPS.Client.Controls.UcPager"
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:SCB.RPS.Client.Controls"
mc:Ignorable="d" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="" />
<ColumnDefinition />
<ColumnDefinition Width="" />
<ColumnDefinition />
<ColumnDefinition Width="" />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="" Orientation="Horizontal">
<Label Content="每页显示"/>
<ComboBox Text="{Binding PagerSize}"
            Foreground="Orange" VerticalContentAlignment="Center">
<ComboBoxItem Content=""/>
<ComboBoxItem Content=""/>
<ComboBoxItem Content=""/>
</ComboBox>
<Label Content="项"/>
</StackPanel>
<StackPanel Grid.Column="" Orientation="Horizontal">
<Label Content="当前显示数目:"/>
<Label Content="{Binding PagerRecord}" Foreground="Orange"/>
</StackPanel>
<StackPanel Grid.Column="" Orientation="Horizontal">
<Label Content="查询总量:"/>
<Label Content="{Binding PagerQuantity}" Foreground="Orange"/>
<Label Content="页,共"/>
<Label Content="{Binding TotalRecord}" Foreground="Orange"/>
<Label Content="项"/>
</StackPanel>
<StackPanel Grid.Column=""
             Orientation="Horizontal" VerticalAlignment="Center">
<Button Content="首页" Width="" Height="20"
            Template="{StaticResource DefaultButton}"
            Command="{Binding PagerFirst}"/>
<Button Content="上页" Width="" Height="20"
            Template="{StaticResource DefaultButton}"
            Command="{Binding PagerBefore}" />
<Label Content="{Binding PagerIndex}"
            Foreground="Orange" ToolTip="当前所在页码"/>
<Button Content="下页" Width=""
            Template="{StaticResource DefaultButton}"
            Command="{Binding PagerAfter}" />
<Button Content="末页" Width="" Height="20"
            Template="{StaticResource DefaultButton}"
            Command="{Binding PagerEnd}" />
</StackPanel>
</Grid>
</UserControl>

1.2 style代码

<ControlTemplate TargetType="{x:Type Button}" x:Key="DefaultButton">
<Border BorderBrush="{TemplateBinding Control.BorderBrush}"
       BorderThickness="" Name="btn_modify_bg">
<Border.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#5CACEE" Offset="1.0" />
</LinearGradientBrush>
</Border.Background>
    <ContentPresenter Content="{TemplateBinding ContentControl.Content}"
          HorizontalAlignment="Center" VerticalAlignment="Center" />
  </Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Border.Background" TargetName="btn_modify_bg">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#1C86EE" Offset="0.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter Property="UIElement.Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="" Color="#1C86EE"
                Direction="" Opacity="0.6"
                RenderingBias="Performance" ShadowDepth="" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

1.3 ViewModel业务代码

/// 分页控件 页面模型
public class PagerViewModel : BindableBase
{
//定义一个委托
public delegate void PageChangedHandle(object sender); //上一页下一页的触发事件
public event PageChangedHandle PageChanged; // 默认显示的页容量
public static int DefaultSize = ; public PagerViewModel()
{
PagerFirst = new RelayCommand(PageFirst);
PagerBefore = new RelayCommand(PageBefore);
PagerAfter = new RelayCommand(PageAfter);
PagerEnd = new RelayCommand(PageEnd);
} private int _pagerSize = DefaultSize; //每页容量
private int _pagerIndex = ; //第几页
private int _pagerQuantity; //多少页
private int _pagerRecord; //当前页大小
private int _totalRecord; //总量多少 // 每页容量
public int PagerSize
{
get => _pagerSize;
set
{
_pagerSize = value;
RaisePropertyChanged();
}
} // 第几页
public int PagerIndex
{
get => _pagerIndex;
set
{
_pagerIndex = value;
RaisePropertyChanged();
}
} // 总共有多少页
public int PagerQuantity
{
get => _pagerQuantity;
set
{
_pagerQuantity = value;
RaisePropertyChanged();
}
} // 当前页有多少条数据
public int PagerRecord
{
get => _pagerRecord;
set
{
_pagerRecord = value;
RaisePropertyChanged();
}
} // 查询记录的总量
public int TotalRecord
{
get => _totalRecord;
set
{
_totalRecord = value;
RaisePropertyChanged();
}
} // 首页
public RelayCommand PagerFirst { get; set; } // 上一页
public RelayCommand PagerBefore { get; set; } // 下一页
public RelayCommand PagerAfter { get; set; } // 末页
public RelayCommand PagerEnd { get; set; } // 首页事件
private void PageFirst()
{
PagerIndex = ;
PageChanged?.Invoke();
} // 上一页事件
private void PageBefore()
{
PagerIndex--;
if (PagerIndex < )
{
PagerIndex = ;
MessageBoxHelper.ShowTips("已经是首页");
return;
}
PageChanged?.Invoke(PagerIndex);
} // 下一页事件
private void PageAfter()
{
PagerIndex++;
if (PagerIndex > PagerQuantity)
{
PagerIndex = PagerQuantity;
MessageBoxHelper.ShowTips("已经是末页");
return;
}
PageChanged?.Invoke(PagerIndex);
} // 末页事件
private void PageEnd()
{
PagerIndex = PagerQuantity;
PageChanged?.Invoke(PagerQuantity);
} // 重置分页数据
public void ResetPage()
{
PagerIndex = ;
PagerSize = DefaultSize;
PagerRecord = ;
PagerQuantity = ;
TotalRecord = ;
}
}

MessageBoxHelper.ShowTip 是我封装的弹框提示信息类,等价于MessageBox.Show,只是便于以后统一修改样式或者更改提示信息时的业务处理;

自定义控件不涉及业务逻辑代码,在业务场景使用的时候,需要返回当前页容量、查询总量和查询页数;

PagerView.PagerRecord = result.Count;
PagerView.TotalRecord = result.FirstOrDefault()?.TotalRecord ?? ;
PagerView.PagerQuantity=PagerView.TotalRecord / PagerView.PagerSize + ;

PageChanged是控件的委托方法,在调用该控件时,需绑定委托的事件;

//代码放置在类初始化事件中
PagerView.PageChanged += SearchPageData; //委托给分页控件的查询方法

可以增加比如缓存效果,点击下一页时保存当前页面数据到内存中,重新刷新再清理内存的数据;


private Dictionary<int, List<BatchProductShiftModel>> _tempPage =

          new Dictionary<int, List<BatchProductShiftModel>>(); //列表内容

 private int _tempIndex = 1; //记录当前页

// 分页查询 [加了缓存效果,保存查过的页码数据]
// 缓存后,不会根据选择的页面大小进行调整
// 缓存已处理的数据,点击下一页时,查询总量会产生变化,因为根据条件查询,状态变了
public void SearchPageData(object str)
{
   //记录当前页面数据
_tempPage[_tempIndex] = LstReceiveOrder.ToList();
   //为下次点击分页操作做准备 在内存中记录当前页码
_tempIndex = PagerView.PagerIndex;//判断该页码是否已经在缓存中
if (_tempPage.ContainsKey(PagerView.PagerIndex))
{
LstReceiveOrder.Clear();
     //清理后加载数据
LstReceiveOrder.AddRange(_tempPage[PagerView.PagerIndex]);
//汇总当前页数量
     PagerView.PagerRecord = LstReceiveOrder.Count;
//清理下面明细页的列表内容
     OrderVolumes.Clear();
SelectItemOrder = string.Empty;
}
else SearchProductShiftData(false);
}

LstReceiveOrder 是查询的列表数据;

SearchProductShifData 是查询数据的方法;具体代码不贴了;

OrderVolumes 是明细页,可以去掉该代码;

  2.中文转拼音代码

using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.International.Converters.PinYinConverter; namespace Common
{
public class PinyinResult
{
public List<string> FirstPingYin { get; set; }
public List<string> FullPingYin { get; set; }
}
/// <summary>
///汉字转为拼音
/// </summary>
public static class PinYinHelper
{
/// <summary>
/// 汉字转为拼音
/// </summary>
/// <param name="str">需要转换的汉字</param>
public static PinyinResult ToPinYin(string str)
{
var chs = str.ToCharArray();
var totalPingYins = new Dictionary<int, List<string>>();
for (int i = ; i < chs.Length; i++)
{
var pinyins = new List<string>();
var ch = chs[i];
//是否是有效的汉字
if (ChineseChar.IsValidChar(ch))
{
var cc = new ChineseChar(ch);
pinyins = cc.Pinyins.Where(
            p => !string.IsNullOrWhiteSpace(p)).ToList();
}
else
pinyins.Add(ch.ToString());
//去除声调,转小写
pinyins = pinyins.ConvertAll(
            p => Regex.Replace(p, @"\d", "").ToLower());
//去重
pinyins = pinyins.Where(p => !string.IsNullOrWhiteSpace(p))
                .Distinct().ToList();
if (pinyins.Any())
totalPingYins[i] = pinyins;
}
var result = new PinyinResult();
foreach (var pinyins in totalPingYins)
{
var items = pinyins.Value;
if (result.FullPingYin==null||result.FullPingYin.Count<=)
{
result.FullPingYin = items;
result.FirstPingYin = items.ConvertAll(
                    p => p.Substring(, ))
                    .Distinct().ToList();
}
else
{
//全拼循环匹配
var newTotalPingYins = new List<string>();
foreach (var totalPingYin in result.FullPingYin)
{
newTotalPingYins.AddRange(
                items.Select(item =>totalPingYin+item));
}
newTotalPingYins = newTotalPingYins.Distinct().ToList();
result.FullPingYin = newTotalPingYins;
//首字母循环匹配
var newFirstPingYins = new List<string>();
foreach (var firstPingYin in result.FirstPingYin)
{
newFirstPingYins.AddRange(
             items.Select(item=>firstPingYin + item.Substring(, )));
}
newFirstPingYins = newFirstPingYins.Distinct().ToList();
result.FirstPingYin = newFirstPingYins;
}
}
return result;
}
}
}

  3.站点请求代码

//添加引用Newtonsoft.Json.dll

// webapi客户端
public class WebApiClient
{
private readonly string _requestUrl;
private readonly string _urlString; // 构造函数
public WebApiClient(string urlString, string requestUrl)
{
_urlString = urlString;
_requestUrl = requestUrl;
} // 发起webapi请求
public T WebApiPost<T>(object value,
        List<Tuple<string, List<string>>> heads = null,
List<string> accepts = null, int timeoutSeconds = )
{
using (var httpClient = new HttpClient())
{
httpClient.Timeout = TimeSpan.FromSeconds(timeoutSeconds);
httpClient.BaseAddress = new Uri(_urlString);
heads?.ForEach(head =>
          httpClient.DefaultRequestHeaders.Add(head.Item1, head.Item2));
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
          new MediaTypeWithQualityHeaderValue("application/json"));
accepts?.ForEach(
accept => httpClient.DefaultRequestHeaders.Accept.Add(
                  new MediaTypeWithQualityHeaderValue(accept)));
return httpClient.PostAsJsonAsync(_requestUrl, value)
.Result.Content.ReadAsAsync<T>()
.Result;
}
} // 发起webapi请求
public T WebApiGet<T>(List<Tuple<string, List<string>>> heads = null,
List<string> accepts = null, bool isResponseJson = true)
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(_urlString);
heads?.ForEach(head =>
           httpClient.DefaultRequestHeaders.Add(
                      head.Item1, head.Item2));
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
           new MediaTypeWithQualityHeaderValue("application/json"));
accepts?.ForEach(
accept => httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue(accept)));
if (isResponseJson)
{
return httpClient.GetAsync(_requestUrl)
.Result.Content.ReadAsAsync<T>()
.Result;
}
var content = httpClient.GetAsync(_requestUrl)
.Result.Content.ReadAsStringAsync()
.Result;
return JsonConvert.DeserializeObject<T>(content);
}
} public static T Post<T>(
    string url, object param, string rpsToken = null) where T : class
{
var json = JsonConvert.SerializeObject(param);
var byteData = Encoding.UTF8.GetBytes(json);
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Method = "POST";
httpRequest.KeepAlive = false;
httpRequest.ContentType = "application/json;charset=utf-8";
if (!string.IsNullOrWhiteSpace(rpsToken))
httpRequest.Headers.Add("RpsToken", rpsToken);
httpRequest.ContentLength = byteData.Length;
httpRequest.GetRequestStream().Write(byteData, , byteData.Length);
httpRequest.Timeout = ;
var strResponse = string.Empty;
Action act = () =>
{
using (var httpResponse = httpRequest.GetResponse())
using (var respStream = httpResponse.GetResponseStream())
using (var reader = new StreamReader(respStream, Encoding.UTF8))
strResponse = reader.ReadToEnd();
};
TryMultiTime(act, );
return JsonConvert.DeserializeObject<T>(strResponse);
} public static void TryMultiTime(
      Action act, int tryTimes, int interval = )
{
var i = ;
while (true)
{
try
{
i++;
act();
break;
}
catch (Exception ex)
{
if (i >= tryTimes)
throw new Exception("请求超时", ex);
System.Threading.Thread.Sleep(interval);
}
}
} /// <summary>
/// 提交带token的请求
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="url">地址</param>
/// <param name="param">json参数</param>
/// <param name="rpsToken">token检验</param>
public static T PostByToken<T>(
    string url, object param, string rpsToken = null) where T : class
{
var json = JsonConvert.SerializeObject(param);
var httpContent = new StringContent(json, Encoding.UTF8);
httpContent.Headers.Add("RpsToken", rpsToken);
httpContent.Headers.ContentType =
              new MediaTypeHeaderValue("application/json");
var response = new HttpClient().PostAsync(url, httpContent);
var strResponse = response.Result.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<T>(strResponse);
}
}

  4.邮件发送代码

//使用System.Net.Mail

/// <summary>
/// 发送信息
/// </summary>
public static void SendMessage(string msg = null)
{
try
{
using (var client = new SmtpClient("主机地址", )
{
Credentials = new NetworkCredential("发送人地址", "发送人邮箱密码")
})
{
client.Send(SetMail(msg));
}
}
catch (Exception ex)
{
NLog.Logger.Debug($"异常:{ex.ToJson()}");
}
} /// <summary>
/// 邮件内容
/// </summary>
private static MailMessage SetMail(string msg = null)
{
var fileName =
$"{AppDomain.CurrentDomain.BaseDirectory}/logs/{DateTime.Now:yyyy-MM-dd}.log";
Stream stream = null;
if (File.Exists(fileName))
{
// 打开文件
var fileStream = new FileStream(
      fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
// 读取文件的 byte[]
var bytes = new byte[fileStream.Length];
fileStream.Read(bytes, , bytes.Length);
fileStream.Close();
// 把 byte[] 转换成 Stream
stream = new MemoryStream(bytes);
}
var body = new StringBuilder();
if (!string.IsNullOrWhiteSpace(msg))
     body.AppendFormat("<br/>异常信息:{0};", msg);
var mail = new MailMessage
{
From = new MailAddress("发送人邮箱地址"),
Subject = $"邮件标题",
IsBodyHtml = true,
BodyEncoding = Encoding.UTF8,
Body = body.ToString(),
Priority = MailPriority.High
};
   //添加收件人
BusinessConfiger.MailTo.ForEach(
      to => mail.To.Add(new MailAddress(to)));
if (stream != null)
mail.Attachments.Add(
      new Attachment(stream, $"Log{DateTime.Now:yyyyMMdd}"));
return mail;
}

  5.导入导出代码量过多,直接再下篇源码中体现;

  6.下篇预告

 干货贴代码,包含需求文案、设计思路、简要数据库结构、简要流程图和明细代码,动图细化每步操作,入门级引导文章;

 项目功能包括:登录、首页、数据维护 和 全文搜索等增删查改的常用操作;