C#實(shí)現(xiàn)串口調(diào)試工具
前文
由于經(jīng)常用到串口調(diào)試, 盡管有現(xiàn)成的軟件, 因?yàn)榍岸藭r(shí)間涉及一個(gè)二次開發(fā), 就因?yàn)橐粋€(gè)RtsEnable設(shè)置, 折騰半天, 網(wǎng)上各種版本的也很多, 功能擴(kuò)展的很開也多。所以現(xiàn)在自己做了一個(gè)夠用版,基于自己的需求,簡(jiǎn)單的實(shí)現(xiàn)發(fā)送接收功能, 至于那些擴(kuò)展功能可以自己根據(jù)需求添加。
正文
先上個(gè)運(yùn)行效果圖:

項(xiàng)目架構(gòu)
該實(shí)例用的GalaSoft.Mvvm, 該插件可以直接在NuGet中并且添加。

1.串口參數(shù) , 為了方便, 端口號(hào)并沒有用動(dòng)態(tài)加載的方式, 如下枚舉結(jié)構(gòu):
namespace System.Windows.Data
{
//
// 摘要:
// 提供將自定義邏輯應(yīng)用于綁定的方法。
public interface IValueConverter
{
//
// 摘要:
// 轉(zhuǎn)換值。
//
// 參數(shù):
// value:
// 綁定源生成的值。
//
// targetType:
// 綁定目標(biāo)屬性的類型。
//
// parameter:
// 要使用的轉(zhuǎn)換器參數(shù)。
//
// culture:
// 要用在轉(zhuǎn)換器中的區(qū)域性。
//
// 返回結(jié)果:
// 轉(zhuǎn)換后的值。 如果該方法返回 null,則使用有效的 null 值。
object Convert(object value, Type targetType, object parameter, CultureInfo culture);
//
// 摘要:
// 轉(zhuǎn)換值。
//
// 參數(shù):
// value:
// 綁定目標(biāo)生成的值。
//
// targetType:
// 要轉(zhuǎn)換為的類型。
//
// parameter:
// 要使用的轉(zhuǎn)換器參數(shù)。
//
// culture:
// 要用在轉(zhuǎn)換器中的區(qū)域性。
//
// 返回結(jié)果:
// 轉(zhuǎn)換后的值。 如果該方法返回 null,則使用有效的 null 值。
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
}
}2.串口參數(shù)配置類 ,
作用: 主要用于綁定界面的參數(shù)選項(xiàng)。
/// <summary>
/// 串口參數(shù)設(shè)置類
/// </summary>
public class ComParameterConfig : ViewModelBase
{
public ComParameterConfig()
{
Port = System.Enum.GetValues(typeof(Port));
CheckMode = System.Enum.GetValues(typeof(CheckMode));
StopBit = System.Enum.GetValues(typeof(StopBit));
BaudRate = new List<int>() { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, };
DataBit = new List<int>() { 6, 7, 8 };
}
private Array port;
private Array checkMode;
private Array stopBit;
private List<int> dataBit;
private List<int> baudRate;
/// <summary>
/// 端口
/// </summary>
public Array Port
{
get { return port; }
set { port = value; RaisePropertyChanged(); }
}
/// <summary>
/// 校驗(yàn)?zāi)J?
/// </summary>
public Array CheckMode
{
get { return checkMode; }
set { checkMode = value; RaisePropertyChanged(); }
}
/// <summary>
/// 停止位
/// </summary>
public Array StopBit
{
get { return stopBit; }
set { stopBit = value; RaisePropertyChanged(); }
}
/// <summary>
/// 波特率
/// </summary>
public List<int> BaudRate
{
get { return baudRate; }
set { baudRate = value; RaisePropertyChanged(); }
}
/// <summary>
/// 數(shù)據(jù)位
/// </summary>
public List<int> DataBit
{
get { return dataBit; }
set { dataBit = value; RaisePropertyChanged(); }
}
}3.當(dāng)前配置參數(shù)類
作用: 用于保存當(dāng)前的串口參數(shù)、串口功能開關(guān)接收數(shù)據(jù)等業(yè)務(wù)。
核心代碼:
/// <summary>
/// 當(dāng)前配置參數(shù)
/// </summary>
public class CurrentParameter : ViewModelBase
{
#region Private
private int baudRdate = 9600;
private int dataBit = 8;
private Port port;
private CheckMode checkMode;
private StopBit stopBit = StopBit.One;
private SerialPort serialPort;
private string dataReceiveInfo;
private string sendData;
private bool isOpen;
private bool receiveFormat16 = true;
private bool sendFormat16 = true;
private int sendCount;
private int receiveCount;
#endregion
#region UI綁定參數(shù)
/// <summary>
/// 發(fā)送數(shù)量
/// </summary>
public int SendCount
{
get { return sendCount; }
set { sendCount = value; RaisePropertyChanged(); }
}
/// <summary>
/// 接收數(shù)量
/// </summary>
public int ReceiveCount
{
get { return receiveCount; }
set { receiveCount = value; RaisePropertyChanged(); }
}
/// <summary>
/// 接收區(qū)16進(jìn)制
/// </summary>
public bool ReceiveFormat16
{
get { return receiveFormat16; }
set { receiveFormat16 = value; RaisePropertyChanged(); }
}
/// <summary>
/// 接收區(qū)數(shù)據(jù)
/// </summary>
public string DataReceiveInfo
{
get { return dataReceiveInfo; }
set { dataReceiveInfo = value; RaisePropertyChanged(); }
}
/// <summary>
/// 發(fā)送數(shù)據(jù)
/// </summary>
public string SendData
{
get
{
return sendData;
}
set { sendData = value; RaisePropertyChanged(); }
}
/// <summary>
/// 發(fā)送區(qū)16進(jìn)制
/// </summary>
public bool SendFormat16
{
get { return sendFormat16; }
set { sendFormat16 = value; RaisePropertyChanged(); }
}
#endregion
#region 串口參數(shù)信息
/// <summary>
/// 開關(guān)
/// </summary>
public bool IsOpen
{
get { return isOpen; }
set { isOpen = value; RaisePropertyChanged(); }
}
/// <summary>
/// 數(shù)據(jù)位
/// </summary>
public int DataBit
{
get { return dataBit; }
set { dataBit = value; RaisePropertyChanged(); }
}
/// <summary>
/// 波特率
/// </summary>
public int BaudRdate
{
get { return baudRdate; }
set { baudRdate = value; RaisePropertyChanged(); }
}
/// <summary>
/// 端口
/// </summary>
public Port Port
{
get { return port; }
set { port = value; RaisePropertyChanged(); }
}
/// <summary>
/// 校驗(yàn)
/// </summary>
public CheckMode CheckMode
{
get { return checkMode; }
set { checkMode = value; RaisePropertyChanged(); }
}
/// <summary>
/// 停止位
/// </summary>
public StopBit StopBit
{
get { return stopBit; }
set { stopBit = value; RaisePropertyChanged(); }
}
/// <summary>
/// COM
/// </summary>
public SerialPort SerialPort
{
get { return serialPort; }
set { serialPort = value; RaisePropertyChanged(); }
}
#endregion
#region 串口操作方法
/// <summary>
/// 開啟串口
/// </summary>
/// <returns></returns>
public bool Open()
{
if (serialPort != null && serialPort.IsOpen)
{
return Close();
}
try
{
serialPort = new SerialPort();
serialPort.DataBits = this.DataBit;
serialPort.StopBits = ComHelper.GetStopBits(this.StopBit.ToString());
serialPort.Parity = ComHelper.GetParity(this.CheckMode.ToString());
serialPort.PortName = this.Port.ToString();
serialPort.RtsEnable = true;
serialPort.DataReceived += SerialPort_DataReceived;
serialPort.Open();
if (serialPort.IsOpen)
return IsOpen = true;
else
return IsOpen = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return IsOpen = false;
}
/// <summary>
/// 關(guān)閉串口
/// </summary>
/// <returns></returns>
public bool Close()
{
try
{
if (serialPort.IsOpen)
{
serialPort.Close();
return IsOpen = serialPort.IsOpen;
}
else
{
return IsOpen = serialPort.IsOpen;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return IsOpen = false;
}
}
/// <summary>
/// 發(fā)送數(shù)據(jù)
/// </summary>
public void Send()
{
if (SendFormat16)
{
byte[] bytes = CRC.StringToHexByte(SendData);
this.SerialPort.Write(bytes, 0, bytes.Length);
SendCount = bytes.Length; //不做增量
}
else
{
this.SerialPort.Write(SendData);
SendCount = SendData.Length;
}
Messenger.Default.Send("", "PlaySendFlashing");
}
/// <summary>
/// 清空接收區(qū)
/// </summary>
public void Clear()
{
this.DataReceiveInfo = string.Empty;
}
/// <summary>
/// 清空發(fā)送區(qū)和緩存區(qū)
/// </summary>
public void ClearText()
{
this.SendData = string.Empty;
this.SendCount = 0;
this.ReceiveCount = 0;
}
#endregion
/// <summary>
/// 返回事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Messenger.Default.Send("", "PlayReciveFlashing");
byte[] readBuffer = new byte[SerialPort.ReadBufferSize];
SerialPort.Read(readBuffer, 0, readBuffer.Length);
ReceiveCount = SerialPort.ReceivedBytesThreshold; //不做增量
if (ReceiveFormat16)
{
//不做增量
DataReceiveInfo = CRC.ByteToString(readBuffer, true);
}
else
{
DataReceiveInfo = Encoding.ASCII.GetString(readBuffer);
}
}
}4.核心MainViewModel類
作用: 關(guān)聯(lián)首頁的上下文, 通過DataContext綁定, 關(guān)聯(lián)界面元素、命令等作用。
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
ComParameterConfig = new ComParameterConfig();
CurrentParameter = new CurrentParameter();
}
private ComParameterConfig comParameter;
/// <summary>
/// 參數(shù)類
/// </summary>
public ComParameterConfig ComParameterConfig
{
get { return comParameter; }
set { comParameter = value; RaisePropertyChanged(); }
}
private CurrentParameter currentParameter;
/// <summary>
/// 當(dāng)前配置參數(shù)
/// </summary>
public CurrentParameter CurrentParameter
{
get { return currentParameter; }
set { currentParameter = value; RaisePropertyChanged(); }
}
#region Command
private RelayCommand _ToOpen;
public RelayCommand ToOpen
{
get
{
if (_ToOpen == null)
{
_ToOpen = new RelayCommand(Open);
}
return _ToOpen;
}
set
{
_ToOpen = value;
}
}
/// <summary>
/// 根據(jù)配置打開端口
/// </summary>
public void Open()
{
this.CurrentParameter.Open();
}
private RelayCommand _ToClick;
public RelayCommand ToClick
{
get
{
if (_ToClick == null)
{
_ToClick = new RelayCommand(Click);
}
return _ToClick;
}
set
{
_ToClick = value;
}
}
/// <summary>
/// 發(fā)送數(shù)據(jù)
/// </summary>
public void Click()
{
this.CurrentParameter.Send();
}
private RelayCommand _ToClear;
public RelayCommand ToClear
{
get
{
if (_ToClear == null)
{
_ToClear = new RelayCommand(Clear);
}
return _ToClear;
}
set
{
_ToClear = value;
}
}
/// <summary>
/// 清空接收區(qū)
/// </summary>
public void Clear()
{
this.CurrentParameter.Clear();
}
private RelayCommand _ToClearText;
public RelayCommand ToClearText
{
get
{
if (_ToClearText == null)
{
_ToClearText = new RelayCommand(ClearText);
}
return _ToClearText;
}
set
{
_ToClearText = value;
}
}
/// <summary>
/// 清空界面值
/// </summary>
public void ClearText()
{
this.CurrentParameter.ClearText();
}
#endregion
}5.CRC校驗(yàn)核心類
作用:主要實(shí)現(xiàn)數(shù)據(jù)校驗(yàn), 含ModbusCR標(biāo)準(zhǔn)校驗(yàn)
/// <summary>
/// CRC校驗(yàn)
/// </summary>
public class CRC
{
#region CRC16
public static byte[] CRC16(byte[] data)
{
int len = data.Length;
if (len > 0)
{
ushort crc = 0xFFFF;
for (int i = 0; i < len; i++)
{
crc = (ushort)(crc ^ (data[i]));
for (int j = 0; j < 8; j++)
{
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
}
}
byte hi = (byte)((crc & 0xFF00) >> 8); //高位置
byte lo = (byte)(crc & 0x00FF); //低位置
return new byte[] { hi, lo };
}
return new byte[] { 0, 0 };
}
#endregion
#region ToCRC16
public static string ToCRC16(string content)
{
return ToCRC16(content, Encoding.UTF8);
}
public static string ToCRC16(string content, bool isReverse)
{
return ToCRC16(content, Encoding.UTF8, isReverse);
}
public static string ToCRC16(string content, Encoding encoding)
{
return ByteToString(CRC16(encoding.GetBytes(content)), true);
}
public static string ToCRC16(string content, Encoding encoding, bool isReverse)
{
return ByteToString(CRC16(encoding.GetBytes(content)), isReverse);
}
public static string ToCRC16(byte[] data)
{
return ByteToString(CRC16(data), true);
}
public static string ToCRC16(byte[] data, bool isReverse)
{
return ByteToString(CRC16(data), isReverse);
}
#endregion
#region ToModbusCRC16
public static string ToModbusCRC16(string s)
{
return ToModbusCRC16(s, true);
}
public static string ToModbusCRC16(string s, bool isReverse)
{
return ByteToString(CRC16(StringToHexByte(s)), isReverse);
}
public static string ToModbusCRC16(byte[] data)
{
return ToModbusCRC16(data, true);
}
public static string ToModbusCRC16(byte[] data, bool isReverse)
{
return ByteToString(CRC16(data), isReverse);
}
#endregion
#region ByteToString
public static string ByteToString(byte[] arr, bool isReverse)
{
try
{
byte hi = arr[0], lo = arr[1];
return Convert.ToString(isReverse ? hi + lo * 0x100 : hi * 0x100 + lo, 16).ToUpper().PadLeft(4, '0');
}
catch (Exception ex) { throw (ex); }
}
public static string ByteToString(byte[] arr)
{
try
{
return ByteToString(arr, true);
}
catch (Exception ex) { throw (ex); }
}
#endregion
#region StringToHexString
public static string StringToHexString(string str)
{
StringBuilder s = new StringBuilder();
foreach (short c in str.ToCharArray())
{
s.Append(c.ToString("X4"));
}
return s.ToString();
}
#endregion
#region StringToHexByte
private static string ConvertChinese(string str)
{
StringBuilder s = new StringBuilder();
foreach (short c in str.ToCharArray())
{
if (c <= 0 || c >= 127)
{
s.Append(c.ToString("X4"));
}
else
{
s.Append((char)c);
}
}
return s.ToString();
}
private static string FilterChinese(string str)
{
StringBuilder s = new StringBuilder();
foreach (short c in str.ToCharArray())
{
if (c > 0 && c < 127)
{
s.Append((char)c);
}
}
return s.ToString();
}
/// <summary>
/// 字符串轉(zhuǎn)16進(jìn)制字符數(shù)組
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
public static byte[] StringToHexByte(string str)
{
return StringToHexByte(str, false);
}
/// <summary>
/// 字符串轉(zhuǎn)16進(jìn)制字符數(shù)組
/// </summary>
/// <param name="str"></param>
/// <param name="isFilterChinese">是否過濾掉中文字符</param>
/// <returns></returns>
public static byte[] StringToHexByte(string str, bool isFilterChinese)
{
string hex = isFilterChinese ? FilterChinese(str) : ConvertChinese(str);
//清除所有空格
hex = hex.Replace(" ", "");
//若字符個(gè)數(shù)為奇數(shù),補(bǔ)一個(gè)0
hex += hex.Length % 2 != 0 ? "0" : "";
byte[] result = new byte[hex.Length / 2];
for (int i = 0, c = result.Length; i < c; i++)
{
result[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
}
return result;
}
#endregion
}WPF技術(shù)點(diǎn):
1.自定義樣式按鈕
<Style x:Key="CommonButtonBase" TargetType="{x:Type Button}">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="4"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="#000000"/>
<Setter Property="Opacity" Value="0.1"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="#FFFF00"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="false">
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}" x:Key="Btn0093EABase" BasedOn="{StaticResource CommonButtonBase}">
<Setter Property="Background" Value="#0093EA"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="22"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Margin" Value="5"/>
</Style>2.轉(zhuǎn)換器用于綁定按鈕
public class FontConverters : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && bool.TryParse(value.ToString(), out bool result))
{
if (result)
{
return "關(guān)閉串口";
}
}
return "打開串口";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}//用于綁定UI的顏色狀態(tài)顯示 public class ColorConverters : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && bool.TryParse(value.ToString(), out bool result))
{
if (result)
{
return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#2E8B57"));
}
}
return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#FF6347"));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}3.引用字體
<TextBlock Text="" Margin="20 5 0 5" FontFamily="pack://application:,,,/Font/#iconfont"
Foreground="White" FontSize="30" VerticalAlignment="Center"/>4.綁定命令和元素
<TextBlock Text="端 口:" Style="{DynamicResource TxtComStyle}"/>
<ComboBox Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.Port}"
ItemsSource="{Binding ComParameterConfig.Port}"
/>
<TextBlock Text="波 特 率:" Style="{DynamicResource TxtComStyle}"/>
<ComboBox Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.BaudRdate}"
ItemsSource="{Binding ComParameterConfig.BaudRate}"
/>
<TextBlock Text="數(shù) 據(jù) 位:" Style="{DynamicResource TxtComStyle}"/>
<ComboBox Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.DataBit}"
ItemsSource="{Binding ComParameterConfig.DataBit}"
/>
<TextBlock Text="校 驗(yàn) 位:" Style="{DynamicResource TxtComStyle}"/>
<ComboBox Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.CheckMode}"
ItemsSource="{Binding ComParameterConfig.CheckMode}"
/>
<TextBlock Text="停 止 位:" Style="{DynamicResource TxtComStyle}"/>
<ComboBox Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.StopBit}"
ItemsSource="{Binding ComParameterConfig.StopBit}"
/>
<TextBlock Text="狀 態(tài):" Style="{DynamicResource TxtComStyle}"/>
<TextBlock Text="" Style="{DynamicResource TxtComStyle1}" Foreground="{Binding CurrentParameter.IsOpen,Converter={StaticResource ColorConverters}}" />寫在最后
主項(xiàng)目的結(jié)構(gòu)圖 , 如下:

到此這篇關(guān)于C#實(shí)現(xiàn)串口調(diào)試工具的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- C#中的串口通信SerialPort詳解
- c# 實(shí)現(xiàn)簡(jiǎn)單的串口通訊
- C#串口連接的讀取和發(fā)送詳解
- C# 實(shí)現(xiàn)簡(jiǎn)易的串口監(jiān)視上位機(jī)功能附源碼下載
- C#串口通信模塊使用方法示例
- C#串口通訊概念及簡(jiǎn)單的實(shí)現(xiàn)方法
- C# 串口接收數(shù)據(jù)中serialPort.close()死鎖的實(shí)例
- C#串口編程實(shí)例代碼
- C#獲取串口列表實(shí)現(xiàn)實(shí)時(shí)監(jiān)控串口
- C#實(shí)現(xiàn)把指定數(shù)據(jù)寫入串口
- C#串口通信實(shí)現(xiàn)方法
- C#串口通信程序?qū)嵗斀?/a>
相關(guān)文章
C#中#define后面只加一個(gè)參數(shù)的解釋
今天小編就為大家分享一篇關(guān)于C#中#define后面只加一個(gè)參數(shù)的解釋,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-04-04
Gridview自動(dòng)排序功能的實(shí)現(xiàn)
本篇文章主要是對(duì)Gridview自動(dòng)排序功能的實(shí)現(xiàn)代碼進(jìn)行了介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助2014-01-01
C# 撒列實(shí)現(xiàn)關(guān)鍵字過濾的實(shí)例
C# 撒列實(shí)現(xiàn)關(guān)鍵字過濾的實(shí)例,需要的朋友可以參考一下2013-04-04
分享C#操作內(nèi)存讀寫方法的主要實(shí)現(xiàn)代碼
這篇文章介紹了C#操作內(nèi)存讀寫方法的主要實(shí)現(xiàn)代碼,下面讓我們來看看具體的實(shí)例實(shí)現(xiàn),有需要的朋友可以參考一下2013-08-08
.Net WInform開發(fā)筆記(三)談?wù)勛灾瓶丶?自定義控件)
自定義控件的出現(xiàn)有利于用戶更好的實(shí)現(xiàn)自己的想法,可以封裝一些常用的方法,屬性等等,本文詳細(xì)介紹一下自定義控件的實(shí)現(xiàn),感興趣的朋友可以了解下2013-01-01
C#簡(jiǎn)易圖片格式轉(zhuǎn)換器實(shí)現(xiàn)方法
這篇文章主要介紹了C#簡(jiǎn)易圖片格式轉(zhuǎn)換器實(shí)現(xiàn)方法,涉及C#基于WinForm操作圖片的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11

