Tree_ 打包TreeView控件 C#
/*****************************************************************************
创建时间 :2019年11月19日
文件名 :Tree_.cs
功能 :树型数据结构
作者 :李锋
微信号 :lifengwx2005
Email :ruizhilf@139.com
联系电话 :1382877886
----------------------最后一次修改时间:2022年08月30日
*******************************************************************************/
#if _WINDOWS_DESKTOP_
using System;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace lf
{
/// <summary>
/// 一般树(N叉树)结点。
/// </summary>
public class TreeNode_
{
/// <summary>
/// 数据
/// </summary>
public string Data { set; get; }
/// <summary>
/// 第一个子结点
/// </summary>
public TreeNode_ FirstChild { set; get; }
/// <summary>
/// 下一个兄弟结点
/// </summary>
public TreeNode_ NextSibling { set; get; }
public TreeNode_()
{
FirstChild = null;
NextSibling = null;
}
public TreeNode_(TreeNode_ tn)
{
Data = tn.Data;
FirstChild = tn.FirstChild;
NextSibling = tn.NextSibling;
}
/// <summary>
/// 判断 tnChild 是否 tnParent 的直系子结点,如果是,则返回 true
/// </summary>
/// <param name="tnChild"></param>
/// <returns></returns>
/// 创建时间:2022-07-18 最后一次修改时间:2022-07-18
public bool IsMyChild(TreeNode_ tnChild)
{
if(tnChild == null) return false;
TreeNode_ tnTemp = FirstChild;
while (tnTemp != null)
{
if (tnTemp == tnChild)
return true;
tnTemp = tnTemp.NextSibling;
}
return false;
}
/// <summary>
/// 判断 tnChild 是否父结点,如果是,则返回 true
/// </summary>
/// <param name="tnParent">父结点</param>
/// <returns></returns>
/// 创建时间:2022-07-18 最后一次修改时间:2022-07-18
public bool IsMyParent(TreeNode_ tnParent)
{
if (tnParent == null) return false;
TreeNode_ tnTemp = tnParent.FirstChild;
while (tnTemp != null)
{
if (tnTemp == this)
return true;
tnTemp = tnTemp.NextSibling;
}
return false;
}
};
public class Tree_
{
public TreeNode_ First { get; set; }
public TreeNode_ GetLastSibling(TreeNode_ tnFirstSibling)
{
TreeNode_ tnTemp = tnFirstSibling.NextSibling;
if (tnTemp == null)
{
return tnFirstSibling;
}
else
{
TreeNode_ tnLastSibling = null;
while (true)
{
tnLastSibling = tnTemp;
tnTemp = tnTemp.NextSibling;
if (tnTemp == null)
{
return tnLastSibling;
}
}
}
}
public virtual bool RemoveNote(TreeNode_ tnRemove)
{
if (First == tnRemove)
{
First = First.NextSibling;
return true;
}
TreeNode_ tnParent = FindIsFirstChild(First, tnRemove);
if (tnParent == null)
{
TreeNode_ tnPrevSibling = FindPrevSibling(First, tnRemove);
if (tnPrevSibling == null)
{
return false;
}
else
{
tnPrevSibling.NextSibling = null;
return true;
}
}
else
{
lg.ShowInfo("tnParent!=null");
tnParent.FirstChild = null;
return true;
}
}
/// <summary>
/// 判断tnFind是否tnFindStart或者tnFindStart下面任何一个节点的第一个子节点,如果不是返回NULL,否刚返回它的父节点
/// </summary>
/// <param name="tnFindStart">查找开始位置</param>
/// <param name="tnFind">所查找的节点</param>
/// <returns>找到则返回父节点,否则返回NULL</returns>
public TreeNode_ FindIsFirstChild(TreeNode_ tnFindStart, TreeNode_ tnFind)
{
if (tnFindStart == tnFind || tnFindStart == null)
return null;
TreeNode_ tn = tnFindStart;
while (tn != null)
{
if (tn.FirstChild == tnFind)
return tn;
else
{
TreeNode_ tmp = FindIsFirstChild(tn.FirstChild, tnFind);
if (tmp != null)
return tmp;
}
tn = tn.NextSibling;
}
return null;
}
/// <summary>
/// 从tnStart处遍历二叉树,查找 tnNode的上一个兄弟节点
/// </summary>
/// <param name="tnStart">查找开始处</param>
/// <param name="tnNode">要查找的节点</param>
/// <returns>找到则返回上一个兄弟节点,否则返回NULL</returns>
/// 创建时间:????-??-?? 最后一次修改时间:2022-07-18
public TreeNode_ FindPrevSibling(TreeNode_ tnStart, TreeNode_ tnNode)
{
if (tnStart == tnNode || tnStart == null)
return null;
TreeNode_ tn = tnStart;
while (tn != null)
{
if (tn.NextSibling == tnNode)
return tn;
else
{
if (tn.FirstChild != null)
{
TreeNode_ tmp = FindPrevSibling(tn.FirstChild, tnNode);
if (tmp != null)
return tmp;
}
}
tn = tn.NextSibling;
}
return null;
}
/// <summary>
/// 遍历所有节点,查找它的子结点,如果子结点包含 tnChild,则返回此结点.
/// </summary>
/// <param name="tnChild">子结点</param>
/// <returns></returns>
/// 创建时间:2022-07-18 最后一次修改时间:2022-07-18
public TreeNode_ FindPrent(TreeNode_ tnStart, TreeNode_ tnChild)
{
TreeNode_ tnTemp = tnStart;
while(tnTemp != null)
{
//lg.D("tnTemp = " + ((TreeViewTreeNode_)tnTemp).DispalyText, "public TreeNode_ FindPrent");
if (tnTemp.IsMyChild(tnChild))
return tnTemp;
TreeNode_ tnResult = FindPrent(tnTemp.FirstChild, tnChild);
if(tnResult != null)
{
return tnResult;
}
tnTemp = tnTemp.NextSibling;
}
return null;
}
}
public class TreeViewTreeNode_ : TreeNode_
{
public const string m_Split = "||||";
/// <summary>
/// 键值,TreeView 的TreeNode的name,是唯一的
/// </summary>
public Int64 KeyValue { get; set; }
/// <summary>
/// 子节点的键值,-1 为没有。
/// </summary>
public Int64 FirstChildKeyValue { get; set; }
/// <summary>
/// 父节点的键值,如果为空,则为根节点
/// </summary>
public Int64 NextSiblingKeyValue { get; set; }
/// <summary>
/// 分隔字符串
/// </summary>
public string SplitString { get { return m_Split; } }
/// <summary>
/// 在TreeView显示的文本
/// </summary>
public string DispalyText { get; set; }
/// <summary>
/// 在TreeView显示文本的字体颜色
/// </summary>
public Color DisplayForeColor { get; set; }
/// <summary>
/// 在TreeView显示文本的背景颜色
/// </summary>
public Color DisplayBackColor { get; set; }
/// <summary>
/// 在TreeView显示的字体
/// </summary>
public Font DispalyFont { get; set; }
public TreeViewTreeNode_(string sDispalyText = "")
{
KeyValue = GetNewKeyname();
FirstChildKeyValue = -1;
NextSiblingKeyValue = -1;
DispalyText = sDispalyText;
DisplayForeColor = Color.Black;
DisplayForeColor = Color.White;
DispalyFont = new Font("微软雅黑", 12);
}
public Int64 GetNewKeyname()
{
Int64 _t;
sdk.QueryPerformanceCounter(out _t);
return _t;
}
/// <summary>
/// 打包数据
/// </summary>
public string PackageData()
{
Data = Data + GetPackageDataAdd();
return Data;
}
public string GetPackageDataAdd()
{
//Data |||| KeyValue |||| FirstChildKeyValue |||| NextSiblingKeyValue |||| DispalyText |||| DisplayForeColor |||| DisplayBackColor |||| DispalyFont + |||| Length
// Length = |||| KeyValue |||| FirstChildKeyValue |||| NextSiblingKeyValue |||| DispalyText |||| DisplayForeColor |||| DisplayBackColor |||| DispalyFont + ||||
if (KeyValue == -1 || DispalyText == null || DispalyText == "" || DispalyFont == null )
throw new Exception(@"KeyValue == -1 || DispalyText == null || DispalyText == "" || DispalyFont == null");
string sAdd = m_Split + KeyValue.ToString() + m_Split + FirstChildKeyValue.ToString() + m_Split + NextSiblingKeyValue.ToString() + m_Split + DispalyText + m_Split;
System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();
string tmp = cc.ConvertToString(DisplayForeColor);
sAdd = sAdd + tmp + m_Split;
tmp = cc.ConvertToString(DisplayBackColor);
sAdd = sAdd + tmp + m_Split;
System.Drawing.FontConverter fc = new System.Drawing.FontConverter();
tmp = fc.ConvertToString(DispalyFont);
sAdd += tmp;
sAdd += m_Split;
sAdd += sAdd.Length.ToString();
return sAdd;
}
/// <summary>
/// 在磁盘中读取数据后,还要运行这个函数
/// </summary>
public void SplitData()
{
//Data |||| KeyValue |||| FirstChildKeyValue |||| FirstChilNextSiblingKeyValue |||| DispalyText |||| DisplayForeColor |||| DisplayBackColor |||| DispalyFont + |||| + Length
// Length = |||| KeyName |||| FirstChildKeyValue |||| NextSiblingKeyValue |||| DispalyText |||| DisplayForeColor |||| DisplayBackColor |||| DispalyFont + ||||
string NewData = "";
int nLengthStart = Data.LastIndexOf(m_Split, Data.Length);
if (nLengthStart == -1)
throw new Exception("nLengthStart = -1");
string sLength = Data.Substring(nLengthStart + m_Split.Length, Data.Length - nLengthStart - m_Split.Length);
if (!sLength._IsNumberString())
throw new Exception("!sLength.isNumberString()");
int nAttribute = System.Convert.ToInt32(sLength);
NewData = Data.Substring(0, Data.Length - nAttribute - sLength.Length);
string sAttribute = Data.Substring(NewData.Length + m_Split.Length, nAttribute - m_Split.Length - m_Split.Length);
StringList_ ls = sAttribute._Split(m_Split);
if (ls.Count != 7)
throw new Exception("sAttribute = " + sAttribute + "分隔sAttribute错误, ls.Count != 7");
KeyValue = System.Convert.ToInt64(ls[0]);
FirstChildKeyValue = System.Convert.ToInt64(ls[1]);
NextSiblingKeyValue = System.Convert.ToInt64(ls[2]);
DispalyText = ls[3];
System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();
DisplayForeColor = (System.Drawing.Color)cc.ConvertFromString(ls[4]);
DisplayBackColor = (System.Drawing.Color)cc.ConvertFromString(ls[5]);
System.Drawing.FontConverter fc = new System.Drawing.FontConverter();
DispalyFont = fc.ConvertFromString(ls[6]) as System.Drawing.Font;
Data = NewData;
}
};
/// <summary>
/// 树型数据显示
/// </summary>
public class TreeDataView_ : Tree_
{
//固定名子,原来用控件名子,但是一旦控件名字更改,就是个bug,文件打不开了。
private const string ConstName = "TreeView";
private TreeView m_TreeView;
/// <summary>
/// 树型控件是否改变
/// </summary>
public bool IsModify { set; get;}
/// <summary>
/// 文件件
/// </summary>
public string KeyBinaryFullPathName { get; set;}
/// <summary>
/// 显示的TreeView控件
/// </summary>
public TreeView DisplayTreeView { get { return m_TreeView; } set { m_TreeView = value; } }
public string Name { get; private set; }
public void AddChild(TreeViewTreeNode_ tnParent, TreeViewTreeNode_ tnChild)
{
if (tnParent == null || tnChild == null)
{
throw new Exception("tnParent == null || tnChild == null");
}
TreeViewTreeNode_ tnTemp = (TreeViewTreeNode_)tnParent.FirstChild;
if (tnTemp == null)
{
tnParent.FirstChild = tnChild;
tnParent.FirstChildKeyValue = tnChild.KeyValue;
}
else
{
AddSibling(tnTemp, tnChild);
}
IsModify = true;
}
/// <summary>
///
/// </summary>
/// <param name="tnRemove"></param>
/// <returns></returns>
/// 创建时间: ????-??-?? 最后一次修改时间:2022-05-30
public override bool RemoveNote(TreeNode_ tnRemove)
{
if (First == tnRemove)
{
First = First.NextSibling;
return true;
}
TreeViewTreeNode_ tnParent = (TreeViewTreeNode_)FindIsFirstChild(First, tnRemove);
if(tnParent == null)
{
TreeViewTreeNode_ tnPrevSibling = (TreeViewTreeNode_)FindPrevSibling(First, tnRemove);//上一个兄弟节点
if(tnPrevSibling == null)
{
return false;
}
else //上一个兄弟结点连结下一个兄弟结点
{
tnPrevSibling.NextSibling = tnRemove.NextSibling;
tnPrevSibling.NextSiblingKeyValue = (tnRemove as TreeViewTreeNode_).NextSiblingKeyValue;
return true;
}
}
else
{
tnParent.FirstChild = null;
tnParent.FirstChildKeyValue = -1;
return true;
}
}
/// <summary>
/// 添加兄弟节点
/// </summary>
/// <param name="tnFirstSibling">第一个兄弟</param>
/// <param name="tnNewSibling">新的兄弟节点</param>
public void AddSibling(TreeViewTreeNode_ tnFirstSibling, TreeViewTreeNode_ tnNewSibling)
{
if (tnNewSibling == null)
{
throw new Exception("给定的新兄弟节点不能为空!");
}
if (tnFirstSibling == null)
{
if (First != null)
{
throw new Exception("给定的第一个兄弟节点不能为空!");
}
else
{
First = tnNewSibling;
}
}
else
{
TreeViewTreeNode_ tnLastSibling = (TreeViewTreeNode_)GetLastSibling(tnFirstSibling);
tnLastSibling.NextSibling = tnNewSibling;
tnLastSibling.NextSiblingKeyValue = tnNewSibling.KeyValue;
}
}
/// <summary>
/// 在结点 tnFindStart 中的子结点中查找节点键值为 iKeyValue 的结点,包括结点 tnFindStart
/// </summary>
/// <param name="tnFindStart">开始查找的结点</param>
/// <param name="sKeyValue">键值</param>
/// <returns>返回第一次出现的结点</returns>
/// <exception cref="Exception"></exception>
/// 创建时间:????-??-?? 最后一次修改时间:2022-07-18
public TreeViewTreeNode_ Find(TreeViewTreeNode_ tnFindStart, string sKeyValue)
{
try
{
if (!sKeyValue._IsNumberString())
{
lg.D("错误: sKeyValue=“" + sKeyValue + "” 不是数字!", "TreeDataView_.Find");
return null;
}
return Find(tnFindStart, System.Convert.ToInt64(sKeyValue));
}
catch (Exception e)
{
lg.ShowError(e.Message, "TreeDataView_.Find");
lg.D(e.Message, "TreeDataView_.Find");
}
return null;
}
/// <summary>
/// 在结点 tnFindStart 中的子结点中查找节点键值为 iKeyValue 的结点,包括结点 tnFindStart
/// </summary>
/// <param name="tnFindStart">开始查找的结点</param>
/// <param name="iKeyValue">键值</param>
/// <returns>返回第一次出现的结点</returns>
/// <exception cref="Exception"></exception>
/// 创建时间:????-??-?? 最后一次修改时间:2022-07-18
public TreeViewTreeNode_ Find(TreeViewTreeNode_ tnFindStart, Int64 iKeyValue)
{
if (tnFindStart == null || iKeyValue == -1 )
throw new Exception("tnFindStart == null || iKeyValue == -1");
if (First == null) return null;
TreeViewTreeNode_ tnTmp = tnFindStart;
while (tnTmp != null)
{
if (tnTmp.KeyValue == iKeyValue) //查找节点本身
{
return tnTmp;
}
else
{
//查找节点的子节点
if(tnTmp.FirstChild != null)
{
TreeViewTreeNode_ tnFind = Find((TreeViewTreeNode_)tnTmp.FirstChild, iKeyValue);
if(tnFind != null)
{
return tnFind;
}
}
}
//如果都还没找到,继续查找兄弟节点
tnTmp = (TreeViewTreeNode_)tnTmp.NextSibling;
}
return null;
}
public TreeViewTreeNode_ FindDispalyText(TreeViewTreeNode_ tnFindStart, string sDispalyText)
{
if (tnFindStart == null || sDispalyText == "")
throw new Exception("tnFindStart == null || sDispalyText == \"\"");
if (First == null) return null;
TreeViewTreeNode_ tnTmp = tnFindStart;
while (tnTmp != null)
{
if (tnTmp.DispalyText == sDispalyText) //查找节点本身
{
return tnTmp;
}
else
{
//查找节点的子节点
if (tnTmp.FirstChild != null)
{
TreeViewTreeNode_ tnFind = FindDispalyText((TreeViewTreeNode_)tnTmp.FirstChild, sDispalyText);
if (tnFind != null)
{
return tnFind;
}
}
}
//如果都还没找到,继续查找兄弟节点
tnTmp = (TreeViewTreeNode_)tnTmp.NextSibling;
}
return null;
}
public TreeViewTreeNode_ GetFirst()
{
return (TreeViewTreeNode_)First;
}
/*
/// <summary>
/// 在父节点KeyName=sParentNoteKeyName所有子节后面再添加一个节点。
/// </summary>
/// <param name="sText"></param>
/// <param name="sFont"></param>
/// <param name="sData"></param>
/// <param name="sParentNoteKeyName"></param>
/// <returns></returns>
public void Add(string sParentNoteKeyName, string sDisplayText, Color cDisplayBackColor,
Color cDisplayForeColor, Font fDisplayFont = null, string sData = "")
{
TreeViewTreeNode_ tnParent = null;
if (sParentNoteKeyName.Trim().Length != 0)
{
tnParent = Find(null,sParentNoteKeyName);
if(tnParent == null)
{
string sError = "查找KeyName = " + sParentNoteKeyName + "的节点不存在!";
lg.d(sError, "TreeDataView_.Add");
throw new Exception(sError);
}
}
TreeViewTreeNode_ tnNew = new TreeViewTreeNode_();
if (fDisplayFont == null)
tnNew.DispalyFont = m_TreeView.Font;
else
tnNew.DispalyFont = fDisplayFont;
tnNew.DispalyText = sDisplayText;
tnNew.DisplayBackColor = cDisplayBackColor;
tnNew.DisplayForeColor = cDisplayForeColor;
tnNew.FirstChild = null;
tnNew.NextSibling = null;
if (tnParent != null) //添加子节点
{
tnNew.ParentKeyName = tnParent.KeyName;
AddChild(tnParent, tnNew);
}
else //添加根节点
{
tnNew.ParentKeyName = "";
AddSibling(First, tnNew);
}
++Count;
}
*/
public void Add(TreeViewTreeNode_ tvtParent,TreeViewTreeNode_ tvtNew)
{
if (tvtNew == null)
throw new Exception("tvtNew == null");
if(tvtParent == null)
{
if(First == null)
{
First = tvtNew;
}
else
{
this.AddSibling((TreeViewTreeNode_)First, tvtNew);
}
}
else
{
AddChild(tvtParent, tvtNew);
}
}
/// <summary>
/// 在LDlist列表中查找键值为 iKeyValue 的结点
/// </summary>
/// <param name="tnList"></param>
/// <param name="iFirstChildKeyValue"></param>
/// <returns></returns>
/// 创建时间:????-??-?? 最后一次修改时间:2022-07-18
private TreeViewTreeNode_ Find(DList_<TreeViewTreeNode_> tnList, Int64 iKeyValue)
{
DListNote_<TreeViewTreeNode_> tn = tnList.First;
while (tn != null)
{
if (tn.Data.KeyValue == iKeyValue)
return tn.Data;
tn = tn.Next;
}
return null;
}
/// <summary>
/// 从文件中建立树,返回上次TTreeNote最后一次所选节点Name
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
/// 创建时间:????-??-?? 最后一次修改时间:2022-07-30
public string BuildInFile()
{
Clear(); //清除数据
KeyBinaryFile_ kbf = new KeyBinaryFile_(KeyBinaryFullPathName);
kbf.OpenFile();
//StringList_ ls = kbf.getIncludeKeyList(tvName + TreeViewTreeNode_.m_Split);
//这里有一个 bug ,旧的 TreeView 建立的,在新的TreeView显示不了,因为这跟名子相关,TreeView控件名子变了,为了不改动大量的代码
//每次文件保存时,写入TreeView控件的名子
//String tvName = kbf.getString("Last_TreeView_Name", "");
/*
String tvName = kbf.getString("Last_TreeView_Name", "");
if (tvName == "") //旧的文件是显示不了的,但是用新版本程序保存一下时,就可以显示出来了。
{
tvName = m_TreeView.Name;
lg.ShowError("无法读取最后写入文件时TreeView控件的名子,将启用新的默认TreeView控件名称。", "BuildInFile");
}
*/
StringList_ ls = kbf.getIncludeKeyList(ConstName + TreeViewTreeNode_.m_Split);
//以前版本的 constName 都是 TreeView 控件的名子,一旦控件名子改了,就建立不了数据了。
//-------------------------------------------------------------------------------------版本兼容
String oldConstName = "";
if (ls.Count == 0)
{
ls = kbf.getIncludeKeyList(TreeViewTreeNode_.m_Split);
if(ls.Count > 0)
{
oldConstName = ls[0]._Split(TreeViewTreeNode_.m_Split)[0];
}
lg.ShowInfo("旧版本文件!");
}
else
{
oldConstName = ConstName;
}
//------------------------------------------------------------------------------------------------
if (ls.Count == 0) return "-1";
DList_<TreeViewTreeNode_> tnList = new DList_<TreeViewTreeNode_>();
for (int i = 0; i < ls.Count; ++i)
{
string sData = kbf.getStringNotNull(ls[i]);
TreeViewTreeNode_ tvtn = new TreeViewTreeNode_();
tvtn.Data = sData;
tvtn.SplitData();
tnList.Add(tvtn);
}
//--------------------------------------建立树
//建立First节点
string sFirstKeyValue = kbf.getString(oldConstName + "_First");
string sSelectName = kbf.getString(oldConstName + "_Select");
kbf.ClearAll();
kbf = null;
int n = ls.IndexOf(oldConstName + TreeViewTreeNode_.m_Split + sFirstKeyValue);
if (n != -1)
{
First = tnList[n];
}
else
{
return "不能建立First节点!";
//throw new Exception("不能建立First节点!");
}
//建立关系
DListNote_<TreeViewTreeNode_> dn = tnList.First;
while(dn != null)
{
TreeViewTreeNode_ tvtn = dn.Data;
TreeViewTreeNode_ tn = null;
if (tvtn.FirstChildKeyValue != -1)
{
tn = Find(tnList, tvtn.FirstChildKeyValue);
if (tn == null)
throw new Exception("无法找到FirstChild!");
else
tvtn.FirstChild = tn;
}
if(tvtn.NextSiblingKeyValue != -1)
{
tn = Find(tnList, tvtn.NextSiblingKeyValue);
if (tn == null)
throw new Exception("无法找到NextSibling!");
else
tvtn.NextSibling = tn;
}
dn = dn.Next;
}
return sSelectName;
}
/// <summary>
/// 把树显示在树型控件上
/// </summary>
/// <param name="tnParent">树型控年的父节点,如果父节点为空,则直接建立根节点</param>
/// <param name="tvtnNote">节点开始处</param>
private void BuildTreeView(TreeNode tnParent, TreeViewTreeNode_ tvtnNote, int nImageIndex = 0)
{
TreeViewTreeNode_ tvtn = tvtnNote;
while(tvtn != null)
{
TreeNode tn = new TreeNode(tvtn.DispalyText);
tn.Name = tvtn.KeyValue.ToString();
tn.ForeColor = tvtn.DisplayForeColor;
tn.BackColor = tvtn.DisplayBackColor;
tn.NodeFont = tvtn.DispalyFont;
tn.ImageIndex = nImageIndex;
tn.SelectedImageIndex = nImageIndex;
tn.StateImageIndex = nImageIndex;
if (tnParent == null)
m_TreeView.Nodes.Add(tn);
else
tnParent.Nodes.Add(tn);
BuildTreeView(tn, (TreeViewTreeNode_)tvtn.FirstChild, nImageIndex);
tvtn = (TreeViewTreeNode_)tvtn.NextSibling;
}
}
/// <summary>
///
/// </summary>
/// <param name="isRecoverSelectd"></param>
/// 创建时间:????-??-?? 最后一次修改时间:2022-07-17
public void Dispaly(bool isRecoverSelectd = true)
{
string sSelectName = BuildInFile();
if (First == null)
{
lg.ShowInfo(" Dispaly First == null " + KeyBinaryFullPathName);
}
BuildTreeView(null,(TreeViewTreeNode_)First,13);
if(isRecoverSelectd)
{
TreeNode[] tnFind = m_TreeView.Nodes.Find(sSelectName,true);
if(tnFind.Length > 0)
{
m_TreeView.SelectedNode = tnFind[0];
if (tnFind[0].Parent != null)
tnFind[0].Parent.ForeColor = Color.Red;
//tnFind[0].Parent.NodeFont = new Font(tnFind[0].Parent.NodeFont, FontStyle.Bold);
tnFind[0].ForeColor = Color.Blue;
//tnFind[0].NodeFont = new Font(tnFind[0].NodeFont, FontStyle.Bold);
}
}
}
public void Save()
{
if (IsModify)
{
KeyBinaryFile_ kbf = new KeyBinaryFile_(KeyBinaryFullPathName);
if(First != null)
{
kbf.addValue(ConstName + "_First", ((TreeViewTreeNode_)First).KeyValue.ToString());
if (m_TreeView != null)
{
if (m_TreeView.SelectedNode != null)
kbf.addValue(ConstName + "_Select", m_TreeView.SelectedNode.Name);
else
kbf.addValue(ConstName + "_Select", "-1");
}
SaveSiblingAndChild((TreeViewTreeNode_)First,kbf);
kbf.saveAsFile(); //只保留一份
kbf.ClearAll();
kbf = null;
}
else
{
kbf.saveAsFile(kbf.FullPathName); //空文件
}
}
IsModify = false;
}
/// <summary>
/// 清除结节
/// </summary>
/// 创建时间:2022-04-15 最后一次修改时间:2021-04-15 未测试
public void Clear()
{
ClearData(First);
First = null;
if(m_TreeView != null) m_TreeView.Nodes.Clear();
}
/// <summary>
/// 遍历二叉树
/// </summary>
/// <param name="tn"></param>
/// 创建时间:2022-04-15 最后一次修改时间:2021-04-15 未测试
private void ClearData(TreeNode_ tn)
{
if (tn != null)
{
tn.Data = null;
ClearData(tn.FirstChild);
ClearData(tn.NextSibling);
tn.FirstChild = null;
tn.NextSibling = null;
}
}
public void SaveSiblingAndChild(TreeViewTreeNode_ tvtnNode,KeyBinaryFile_ kbf)
{
TreeViewTreeNode_ tvtn = tvtnNode;
while (tvtn != null)
{
string sKeyString = ConstName + tvtn.SplitString + tvtn.KeyValue.ToString();
kbf.addValue(sKeyString, tvtn.PackageData());
SaveSiblingAndChild((TreeViewTreeNode_)tvtn.FirstChild,kbf);
tvtn = (TreeViewTreeNode_)tvtn.NextSibling;
}
}
/// <summary>
/// 改变节点数据
/// </summary>
/// <param name="sData"></param>
public void ChangeSelectedNodeData(string sData)
{
TreeNode tn = m_TreeView.SelectedNode;
if (tn == null)
throw new Exception("tn == null");
TreeViewTreeNode_ tvtn = Find((TreeViewTreeNode_)First, m_TreeView.SelectedNode.Name);
if (tvtn == null)
throw new Exception("tvtn == null");
tvtn.Data = sData;
if (tn.NodeFont != null)
{
tvtn.DispalyFont = tn.NodeFont;
}
tvtn.DispalyText = tn.Text;
tvtn.DisplayBackColor = tn.BackColor;
tvtn.DisplayForeColor = tn.ForeColor;
IsModify = true;
}
public void SetFirstData(string sData)
{
if (First != null)
First.Data = sData;
IsModify = true;
}
/// <summary>
/// TreeView控件的修改节点事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="Exception"></exception>
/// 创建时间: ????-??-?? 最后一次修改时间:2022-03-05
public void AfterLabelEdit_Event(object sender, NodeLabelEditEventArgs e)
{
if(e.Label != null && e.Label.Trim() != "")
{
UpdateTreeViewNote(e.Node, e.Label);
IsModify = true;
}
else
{
e.CancelEdit = true;
}
}
/// <summary>
/// TreeView的节点属性改变,同时更新Tree_的节点。
/// </summary>
/// <param name="tnModify">发生改变的节点</param>
/// <param name="sDispalyText">所显示的文本</param>
/// <exception cref="Exception"></exception>
/// 创建时间: 2022-03-05 最后一次修改时间:2022-03-05
public void UpdateTreeViewNote(TreeNode tnModify, string sDispalyText = "")
{
TreeViewTreeNode_ tnFind = Find((TreeViewTreeNode_)First, tnModify.Name);
if (tnFind == null)
throw new Exception("tnFind = null");
if (sDispalyText != "")
tnFind.DispalyText = sDispalyText;
else
tnFind.DispalyText = tnModify.Text;
if (tnModify.NodeFont == null) //如果没有设置时,新建的节点是NULL,自动跟TreeView的Font相同
{
tnFind.DispalyFont = m_TreeView.Font;
}
else
{
tnFind.DispalyFont = tnModify.NodeFont;
}
tnFind.DisplayBackColor = tnModify.BackColor;
tnFind.DisplayForeColor = tnModify.ForeColor;
}
public void TreeViewSelectedNodeAddNewNode(string sDisplayText = "新节点")
{
TreeViewTreeNode_ tvtnNew = new TreeViewTreeNode_();
tvtnNew.Data = "";
tvtnNew.DispalyFont = m_TreeView.Font;
tvtnNew.DispalyText = sDisplayText;
tvtnNew.DisplayBackColor = m_TreeView.BackColor;
tvtnNew.DisplayForeColor = m_TreeView.ForeColor;
tvtnNew.NextSibling = null;
TreeNode tn = new TreeNode(sDisplayText);
tn.Name = tvtnNew.KeyValue.ToString();
if (m_TreeView.SelectedNode == null)
{
m_TreeView.Nodes.Add(tn);
Add(null, tvtnNew);
}
else
{
m_TreeView.SelectedNode.Nodes.Add(tn);
TreeViewTreeNode_ tvtnParent = Find((TreeViewTreeNode_)First, m_TreeView.SelectedNode.Name);
if (tvtnParent == null)
{
string sErrorInfo = "TreeDataView_中无法找到 m_TreeView.SelectedNode.Name= “" + m_TreeView.SelectedNode.Name + "” 的节点";
lg.ShowError(sErrorInfo, "TreeDataView_.TreeViewSelectedNodeAddNewNode");
lg.D(sErrorInfo, "TreeDataView_.TreeViewSelectedNodeAddNewNode");
return;
}
AddChild(tvtnParent, tvtnNew);
}
m_TreeView.SelectedNode = tn;
IsModify = true;
}
public void TreeViewAddNodeChild(TreeNode tnParent, string sChildDisplayText)
{
if (tnParent == null)
throw new Exception("tnParent == null");
TreeViewTreeNode_ tvtnNew = new TreeViewTreeNode_();
tvtnNew.Data = "";
tvtnNew.DispalyFont = m_TreeView.Font;
tvtnNew.DispalyText = sChildDisplayText;
tvtnNew.DisplayBackColor = m_TreeView.BackColor;
tvtnNew.DisplayForeColor = m_TreeView.ForeColor;
tvtnNew.NextSibling = null;
TreeNode tn = new TreeNode(sChildDisplayText);
tn.Name = tvtnNew.KeyValue.ToString();
TreeViewTreeNode_ tvtnParent = Find((TreeViewTreeNode_)First, m_TreeView.SelectedNode.Name);
if (tvtnParent == null)
throw new Exception("tvtnParent = null");
AddChild(tvtnParent, tvtnNew);
tnParent.Nodes.Add(tn);
}
/// <summary>
/// 改变字体或者前景色
/// </summary>
/// <param name="tnNode"></param>
public void TreeViewNodeAttributeChange(TreeNode tnNode)
{
TreeViewTreeNode_ tn = Find((TreeViewTreeNode_)First, tnNode.Name);
if(tn != null)
{
tn.DispalyFont = tnNode.NodeFont;
tn.DispalyText = tnNode.Text;
tn.DisplayForeColor = tnNode.ForeColor;
tn.DisplayBackColor = tnNode.BackColor;
IsModify = true;
}
}
/// <summary>
/// 把选中的节点上移一位
/// </summary>
/// <returns></returns>
/// 创建时间:2022-07-17 最后一次修改时间:2022-07-19
public string TreeViewSelectNoteMoveUp()
{
TreeNode tnSelect = m_TreeView.SelectedNode;
if(tnSelect == null)
{
return "未先定节点";
}
TreeViewTreeNode_ tvtnSelect = Find((TreeViewTreeNode_)First, tnSelect.Name);
TreeViewTreeNode_ tvtnSelectParent = (TreeViewTreeNode_)FindPrent(First, tvtnSelect);
if (tvtnSelectParent == null) return "未找到父结点!";
if (tvtnSelect == null) return "未找到值为:+ " + tnSelect.Name + " 的节点!";
TreeViewTreeNode_ prevOne = (TreeViewTreeNode_)FindPrevSibling(First, tvtnSelect);
if (prevOne == null) return "上一个兄弟结点不存在,无法上移!";
TreeViewTreeNode_ prevTwo = (TreeViewTreeNode_)FindPrevSibling(First, prevOne);
if (prevTwo == null)
{
//-----------------------------在TreeView中
TreeNode tnParent = tnSelect.Parent;
int nInsertIndex = tnSelect.Index - 1;
if (tnParent != null)
{
TreeNode newNode = (TreeNode)tnSelect.Clone(); //克隆选中的节点
tnParent.Nodes.Insert(nInsertIndex, newNode);
tnSelect.Remove();
//------------------------------------------在 Tree中
//交换的是第一个结点,要找出它的父结点
Int64 iTempKey = tvtnSelect.NextSiblingKeyValue;
tvtnSelectParent.FirstChildKeyValue = tvtnSelect.KeyValue;
tvtnSelect.NextSiblingKeyValue = prevOne.KeyValue;
prevOne.NextSiblingKeyValue = iTempKey;
TreeNode_ tnTemp = tvtnSelect.NextSibling;
tvtnSelectParent.FirstChild = tvtnSelect;
tvtnSelect.NextSibling = prevOne;
prevOne.NextSibling = tnTemp;
m_TreeView.SelectedNode = newNode;
}
}
else
{
//-----------------------------在TreeView中
TreeNode tnParent = tnSelect.Parent;
int nInsertIndex = tnSelect.Index - 1;
if (tnParent != null)
{
TreeNode newNode = (TreeNode)tnSelect.Clone(); //克隆选中的节点
tnParent.Nodes.Insert(nInsertIndex, newNode);
tnSelect.Remove();
//------------------------------------------在 Tree中
//交换中间结点,要找出它的父结点
Int64 iTempKey = tvtnSelect.NextSiblingKeyValue;
prevTwo.NextSiblingKeyValue = tvtnSelect.KeyValue;
tvtnSelect.NextSiblingKeyValue = prevOne.KeyValue;
prevOne.NextSiblingKeyValue = iTempKey;
TreeNode_ tnTemp = tvtnSelect.NextSibling;
prevTwo.NextSibling = tvtnSelect;
tvtnSelect.NextSibling = prevOne;
prevOne.NextSibling = tnTemp;
m_TreeView.SelectedNode = newNode;
}
}
return "";
}
/// <summary>
/// 把选中的节点下移一位
/// </summary>
/// <returns></returns>
/// 创建时间:2022-07-17 最后一次修改时间:2022-07-19
public string TreeViewSelectNoteMoveDown()
{
TreeNode tnSelect = m_TreeView.SelectedNode;
if (tnSelect == null)
{
return "未先定节点";
}
TreeViewTreeNode_ tvtnSelect = Find((TreeViewTreeNode_)First, tnSelect.Name);
TreeViewTreeNode_ tvtnSelectParent = (TreeViewTreeNode_)FindPrent(First, tvtnSelect);
if (tvtnSelectParent == null) return "未找到父结点!";
if (tvtnSelect == null) return "未找到值为:+ " + tnSelect.Name + " 的节点!";
TreeViewTreeNode_ nextOne = (TreeViewTreeNode_)tvtnSelect.NextSibling;
if (nextOne == null) return "下一个兄弟结点不存在,无法下移!";
TreeViewTreeNode_ prevOne = (TreeViewTreeNode_)FindPrevSibling(First, tvtnSelect);
if (prevOne == null) //第一节点移到下面
{
//-----------------------------在TreeView中
TreeNode tnParent = tnSelect.Parent;
int nInsertIndex = tnSelect.Index + 2;
if (tnParent != null)
{
TreeNode newNode = (TreeNode)tnSelect.Clone(); //克隆选中的节点
tnParent.Nodes.Insert(nInsertIndex, newNode);
tnSelect.Remove();
//------------------------------------------在 Tree中
//交换的是第一个结点,要找出它的父结点
Int64 iTempKey = nextOne.NextSiblingKeyValue;
tvtnSelectParent.FirstChildKeyValue = nextOne.KeyValue;
nextOne.NextSiblingKeyValue = tvtnSelect.KeyValue;
tvtnSelect.NextSiblingKeyValue = iTempKey;
TreeNode_ tnTemp = nextOne.NextSibling;
tvtnSelectParent.FirstChild = nextOne;
nextOne.NextSibling = tvtnSelect;
tvtnSelect.NextSibling = tnTemp;
m_TreeView.SelectedNode = newNode;
}
}
else
{
//-----------------------------在TreeView中
TreeNode tnParent = tnSelect.Parent;
int nInsertIndex = tnSelect.Index + 2;
if (tnParent != null)
{
TreeNode newNode = (TreeNode)tnSelect.Clone(); //克隆选中的节点
tnParent.Nodes.Insert(nInsertIndex, newNode);
tnSelect.Remove();
//------------------------------------------在 Tree中
//交换中间结点
Int64 iTempKey = nextOne.NextSiblingKeyValue;
prevOne.NextSiblingKeyValue = nextOne.KeyValue;
nextOne.NextSiblingKeyValue = tvtnSelect.KeyValue;
tvtnSelect.NextSiblingKeyValue = iTempKey;
TreeNode_ tnTemp = nextOne.NextSibling;
prevOne.NextSibling = nextOne;
nextOne.NextSibling = tvtnSelect;
tvtnSelect.NextSibling = tnTemp;
m_TreeView.SelectedNode = newNode;
}
}
return "";
}
public void TreeViewSelectedNodeDelete()
{
TreeNode tn = m_TreeView.SelectedNode;
if (tn != null)
{
if (MessageBox.Show("真的要删除“" + tn.Text + "”吗?", "删除提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
m_TreeView.Nodes.Remove(tn);
if (First == null)
throw new Exception("First==null");
TreeViewTreeNode_ tnRemove = Find((TreeViewTreeNode_)First, tn.Name);
if (tnRemove == null)
throw new Exception("tnRemove == null");
if (!RemoveNote(tnRemove))
{
throw new Exception(" RemoveNote(tnRemove) == False");
}
IsModify = true;
}
}
}
public TreeNode TreeViewAddRootNode(string sText = "")
{
TreeViewTreeNode_ tvtnNew = GetinitTreeViewTreeNode(sText);
Add(null, tvtnNew);
TreeNode tnNew;
if (sText != "")
tnNew = m_TreeView.Nodes.Add(sText);
else
tnNew = m_TreeView.Nodes.Add("新节点");
tnNew.Name = tvtnNew.KeyValue.ToString();
IsModify = true;
return tnNew;
}
public string GetTreeNodeData(TreeNode tn)
{
TreeViewTreeNode_ tvtn = Find((TreeViewTreeNode_)First, tn.Name);
if (tvtn == null)
throw new Exception("tvtn == null");
return tvtn.Data;
}
public void SetTreeNodeData(TreeNode tn, string sData)
{
TreeViewTreeNode_ tvtn = Find((TreeViewTreeNode_)First, tn.Name);
if (tvtn == null)
throw new Exception("tvtn == null");
tvtn.Data = sData;
IsModify = true;
}
public string GetFirstNodeData()
{
if (First != null)
return First.Data;
else
return "";
}
public void DragDrop_Event(object sender, DragEventArgs e)
{
//获得拖放中的节点
TreeNode tnMove = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
//根据鼠标坐标确定要移动到的目标节点
Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
TreeNode tnTarget = m_TreeView.GetNodeAt(pt);
if (tnTarget == null) { return; }
if (tnMove.Nodes.Find(tnTarget.Name, true).Length != 0)
{
lg.ShowError("无法移动父节点到到它子节点中去!", null);
return;
}
//不需要变动,因为把tnMove放在tnTarget下为子节点,而tnMove本身就是tnTarget的最后一个子节点。
if (tnTarget.Nodes.Count > 0 && tnTarget.Nodes[tnTarget.Nodes.Count - 1] == tnMove)
return;
//----------------------------------------------------------------在Tree_上操作
TreeViewTreeNode_ ltnMove = Find((TreeViewTreeNode_)First, tnMove.Name);
if(ltnMove == null)
{
throw new Exception("无法找到节点:" + tnMove.Text);
}
//除去ltnMove
TreeViewTreeNode_ ltnPrevSibling = (TreeViewTreeNode_)FindPrevSibling(First, ltnMove);
if (ltnPrevSibling != null)
{
ltnPrevSibling.NextSibling = ltnMove.NextSibling;
ltnPrevSibling.NextSiblingKeyValue = ltnMove.NextSiblingKeyValue;
//一定要设置ltnMove为孤链
ltnMove.NextSibling = null;
ltnMove.NextSiblingKeyValue = -1;
}
else
{
TreeViewTreeNode_ ltnParent = (TreeViewTreeNode_)FindIsFirstChild(First, ltnMove);
if (ltnParent != null)
{
if (ltnMove.NextSibling != null)
{
ltnParent.FirstChild = ltnMove.NextSibling;
ltnParent.FirstChildKeyValue = (ltnMove.NextSibling as TreeViewTreeNode_).KeyValue;
//一定要设置ltnMove为孤链
ltnMove.NextSibling = null;
ltnMove.NextSiblingKeyValue = -1;
}
else
{
ltnParent.FirstChild = null;
ltnParent.FirstChildKeyValue = -1;
}
}
else
{
throw new Exception("链接错误!");
}
}
//查找目标节点
TreeViewTreeNode_ ltnTarget = (TreeViewTreeNode_)Find((TreeViewTreeNode_)First, tnTarget.Name);
if (ltnTarget == null)
{
throw new Exception("无法找到节点:" + tnTarget.Text);
}
//在目标节点未加上子节点ltnMove
AddChild((TreeViewTreeNode_)ltnTarget, (TreeViewTreeNode_)ltnMove);
//-------------------------------------------------------------在控件上操作
//如果目标节点为分组则添加到下级节点的未端,如果目标节点为素材文件则添加为同级节点
TreeNode NewMoveNode = (TreeNode)tnMove.Clone();
tnTarget.Nodes.Insert(tnTarget.Nodes.Count, NewMoveNode);
//更新当前拖动的节点选择
m_TreeView.SelectedNode = NewMoveNode;
//展开目标节点,便于显示拖放效果
tnTarget.Expand();
//移除拖放的节点
tnMove.Remove();
IsModify = true;
}
public void ItemDrag_Event(object sender, ItemDragEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_TreeView.DoDragDrop(e.Item, DragDropEffects.Move);
}
}
public void DragEnter_Event(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(TreeNode)))
e.Effect = DragDropEffects.Move;
else
e.Effect = DragDropEffects.None;
}
public TreeViewTreeNode_ GetinitTreeViewTreeNode(string sText)
{
TreeViewTreeNode_ tvtnNew = new TreeViewTreeNode_();
if (sText != "")
tvtnNew.DispalyText = sText;
else
tvtnNew.DispalyText = "新节点";
if (m_TreeView.Font == null)
throw new Exception("m_TreeView.Font== null");
tvtnNew.DispalyFont = m_TreeView.Font;
tvtnNew.DisplayBackColor = m_TreeView.BackColor;
tvtnNew.DisplayForeColor = m_TreeView.ForeColor;
return tvtnNew;
}
public void WriteToFile(KeyBinaryFile_ kbf)
{
}
public void ReadFromFile(KeyBinaryFile_ kbf)
{
}
public void ReadFromTreeView(TreeView tv)
{
}
public TreeDataView_(string sFullPathName, TreeView tvDisplayTreeView)
{
KeyBinaryFullPathName = sFullPathName;
First = null;
m_TreeView = tvDisplayTreeView;
if (m_TreeView != null)
{
m_TreeView.Nodes.Clear();
}
}
public void SetDisplayTreeView(TreeView tvDisplayTreeView)
{
m_TreeView = tvDisplayTreeView;
if(m_TreeView != null)
{
m_TreeView.Nodes.Clear();
}
}
};
}
#endif
Tree_ 打包TreeView控件 C#相关推荐
- dev treeview控件_在Winform开发框架中使用DevExpress的TreeList和TreeListLookupEdit控件
DevExpress提供的树形列表控件TreeList和树形下拉列表控件TreeListLookupEdit都是非常强大的一个控件,它和我们传统Winform的TreeView控件使用上有所不同,我一 ...
- C# 在DataGridView中,点击单元格调出 TreeView控件 或 ListBox控件
1.调出 TreeView控件 或 ListBox控件 private void deductGrid1_CellClick(object sender, DataGridViewCellEvent ...
- 递归遍历文件夹,并添加到TreeView控件中
遍历文件夹,并把所有节点增加到TreeView控件中,这里单独写成了一个静态类,传入根目录节点和指定的目录这两个参数即可,可以稍作扩展用于其他方案 View Code 1 using System; ...
- WinForm开发中针对TreeView控件改变当前选择节点的字体与颜色
WinForm开发中针对TreeView控件改变当前选择节点的字体与颜色 在B/S开发中,对TreeView控件要改变当前选中节点的颜色比较方便,其有相应的SelectedNodeChanged事件 ...
- matlab treeview,treeview控件
高分在线等:C#中如何给treeview控件上的节点前添比如有一treeview1控件,需要在其所有节点前均加一静态小图标(所有节点我也新手,正好刚用过这功能.如下: 1. 到工具箱中拖一个image ...
- C# 系统应用之TreeView控件 (一).显示树状磁盘文件目录及加载图标
在C#系统应用毕设U盘防御软件中需要实现文件不可恢复的删除,首先需要实现类似于资源管理器的界面,通过TreeView控件显示"我的电脑"所有磁盘文件树状目录并加载相应图标.显示结果 ...
- TreeView控件的展开与折叠
在窗体中添加一个TreeView控件,设置CheckBox属性为True,绑定数据 Archive jkj = new Archive();//自定义类 public void Bin ...
- 给TreeView控件绑定数据
TreeView控件显示Node对象的等级体系结构,每个Node对象包含了一个标签和可选的点位图.TreeView控件通常用于显示文档头.索引中的条目.磁盘上的文件和目录或者可以显示为等级结构的各种其 ...
- 部门树形结构,使用Treeview控件显示部门
部门树形结构.设计张部门表用于存储部门编码.名称.上级部门id,使用Treeview控件显示部门树,并实现部门增删改.移动.折叠等功能.特别提示,部门有层级关系,可用donetbar的adtree控件 ...
最新文章
- 工作5年才有自己博客...汗...
- Java浅克隆与深克隆区别详解与实现,以及String类型属性克隆为什么不受影响?克隆clone()方法中为什么是super.clone()
- C++ STL学习之容器set和multiset (补充材料)
- 关于restful协议很多人的误解
- 回归素材(part2)--机器学习与R语言
- mysql 常用命令的使用_MySQL基本命令
- Python3.x爬虫教程:爬网页、爬图片、自己主动登录
- 北京地铁规划大全(图),买房子可以参考一下
- still a week before on board
- PYG解密小组的Visual Assist X插件完全卸载
- 判断浏览器内核是否是IE8及其以下
- 教你制作一个自动发摩斯电码的PYTHON小程序
- Spring Cloud 快速入门指南(一)
- Week 10. 第189-204题
- c语言sqrt是double,实现double sqrt(double num)
- VMware连接不上网络解决办法
- do还是doing imagine加to_to-do和doing用法及区别
- MBR+双硬盘(固态+机械)双系统(win+ubuntu21.04)装机教程
- 【Ansible】 Ansible 模块 setup 与 stat 模块用法
- 跨境电商独立站模式分析
热门文章
- 从程序员到系统分析员(转)
- 三维住房模型中的构件如何线框显示?
- html做微信logo,纯CSS实现一个微信logo,需要几个标签?_html/css_WEB-ITnose
- 彻底理解0-1背包问题
- excel怎么批量提取指定网站内容-免费指定网站提取导出excel软件
- HDU2060 Snooker斯诺克
- 计算机应用技能大赛方案,小学电脑技能大赛活动方案
- SpringBoot 集成 ElasticSearch 全文搜索(步骤非常的详细)
- 田野调查手记·浮山摩崖石刻(十六)
- html文档转换word,如何把HTML文档转换成Word文件?