C#创建一种新的文件格式

 

/*****************************************************************************

创建时间       :2019年06月16日

文件名         :KeyBinaryFile_.cs

功能           :利用二进制文件读写创建一个新的文件格式。
  
作者           :李锋

微信号         :lifengwx2005

Email          :ruizhilf@139.com
  
联系电话       :1382877886

----------------------最后一次修改时间:2019年07月03日
 
*******************************************************************************/
using System;
using System.IO;

#if _WINDOWS_PLATFORM_
#if _WINDOWS_DESKTOP_
        using System.Windows.Forms;
#endif
#endif

namespace lf
{

    /*
     *  public override void Close()
        关闭 BinaryWriter 对象和基础流。 
        2 public virtual void Flush()
        清理当前编写器的所有缓冲区,使得所有缓冲数据写入基础设备。 
        3 public virtual long Seek(int offset,SeekOrigin origin ) 
        设置当前流内的位置。 
        4 public virtual void Write(bool value ) 
        把一个单字节的布尔值写入到当前流中,0 表示 false,1 表示 true。 
        5 public virtual void Write(byte value ) 
        把一个无符号字节写入到当前流中,并把流的位置往前移一个字节。 
        6 public virtual void Write(byte[] buffer ) 
        把一个字节数组写入到基础流中。 
        7 public virtual void Write(char ch ) 
        把一个 Unicode 字符写入到当前流中,并把流的当前位置按照所使用的编码和要写入到流中的指定的字符往前移。 
        8 public virtual void Write(char[] chars ) 
        把一个字符数组写入到当前流中,并把流的当前位置按照所使用的编码和要写入到流中的指定的字符往前移。  
        9 public virtual void Write(double value ) 
        把一个 8 字节浮点值写入到当前流中,并把流位置往前移八个字节。 
        10 public virtual void Write(int value ) 
        把一个 4 字节有符号整数写入到当前流中,并把流位置往前移四个字节。 
        11 public virtual void Write(string value ) 
        把一个以长度为前缀的字符串写入到 BinaryWriter 的当前编码的流中,并把流的当前位置按照所使用的编码和要写入到流中的指定的字符往前移。 
    */

    /// <summary>
    /// 数据类型
    /// </summary>
    public enum KeyData_Type
    {
        /*
  
        从当前流中读取 Boolean 值,并使该流的当前位置提升 1 个字节。 
        public virtual bool ReadBoolean();       

        从当前流中读取下一个字节,并使流的当前位置提升 1 个字节。 
        public virtual byte ReadByte();
       
        从当前流中读取指定的字节数以写入字节数组中,并将当前位置前移相应的字节数。       
        public virtual byte[] ReadBytes(int Count);

        从当前流中读取下一个字符,并根据所使用的 Encoding 和从流中读取的特定字符,提升流的当前位置。       
        public virtual char ReadChar();

        从当前流中读取指定的字符数,并以字符数组的形式返回数据,然后根据所使用的 Encoding 和从流中读取的特定字符,将当前位置前移。
        public virtual char[] ReadChars(int Count);

        从当前流中读取十进制数值,并将该流的当前位置提升十六个字节。       
        public virtual decimal ReadDecimal();

        从当前流中读取 8 字节浮点值,并使流的当前位置提升 8 个字节。       
        public virtual double ReadDouble();
        
        从当前流中读取 2 字节有符号整数,并使流的当前位置提升 2 个字节。      
        public virtual short ReadInt16();        

        从当前流中读取 4 字节有符号整数,并使流的当前位置提升 4 个字节。        
        public virtual int ReadInt32();
       
        从当前流中读取 8 字节有符号整数,并使流的当前位置提升 8 个字节。        
        public virtual long ReadInt64();
       
        从此流中读取 1 个有符号字节,并使流的当前位置提升 1 个字节。       
        public virtual sbyte ReadSByte();

        从当前流中读取 4 字节浮点值,并使流的当前位置提升 4 个字节。       
        public virtual float ReadSingle();

        从当前流中读取一个字符串。 字符串有长度前缀,一次 7 位地被编码为整数。       
        public virtual string ReadString();
        
        使用 Little-Endian 编码从当前流中读取 2 字节无符号整数,并将流的位置提升 2 个字节。        
        public virtual ushort ReadUInt16();        

        从当前流中读取 4 字节无符号整数并使流的当前位置提升 4 个字节。        
        public virtual uint ReadUInt32();

        从当前流中读取 8 字节无符号整数并使流的当前位置提升 8 个字节。       
        public virtual ulong ReadUInt64();
        */

        /// <summary>
        /// Boolean值,长度1
        /// </summary>
        dtBoolean = 1,

        /// <summary>
        /// byte 无符号整数,存储空间1B,取值范0~255,
        /// </summary>
        dtByte = 2,

        /// <summary>
        /// Byte数组值
        /// </summary>
        dtByteArray = 3,


        /// <summary>
        /// 字符值,长度为sizeof(char)
        /// </summary>
        dtChar = 4,

        /// <summary>
        /// 字符数组值,长度为sizeof(char) * ArrayLength
        /// </summary>
        dtCharArray = 5,

        /// <summary>
        /// (decimal是高精度 128bit,浮点型),长度为sizeof(decimal)
        /// </summary>
        dtDecimal = 6,

        /// <summary>
        /// (double 双精度浮点64bit),长度为sizeof(double)
        /// </summary>
        dtDouble = 7,


        /// <summary>
        /// 2个字节,16位有符号整数,长度为2。
        /// </summary>
        dtInt16 = 8,

        /// <summary>
        /// 4个字节,32位有符号整数,长度为4。
        /// </summary>
        dtInt32 = 9,

        /// <summary>
        /// 8个字节,64位有符号整数,长度为8。
        /// </summary>
        dtInt64 = 10,

        /// <summary>
        /// sbyte 有符号整数,存储空间1B, 取值范围-128~127,,长度为1。
        /// </summary>
        dtSbyte = 11,


        /// <summary>
        /// 4 字节浮点值,长度为4,sizeof(single);
        /// </summary>
        dtSingle = 12,

        /// <summary>
        /// 字符串
        /// </summary>
        dtString = 13,

        /// <summary>
        /// 2个字节,16位无符号整数,长度为2。
        /// </summary>
        dtUInt16 = 14,

        /// <summary>
        /// 4个字节,32位无符号整数,长度为2。
        /// </summary>
        dtUInt32 = 15,

        /// <summary>
        /// 8个字节,64位无符号整数,长度为2。
        /// </summary>
        dtUInt64 = 16 
         

    }


    /// <summary>
    ///=============      0            ============     ==========    ==========          ==========       ===============      0         ============ 
    ///记录个数int32     byte0         关键字长度int32     关键字       数据类型Byte        数据长度int32      数据               byte0     关键字长度int32
    /// </summary>
    public class KeyData_ : IComparable
    {
        
        int IComparable.CompareTo(object obj)
        {
            if (obj is KeyData_)
                return Keyword.CompareTo((obj as KeyData_).Keyword);
            else if (obj is string)  //直接比较字符键
                return Keyword.CompareTo((obj as string));
            else
                throw new NotImplementedException();
        }

        public override bool Equals(Object obj)
        {
            if(obj is KeyData_)
                return Keyword.Equals( (obj as KeyData_).Keyword);
            else if(obj is string)
                return Keyword.Equals((obj as string));
            else
                throw new NotImplementedException();

        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public KeyData_(string sKeyword)
        {
            Keyword = sKeyword;
        }


        public KeyData_()
        {

        }


        /// <summary>
        /// 关键字,只读   
        /// </summary>
        public string Keyword { get; set; }


        /// <summary>
        /// 关键字所占内存大小
        /// </summary>
        public int KeywordLength { get; set; }

        /// <summary>
        /// 数据,byte数组
        /// </summary>
        public byte[] Data { get; set; }


        /// <summary>
        /// 数据类型
        /// </summary>
        public KeyData_Type DataType{ get; set; }


        /// <summary>
        /// 数据长度
        /// </summary>
        public int DataLength { get; set; }


        /// <summary>
        /// 数据最大长度为100MB。
        /// </summary>
        public static readonly int MaxDataLength = 1024 * 1024 * 100;


    }

    /// <summary>
    /// 
    /// </summary>
    public class KeyBinaryFile_
    {
 
        private SortList_<KeyData_> _slKey;

        public string FullPathName { get; set; }

        /// <summary>
        /// 初始化数据
        /// </summary>
        public virtual void InitData()
        {
            FullPathName = "";
            _slKey = new SortList_<KeyData_>(Sortord_.s_min_max);
        }


        /// <summary>
        /// 把文件名重命名,包括备份文件
        /// </summary>
        /// <param name="sFullPathName">文件全路径</param>
        /// <param name="sNewFileName">新文件名</param>
        public static void FileRename(string sFullPathName, string sNewFileName)
        {
            lg.FileRename(sFullPathName, sNewFileName);

            //如果存在备份文件,也重新命名
            lg.FileRename((sFullPathName + ".bck"), sNewFileName + "." + sFullPathName._FileNameExt(), true);
        }
 


        public DList_<KeyData_> DataList { get { return _slKey; } }


        /// <summary>
        /// 数据最大长度为1000万条
        /// </summary>
        private static readonly int iMaxRecordCount = 10000000;


        /// <summary>
        /// 每次打开文件时,把整个文件加载到内存中
        /// </summary>
        /// <param name="sFullPathName"></param>
        public void OpenFile(string sFullPathName)
        {
            if (sFullPathName == "" || sFullPathName.Trim().Length == 0)
            {
                throw new System.Exception("文件名不能为空!");
            }

            FullPathName = sFullPathName;

            OpenFile();
        }
 
        public void OpenFile()
        {          

            if (File.Exists(FullPathName)) //文件已存在,检查文件格式是否正确,如果正确,则加载文件内容
            {
                /*
                if (!File.Exists(FullPathName + ".bck"))
                {
#if _WINDOWS_DESKTOP_
                    if (MessageBox.Show("没有找到 “" + FullPathName._FileName() + "” 的备份文件 “" + FullPathName._FileName() + ".bck" + "”  ,这个文件可能格式错误,要删除重建吗?", "文件删除提示!", MessageBoxButtons.YesNo,
                        MessageBoxIcon.Warning) == DialogResult.Yes)
                    {
                        File.Delete(FullPathName);
                        saveAndBackupFile();
                        return;
                    }
                    else
                    {
                        return;
                    }
#endif
                }
                */
               

                BinaryReader br;

                try
                {
                    br = new BinaryReader(new FileStream(FullPathName, FileMode.Open));
                }
                catch (IOException e)
                {
                    throw new System.Exception(e.Message + "\n Cannot open file.");
                }


                if (br.BaseStream.Length == 0)  //空白文件
                {
                    lg.ShowInfo("br.BaseStream.Length=" + br.BaseStream.Length.ToString(), null);
                    br.Close();
                    return;
                }

              

                //                                记录1                                                                                   记录2
                //=============   ============    ====================     =======    ===========      ============   ====    ========    ===============      
                //记录个数int32   (byte==0)边界    第一个关键字长度int32     关键字     数据类型Byte     数据长度int32   数据    byte0边界    第二个关键字长度   


                int iCount = br.ReadInt32();  //读取记录个数

                if (iCount > iMaxRecordCount || iCount < 0)
                {
                    throw new System.Exception("文件格式错误!");
                }


                if (iCount == 0) 空记录
                {
                    br.Close();
                    return;
                }


                if (br.BaseStream.Length <= sizeof(int) * 3 + sizeof(byte))  //有记录的最小文件长度
                {
                    throw new System.Exception("文件格式错误!");
                }

               

                while (iCount > 0)
                {
                    if (br.ReadByte() != 0)
                        throw new System.Exception("文件格式错误!");

                    KeyData_ kd = new KeyData_();

                    //读取关键字
                    kd.KeywordLength = br.ReadInt32();
                    if (kd.KeywordLength <= 0 || kd.KeywordLength > KeyData_.MaxDataLength)
                        throw new System.Exception("文件格式错误!");
                    byte[] bArray = br.ReadBytes(kd.KeywordLength);
                    kd.Keyword = System.Text.Encoding.Default.GetString(bArray);

                    //读取数据类型Byte
                    kd.DataType = (KeyData_Type)br.ReadByte();


                    //读取数据类型长度
                    kd.DataLength = br.ReadInt32();
                    if (kd.DataLength <= 0 || kd.DataLength > KeyData_.MaxDataLength)
                        throw new System.Exception("文件格式错误!");

                    //读取数据
                    kd.Data = br.ReadBytes(kd.DataLength);

                    _slKey.Add(kd);  //添加到链表上

                    --iCount;
                }
                 
                br.Close();


            }
            else
            {
                lg.ShowInfo(FullPathName + " 文件不存在!");
            }
        }

        /// <summary>
        /// 清除所有内容
        /// </summary>
        public void ClearAll()
        {
            _slKey.Clear();
        }


        //=============      0            ============     ==========    ==========          ==========       ===============      0         ============ 
        //记录个数int32     byte0         关键字长度int32     关键字       数据类型Byte        数据长度int32      数据               byte0     关键字长度int32
        private void writeKeyword(KeyData_ kd, BinaryWriter bw)
        {
            //写入边界
            bw.Write(System.Convert.ToByte(0)); //写入0

            //写入关键字长充与关键字
            byte[] byteKeyWordArray = System.Text.Encoding.Default.GetBytes(kd.Keyword);
            int byteKeywordLen = byteKeyWordArray.Length;

            bw.Write(byteKeywordLen);
            bw.Write(byteKeyWordArray);

            //写入数据类型
            bw.Write((byte)kd.DataType);

            /*
            switch (kd.DataType)
            {
                case KeyData_Type.dtBoolean:
                    {
                        kd.DataLength = sizeof(Boolean);           
                        break;
                    }
                case KeyData_Type.dtByte:
                    {
                        kd.DataLength = sizeof(byte);
                        break;
                    }
                case KeyData_Type.dtByteArray:
                    {
                        kd.DataLength = kd.Data.Length;
                        break;
                    }
                case KeyData_Type.dtChar:
                    {
                        kd.DataLength = sizeof(char);      
                        break;
                    }
                case KeyData_Type.dtCharArray:
                {
                        kd.DataLength = sizeof(char);
                        break;
                    }
                default:
                    throw new System.Exception(_sFileFormatError);
            }
            */


            //写入数据长度 
            bw.Write(kd.DataLength);

            //写入数据
            bw.Write(kd.Data, 0, kd.DataLength);
        }

        /// <summary>
        /// 把文件保存为一个完整路径
        /// </summary>
        /// <param name="sFullPathNane">完整路径</param>
        public void saveAsFile(string sFullPathNane = "")
        {
            string sPathName = sFullPathNane;

            if (sPathName == "")
            {
                sPathName = FullPathName;
            }


            if (sPathName.Trim().Length == 0)
            {
                throw new System.Exception("文件名不能为空!");
            }


            //检查是否正确的文件路径
            if (lg.isInvalid_filePath(sPathName))
            {
                throw new Exception("错误的文件名!");
            }


            if (File.Exists(sPathName))  //如果文件存在,怎么处理
            {
                File.Delete(sPathName);

                if (File.Exists(sPathName))
                {
                    throw new System.Exception("无法删除文件“" + sPathName + "”!");

                }
            }


            //if (_slKey.Count == 0)      return;       
            //写入文件,当_slKey.Count == 0时,只写入一个int数

            BinaryWriter bw;

            try
            {
                bw = new BinaryWriter(new FileStream(sPathName, FileMode.Create));


                bw.Write(_slKey.Count);  //写入记录个数


                // 写入文件
                try
                {
                    DListNote_<KeyData_> dnNote = _slKey.First;
                    while (dnNote != null)
                    {
                        KeyData_ kd = dnNote.Data;
                        writeKeyword(kd, bw);
                        dnNote = dnNote.Next;
                    }

                }
                catch (IOException e)
                {
                    Console.WriteLine(e.Message + "\n Cannot write to file.");
                    return;
                }

            }
            catch (IOException e)
            {
                throw new System.Exception(e.Message + "\n 不能创建文件.");
            }

            bw.Close();
        }


        /// <summary>
        /// 保存并自动备份文件
        /// </summary>
        public void saveAndBackupFile()
        {
            saveAsFile(FullPathName);
            saveAsFile(FullPathName + ".bck");
        }


        /// <summary>
        /// 保存备份文件
        /// </summary>
        public void saveBackupFile()
        {
            saveAsFile(FullPathName + ".bck");
        }


        /// <summary>
        /// 另存文件和文件备份
        /// </summary>
        /// <param name="sFullPathName"></param>
        public void saveAsAndBackupFile(string sFullPathName)
        {
            saveAsFile(sFullPathName);
            saveAsBackupFile(sFullPathName);
        }


        /// <summary>
        /// 另存备份文件
        /// </summary>
        /// <param name="sFullPathName"></param>
        public void saveAsBackupFile(string sFullPathName)
        {
            saveAsFile(sFullPathName + ".bck");
        }

        public byte[] getData(string sKey, KeyData_Type kdtCheck)
        {
            DListNote_<KeyData_> nFind = _slKey.FindNoteItem(new KeyData_(sKey));

            //if (nFind == null)
            //    throw new Exception("不存在Key \"" + sKey + "\" !");

            if (nFind == null)
                return null;    //如果没有找到key,返加空的数组指针

            if (nFind.Data.DataType != kdtCheck)
                throw new Exception("数据格式错误!");

            return nFind.Data.Data;
        }

        public KeyData_Type getDataType(string sKey)
        {
            DListNote_<KeyData_> nFind = _slKey.FindNoteItem(new KeyData_(sKey));

            if (nFind == null)
                throw new Exception("不存在Key \"" + sKey + "\" !");

            return nFind.Data.DataType;
        }


        public KeyData_ getKeyData(string sKey)
        {
            DListNote_<KeyData_> nFind = _slKey.FindNoteItem(new KeyData_(sKey));

            if (nFind == null)
                throw new Exception("不存在Key \"" + sKey + "\" !");

            return nFind.Data;
        }

        public bool getBoolean(string sKey)
        {
            return BitConverter.ToBoolean(getData(sKey, KeyData_Type.dtBoolean), 0);
        }


        public byte getByte(string sKey)
        {
            return getData(sKey, KeyData_Type.dtByte)[0];
        }

        public Byte[] getByteArray(string sKey)
        {
            return getData(sKey, KeyData_Type.dtByteArray);
        }


        public char getChar(string sKey)
        {
            return BitConverter.ToChar(getData(sKey, KeyData_Type.dtChar), 0);
        }


        public char[] getCharArray(string sKey)
        {
            return System.Text.ASCIIEncoding.Default.GetChars(getData(sKey, KeyData_Type.dtCharArray));
        }

        public decimal getDecimal(string sKey)
        {
            return Convert.ToDecimal(BitConverter.ToDouble(getData(sKey, KeyData_Type.dtDecimal), 0 /* Which byte position to convert */));

        }


        public double getDouble(string sKey)
        {
            return BitConverter.ToDouble(getData(sKey, KeyData_Type.dtDouble), 0);

        }


        public Int16 getInt16(string sKey)
        {
            return BitConverter.ToInt16(getData(sKey, KeyData_Type.dtInt16), 0);
        }


        public Int32 getInt32(string sKey)
        {
            return BitConverter.ToInt32(getData(sKey, KeyData_Type.dtInt32), 0);
        }

        /// <summary>
        /// 获取一个32位整数,如果Key不存在,则返回默认数值。
        /// </summary>
        /// <param name="sKey">关键字</param>
        /// <param name="iDefaultValue">默认数值</param>
        /// <returns></returns>
        public Int32 TryToGetInt32(string sKey, Int32 iDefaultValue = 0)
        {
            byte[] iData = getData(sKey, KeyData_Type.dtInt32);

            if (iData != null)
                return BitConverter.ToInt32(iData, 0);
            else
                return iDefaultValue;
        }

        public Int64 getInt64(string sKey)
        {
            return BitConverter.ToInt64(getData(sKey, KeyData_Type.dtInt64), 0);
        }


        public int getInt(string sKey, int iDufaultValue = 0)
        {
            byte[] bArray = getData(sKey, KeyData_Type.dtInt32);

            if (bArray != null)
                return BitConverter.ToInt32(bArray, 0);

            return 0;
        }


        public sbyte getSbyte(string sKey)
        {
            return (sbyte)getData(sKey, KeyData_Type.dtSbyte)[0];
        }


        public Single getSingle(string sKey)
        {
            return BitConverter.ToSingle(getData(sKey, KeyData_Type.dtSingle), 0);
        }

        public string getString(string sKey, string sDefaultValue = "")
        {
            byte[] bArray = getData(sKey, KeyData_Type.dtString);

            if (bArray == null)
                return sDefaultValue;
            else
                return System.Text.ASCIIEncoding.Default.GetString(bArray);
        }

        public string getStringNotNull(string sKey, string sDefaultValue = "")
        {
            byte[] bArray = getData(sKey, KeyData_Type.dtString);

            if (bArray == null)
                throw new Exception("bArray == null");
            else
                return System.Text.ASCIIEncoding.Default.GetString(bArray);
        }

#if _WINDOWS_DESKTOP_

        public string getTreeViewString(TreeView tv, TreeNode tn, string sDefaultValue = "")
        {
            string sKeyWord = getTreeViewKeyWordText(tv, tn);
            return getString(sKeyWord);
        }


        public void addTreeViewString(TreeView tv, TreeNode tn, string sText)
        {
            writeNote(tv.Name, tn);

            string sKeyWord = getTreeViewKeyWordText(tv, tn);
            addValue(sKeyWord, sText);
        }

#endif

        public KeyBinaryFile_(string sFullPathName)
        {
            InitData();
            FullPathName = sFullPathName;     
        }


        /// <summary>
        /// 释放内存
        /// </summary>
        ~KeyBinaryFile_()
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public KeyBinaryFile_()
        {
            InitData();
        }


        /// <summary>
        /// 如果键字符串存在,则覆盖原来的数值,如果不存在,则创建一个新的数值,创建一个新的sKey时,返回真,否则返回假。
        /// </summary>
        /// <param name="sKey">键字符串</param>
        /// <param name="iValue">整数值</param>
        /// <returns>创建一个新的键时,返回true,否则返回false</returns>
        public void addValue(string sKey, int iValue)
        {
            addValue(sKey, BitConverter.GetBytes((Int32)iValue), KeyData_Type.dtInt32);
        }

        public void addValue(string sKey, decimal d)
        {
            addValue(sKey, BitConverter.GetBytes((double)d), KeyData_Type.dtDecimal); ;
        }

        public void addValue(string sKey, char cValue)
        {
            addValue(sKey, BitConverter.GetBytes(cValue), KeyData_Type.dtChar);
        }


        public void addValue(string sKey, bool bValue)
        {
            addValue(sKey, BitConverter.GetBytes(bValue), KeyData_Type.dtBoolean);
        }

        public void addValue(string sKey, string sValue)
        {
            addValue(sKey, System.Text.ASCIIEncoding.Default.GetBytes(sValue), KeyData_Type.dtString);

        }

        public void addValue(string sKey, char[] cArray)
        {
            addValue(sKey, System.Text.ASCIIEncoding.Default.GetBytes(cArray), KeyData_Type.dtCharArray);

        }


        public void addValue(string sKey, byte[] bData, KeyData_Type kdtType)
        {
            if (sKey == null || sKey.Length == 0 || sKey.Trim().Length == 0)
                throw new System.Exception("sKey不能为空或者sKey不能为空字符!");

            if (bData.Length == 0)
            {
                //throw new System.Exception("数据不能为空!");
                //lg.D("数据不能为空!", "KeyBinaryFile_.addValue");
                return;
            }
                

            KeyData_ kd = new KeyData_(sKey);

            kd.Data = bData;
            kd.DataLength = bData.Length;
            kd.DataType = kdtType;

            DListNote_<KeyData_> kdExist = _slKey.FindNoteItem(kd); //查找一个存在的则


            if (kdExist != null) //已有sKey这个值,覆盖原来的数值。
            {
                kdExist.Data.Data = kd.Data;
                kdExist.Data.DataLength = bData.Length;
                kdExist.Data.DataType = kdtType;
                return;
            }

            _slKey.Add(kd);
        }


#if _WINDOWS_PLATFORM_//-------------------------------------------------------------------------Windows CS平台开始
#if _WINDOWS_DESKTOP_


        /// <summary>
        /// 
        /// </summary>
        /// <param name="tv">TreeView控件</param>
        /// 
        public void addValue(TreeView tv)
        {
            writeTreeNodeCollection(tv.Name, tv.Nodes);
        }


        private void writeTreeNodeCollection(string sKeyBranch, TreeNodeCollection tnc)
        {
            for (int i = 0; i < tnc.Count; ++i)
            {
                writeNote(sKeyBranch, tnc[i]);
                writeTreeNodeCollection(sKeyBranch, tnc[i].Nodes);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sKeyBranch">默认为TreeView的名子</param>
        /// <param name="tn"></param>
        private void writeNote(string sKeyBranch, TreeNode tn)
        {
            //Key字符串  TreeView.1.1.0.0  表示的意思是第一层,第一个位置的node,父节点无。
            //Key字符串  TreeView.2.3.1.2  表示的意思是第二层,第三个位置的node,父节点第一层的第二个节点。

            

            string sKey = sKeyBranch + "." + (tn.Level + 1).ToString() + "." + (tn.Index + 1).ToString();

            sKey += ".";

            if (tn.Parent != null)
            {
                sKey += (tn.Level).ToString();
                sKey = sKey + "." + (tn.Parent.Index + 1).ToString();
                
            }
            else
            {
                sKey += "0.0";
            }


            //Text + Font + ForeColor
            //保存文本,文本的字体属性,文本的字体颜色
            System.Drawing.FontConverter fc = new System.Drawing.FontConverter();

            System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();


            string sText = tn.Text + "||" + fc.ConvertToString(tn.NodeFont) + "||" + cc.ConvertToString(tn.ForeColor);

            addValue(sKey.Trim(), sText);
        }

        /// <summary>
        /// 获取tnRoot下的所有子节点
        /// </summary>
        /// <param name="tnRoot"></param>
        /// <param name="lsl"></param>
        private void TreeNoteGetChild(TreeNode tnParent, StringList_ lsAll)
        {
            if (lsAll.Count == 0) return;


            StringList_ lsRemainder = new StringList_(); //剩余项

            //1.找出所有子项

            StringList_ lsChild = new StringList_();

            for (int i = 0; i < lsAll.Count; ++i)
            {
                //Key字符串  TreeView.1.1.0.0  表示的意思是第一层,第一个位置的node,父节点无。
                //Key字符串  TreeView.2.3.1.2  表示的意思是第二层,第三个位置的node,父节点第一层的第二个节点。

                //错误: 节点1   TreeView.2.1.1.1 
                //       节点1   TreeView.2.1.1.2
                //       节点3   TreeView.?.?.2.1,    父节3应该加在那里?
                //所以应该       Treeview ???? ????,  除非只有一个根节点

                string sKey = lsAll[i];
                StringList_ lslTmp = sKey._Split(".");

                if (lslTmp.Count == 5)
                {
                    //找出所有出子项
                    if (  Convert.ToInt32(lslTmp[3]) == tnParent.Level + 1 &&  Convert.ToInt32(lslTmp[4]) == tnParent.Index + 1)
                    {
                        lsChild.Add(lsAll[i]);
                    }
                    else
                    {
                        lsRemainder.Add(lsAll[i]); //多余项
                    }
                }
                else
                {
                    lg.ShowInfo(lslTmp);
                }
            }

            //3.把子项按索引排序,性能很差,有时间再优化             

            StringList_ lsSort = new StringList_();

            for (int n = 1; n <= lsChild.Count; ++n)
            {

                for (int i = 0; i < lsChild.Count; ++i)
                {
                    //TreeView.2.31.14.24
                    string sKey = lsChild[i];
                    StringList_ lslTmp = sKey._Split(".");

                    if (lslTmp.Count == 5)
                    {
                        if (Convert.ToInt32(lslTmp[2]) == n)
                        {
                            lsSort.Add(lsChild[i]);
                        }
                    }
                }
            }
            //lg.stopTime();
            //lg.showTimeUsed(null);


            if (lsChild.Count != lsSort.Count) lg.ShowInfo("lsSub.Count != lsSort.Count,getTreeViewNoteChild.GetChild");

            for (int i = 0; i < lsSort.Count; ++i)
            {
                string sKey = lsSort[i];

                string sText = getString(sKey);

                StringList_ slText = sText._Split("||");

                if (slText.Count > 0)
                {
                    //设置内容
                    TreeNode tn = tnParent.Nodes.Add(slText[0]);

                    if (slText.Count > 1)
                    {
                        //设置字体
                        System.Drawing.FontConverter fc = new System.Drawing.FontConverter();
                        tn.NodeFont = fc.ConvertFromString(slText[1]) as System.Drawing.Font;

                        //设置前景色
                        if (slText.Count > 2)
                        {
                            System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();
                            tn.ForeColor = (System.Drawing.Color)cc.ConvertFromString(slText[2]);
                        }
                    }

                    TreeNoteGetChild(tn, lsRemainder);
                }
            }

        }


        /// <summary>
        /// 从文件中读取TreeView控件,并把它赋值于TreeView控件
        /// </summary>
        /// <param name="tv">TreeView控件</param>
        public void getTreeview(TreeView tv)
        {
            //throw new Exception("超过10个TreeNote会出现错误 ,请重写getTreeview(TreeView tv)函数");

            if (tv.Name.IndexOf(".") != -1) throw new Exception("TreeView控件名子不能包含字符“.”");

            //获取所有TreeView节点
            StringList_ lsAll = getIncludeKeyList(tv.Name + ".");
            DList_<StringList_> dlSplit = new DList_<StringList_>();

            for (int i = 0; i < lsAll.Count; ++i)
            {
                dlSplit.Add(lsAll[i]._Split("."));
            }

            tv.Nodes.Clear();
            if (lsAll.Count == 0) return;


            //第一层
            StringList_ lsOne = getIncludeKeyList(tv.Name + ".1");  //tv_Notepad.1.1.0.0

            for (int i = 0; i < lsOne.Count; ++i)
            {
                lsAll.RemoveItem(lsOne[i]);
            }

            for (int i = 0; i < lsOne.Count; ++i)
            {
                StringList_ slText = getString(lsOne[i])._Split("||");

                TreeNode tnRoot = tv.Nodes.Add(slText[0]);

                if (slText.Count > 1)
                {
                    //设置字体
                    System.Drawing.FontConverter fc = new System.Drawing.FontConverter();
                    tnRoot.NodeFont = fc.ConvertFromString(slText[1]) as System.Drawing.Font;


                    if (slText.Count > 2)
                    {
                        //设置前景色
                        System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();
                        tnRoot.ForeColor = (System.Drawing.Color)cc.ConvertFromString(slText[2]);
                    }
                }

                //获取子项
                TreeNoteGetChild(tnRoot, lsAll);
            }

            //展开树形目录
            tv.ExpandAll();
        }


        /// <summary>
        /// 返回TreeView的节点在文件中所表的Key
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="tn"></param>
        /// <returns></returns>
        public string getTreeViewKeyWord(TreeView tv, TreeNode tn)
        {
            //Key字符串  TreeView.1.1.0.0  表示的意思是第一层,第一个位置的node,父节点无。
            //Key字符串  TreeView.2.3.1.2  表示的意思是第二层,第三个位置的node,父节点第一层的第二个节点。

            string sKeyWord = tv.Name + "." + (tn.Level + 1).ToString() + "." + (tn.Index + 1).ToString();

            TreeNode tnParent = tn.Parent;
            if (tnParent != null)
            {
                sKeyWord += ".";
                sKeyWord += (tnParent.Level + 1).ToString();
                sKeyWord += ".";
                sKeyWord += (tnParent.Index + 1).ToString();
            }
            else
            {
                sKeyWord += ".0.0";
            }

            return sKeyWord;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="tn"></param>
        /// <returns></returns>
        public string getTreeViewKeyWordText(TreeView tv, TreeNode tn)
        {
            return getTreeViewKeyWord(tv, tn).Replace(tv.Name, tv.Name + "Text");
        }


        /// <summary>
        /// 改变父节点的索引,也要改变它子节点的指针,即子节点记录父节点的位置,父节点是由 层+索引 决定的。
        /// 例如:(2,1)(1,3)表示第二层,第一个节点,它的父节点是第一层,第三个节点
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="tn"></param>
        public void OnTreeNodeIndexChange(TreeView tv, TreeNode tn)
        {
            foreach (TreeNode tmp in tn.Nodes)
            {
         
                string sKeyWord = getTreeViewKeyWord(tv, tmp);
                string sKeyWordText = getTreeViewKeyWordText(tv, tmp);


                bool bFindKeyWord = false;
                bool bFindKeyWordText = false;


                DListNote_<KeyData_> First = _slKey.First;
                while (First != null)
                {
                    if (First.Data.Keyword == sKeyWord)
                    {
                        bFindKeyWord = true;
                        //Key字符串  TreeView.1.1.0.0  表示的意思是第一层,第一个位置的node,父节点无。
                        //Key字符串  TreeView.2.3.1.2  表示的意思是第二层,第三个位置的node,父节点第一层的第二个节点。                   

                        StringList_ ls = First.Data.Keyword._Split(".");

                        if (ls.Count == 5)
                        {
                            First.Data.Keyword = ls[0] + "." + ls[1] + "." + ls[2] + "." + ls[3] + "." + (tmp.Parent.Index).ToString();
                        }
                        else
                        {
                            lg.ShowInfo("错误:Keyword=" + First.Data.Keyword);
                        }
                    }
                    else if(First.Data.Keyword == sKeyWordText)
                    {
                        bFindKeyWordText = true;
                        //Key字符串  TreeView.1.1.0.0  表示的意思是第一层,第一个位置的node,父节点无。
                        //Key字符串  TreeView.2.3.1.2  表示的意思是第二层,第三个位置的node,父节点第一层的第二个节点。                   

                        StringList_ ls = First.Data.Keyword._Split(".");

                        if (ls.Count == 5)
                        {
                            First.Data.Keyword = ls[0] + "." + ls[1] + "." + ls[2] + "." + ls[3] + "." + (tmp.Parent.Index).ToString();
                        }
                        else
                        {
                            lg.ShowInfo("错误:KeyWord =" + First.Data.Keyword);
                        }

                    }

                    if (bFindKeyWord && bFindKeyWordText) //当查到两个时,这次任务结束
                    {
                        break;
                    }

                    First = First.Next;
                }

                bFindKeyWord = false;
                bFindKeyWordText = false;
            }
        }
 


        public void OnTreeNodeRemove(TreeView tv, TreeNode tn, bool bNeedChangeIndex = true)
        { 
            /*
            在移除之前,TreeView会改变索引,例如:
            key 1.1   内存 a
            key 1.2   内容 b
            key 1.3   内容 c            
            
            删除1.2,要改变1.2后面的索引
            */

            //1、删除KeyWord 与 KeyWordText

            string sDelKeyWord = getTreeViewKeyWord(tv, tn);
            string sDelKeyWordText = getTreeViewKeyWordText(tv, tn);

            if (!removeKeyword(sDelKeyWord))
            {
                throw new Exception("无法删除" + sDelKeyWord);
            }

            if (!removeKeyword(sDelKeyWordText))
            {
                throw new Exception("无法删除" + sDelKeyWordText);
            }
            

            TreeNodeCollection tnc = null;

            if (tn.Parent == null)
            {
                tnc = tv.Nodes;
            }
            else
            {
                tnc = tn.Parent.Nodes;
            }


            if(bNeedChangeIndex)
            { 
                //查找tn同一级后面的TreeNoder列表,把KeyWordText索引减去1.    
                //1,2,3,4   删除2, tn.Parent.Notes.Count = 4,  tn.Index = 1,  需修改后面二个KeyWord

                foreach (TreeNode tmp in tnc)
                {
                    if (tmp.Index > tn.Index) //把KeyWordText索引减去1.
                    {
                        OnTreeNodeIndexChange(tv, tmp);


                        bool bFindKeyWord = false;
                        bool bFindKeyWordText = false;

     
                        string sKeyWord = getTreeViewKeyWord(tv, tmp);
                        string sKeyWordText = getTreeViewKeyWordText(tv, tmp);

                        
                        DListNote_<KeyData_> First = _slKey.First;
                        while (First != null)
                        {
                            if (First.Data.Keyword == sKeyWord)
                            {
                                bFindKeyWord = true;
                                First.Data.Keyword = getTreeViewKeyWord(tv, tmp.PrevNode);    //把KeyWord索引减去1  
                            }
                            if(First.Data.Keyword == sKeyWordText)
                            {
                                bFindKeyWordText = true;
                                First.Data.Keyword = getTreeViewKeyWordText(tv, tmp.PrevNode); //把KeyWordText索引减去1  
                            }

                            if(bFindKeyWord && bFindKeyWordText) //当查到两个时,这次任务结束
                            {
                                break;
                            }

                            First = First.Next;
                        }

                        bFindKeyWord = false;
                        bFindKeyWordText = false;
                    }               
                }
            }

            //删除子节点
            if (tn.Nodes.Count > 0)
            {
                foreach (TreeNode tmp in tn.Nodes)
                {
                    OnTreeNodeRemove(tv, tmp,false);
                }
            }
         
        }

   

        /// <summary>
        /// 禁止删除TreeView中的单个TreeNote,因为删除TreeView中的单个TreeNote,TreeNote的index会改变
        /// </summary>
        /// <param name="tv"></param>
        public void clearTreeView(TreeView tv)
        {
            //(1)不能用_bfk.removeKeyword(_tn.Name),因为KeyBinaryFile_用的Key不是tn.Name,且tn.Name可能为空
            //_bfk.removeKeyword(tn.Name);  

            //(2)这个也不能用,因为在同一层中有多个TreeNote,删除中间的TreeNote会导致Index的改变,也就是就
            //当再删除两旁的TreeNote会出现错误  123  del 2   变成12   而文件中还是13,3未变成2


            StringList_ ls = this.getIncludeKeyList(tv.Name + ".");

            for (int i = 0; i < ls.Count; ++i)
            {
                removeKeyword(ls[i]);
            } 
        }

#endif
#endif//-----------------------------------------------------------------------------------Windows CS平台结束


        /// <summary>
        /// 移除一个Keyword,成功返回true;
        /// </summary>
        /// <param name="sKeyword"></param>
        /// <returns></returns>
        public bool removeKeyword(string sKeyword)
        {
            KeyData_ kd = new KeyData_(sKeyword);
                        

            int i = _slKey.BinarySearch(kd);
             
            
            if (i != -1)
            {                
                return _slKey.RemoveAt(i);
            }
            return false;                  
        }

        /// <summary>
        /// 获取KeyList,如果SubString为"",则为默认获取所有
        /// </summary>
        /// <param name="SubString">KeyWord中的子字符串</param>
        /// <returns>返回KeyWord列表</returns>
        public StringList_  getIncludeKeyList(string SubString)
        {
            //throw new Exception("请重写!");

            StringList_ slFind = new StringList_();
           
            DListNote_<KeyData_> First = _slKey.First;
            while(First != null)
            {
                
                if (First.Data.Keyword.IndexOf(SubString) != -1)
                    slFind.Add(First.Data.Keyword);

                First = First.Next;
            }
            return slFind;
        }

        


    }
}
 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值