利用FarPoint Spread表格控件,構造Winform的Excel表格界面輸入


因有一個業務需要在Winform界面中,以類似Excel表格界面中錄入相關的數據(畢竟很多時候,客戶想利用成熟的軟件體驗來輸入他們想要的東西),其中界面需要錄入基礎信息,列表信息,圖片信息等,綜合這些就是例如下面這樣的界面效果。本文主要針對如何利用FarPoint Spread表格控件實現類似Excel界面豐富數據的保存及顯示,以及在使用過程中的一些經驗心得,希望對大家在開發Winform的Excel數據錄入和顯示方面的開發有一定幫助。

從以上的界面分類可以看到,大致可以分為幾個類型的數據,一個是基礎字段數據,一個是有多行的列表數據,一個是圖片數據,還有就是備注信息的顯示錄入了。下面我來分類介紹這些功能的實現。

1、類似Excel的列表總體界面設計

首先,這個列表需要在Winform的界面中進行設計,拖入一個Farpoint控件到Winform界面上,設置好布局等屬性,然后在右鍵菜單上啟動Spread Designer就可以設計相關的Excel樣式表格的內容了。

注意,這里界面一般是在窗體中設計的,當然你的內容可以通過復制粘貼的方式,從Excel文檔拷貝過來,效果看起來一樣的,非常不錯。不過,雖然Farpoint Spread控件提供了一個另存為Xml文件的操作,並且可以通過API,Open一個XML文件,不過Open的XML文件后,好像內容不能進行修改的,而且類型CellType也是Null的,所以如果要在一個窗體上動態加載布局好像做不到,至少我沒有做到。不過對於開發來說,我們在設計時刻,設計好Excel樣式的列表界面,也未嘗不是一件好事。

2、下拉列表的綁定

在Excel列表中,我們很多時候,為了輸入的方便,需要通過下拉列表方式輸入內容,這樣可以提高速度和用戶體驗,但這些內容必須是通過數據庫內容進行綁定的,Farpoint Spread控件是如何做到綁定下拉列表的數據的呢。首先Farpoint Spread控件由很多輸入的內容,其中就包括有ComoBox類型,如下所示。

我們在指定下拉的類型后,Excel列表的顯示方式也跟着變化為下面樣式了。

以上打勾的就是我們下一步需要綁定列表數據的列表了,綁定列表的數據也不麻煩,就是需要明確Cell的序號,綁定給他數據源就可以了,不過說實話,經常要數着Cell的行列號是什么數字,有點不方便。

        private void BindDict()
        {
            FarPoint.Win.Spread.Cell cell;

            //品名
            cell = this.fpSpread1_Sheet1.Cells[1,9];
            FarPoint.Win.Spread.CellType.ComboBoxCellType productType = new FarPoint.Win.Spread.CellType.ComboBoxCellType();
            productType.BindDictItems("品名");
            cell.CellType = productType;

            //客戶名
            cell = this.fpSpread1_Sheet1.Cells[4, 8];
            FarPoint.Win.Spread.CellType.ComboBoxCellType customerType = new FarPoint.Win.Spread.CellType.ComboBoxCellType();
            customerType.BindDictItems("客戶名");
            cell.CellType = customerType;

            //款號
            cell = this.fpSpread1_Sheet1.Cells[1, 12];
            FarPoint.Win.Spread.CellType.ComboBoxCellType styleType = new FarPoint.Win.Spread.CellType.ComboBoxCellType();
            styleType.BindDictItems("款號");
            cell.CellType = styleType;

            //面料
            cell = this.fpSpread1_Sheet1.Cells[1, 15];
            FarPoint.Win.Spread.CellType.ComboBoxCellType materialType = new FarPoint.Win.Spread.CellType.ComboBoxCellType();
            materialType.BindDictItems("面料");
            cell.CellType = materialType;

        }

其中代碼的BindDictItems我用了擴展方法,所以能通過對象直接調用,具體的函數代碼如下所示,就是調用字典業務類獲取數據,賦值給Items屬性即可,注意其中的Edittable最好選擇為true,否則它只是顯示里面列表的內容,類似DropdownList那樣。

        /// <summary>
        /// 綁定下拉列表控件為指定的數據字典列表
        /// </summary>
        /// <param name="combo">下拉列表控件</param>
        /// <param name="dictTypeName">數據字典類型名稱</param>
        public static void BindDictItems(this FarPoint.Win.Spread.CellType.ComboBoxCellType combo, string dictTypeName)
        {
            Dictionary<string, string> dict = BLLFactory<DictData>.Instance.GetDictByDictType(dictTypeName);
            List<string> listData = new List<string>();
            foreach (string key in dict.Keys)
            {
                listData.Add(key);
            }
            combo.Items = listData.ToArray();
            combo.Editable = true;
        } 

3、如何構造界面自定義錄入

為了輸入方便,對於一些例如彈出框選擇內容,圖片編輯,備注內容(很長的時候)的編輯,這些一般來說,我們通過自定義界面來錄入比較好,比較Excel樣式的界面,錄入單元格很小,也有時候實現不了的。所以通過制定控件單元格的單擊事件,用來處理特殊錄入信息的操作。

this.fpSpread1.CellClick += new FarPoint.Win.Spread.CellClickEventHandler(fpSpread1_CellClick);

展開界面部分給大家看看,就是很把內容

        void fpSpread1_CellClick(object sender, FarPoint.Win.Spread.CellClickEventArgs e)
        {
            FarPoint.Win.Spread.Cell cell = this.fpSpread1_Sheet1.Cells[e.Row, e.Column];
            fpSpread1_Sheet1.SetActiveCell(e.Row, e.Column);

            if(e.Column == 14 && e.Row == 6)
            {
                #region 圖片操作
                FrmImageEdit dlg = new FrmImageEdit();
                if (!string.IsNullOrEmpty(ID))
                {
                    dlg.ID = ID;
                    dlg.IsNew = false;
                }
                else
                {
                    dlg.ID = NewID;
                    dlg.IsNew = true;
                }
                dlg.OnDataSaved += new EventHandler(dlgPicture_OnDataSaved);
                dlg.ShowDialog(); 
                #endregion
            }
            else if (e.Column == 1 && e.Row == 42)
            {
                #region 注意事項
                object value = this.fpSpread1_Sheet1.Cells[e.Row, e.Column].Value;
                if (value != null)
                {
                    FrmEditNote dlg = new FrmEditNote();
                    dlg.txtContent.Text = value.ToString();
                    if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        this.fpSpread1_Sheet1.Cells[e.Row, e.Column].Value = dlg.txtContent.Text;
                    }
                } 
                #endregion
            }

例如,對於下拉列表內容,需要進行彈出式選擇內容,如下界面所示。

對於圖片單元格,單擊就可以彈出下面的窗體,方便編輯或者查看。

對於備注內容,我們讓他彈出一個窗體,更好展現和編輯。

4、數據的顯示和保存

對於普通的主表數據字段的顯示很簡單,把內容賦值給對應的單元格Text屬性即可,如下所示。

        /// <summary>
        /// 數據顯示的函數
        /// </summary>
        public void DisplayData()
        {
            if (!string.IsNullOrEmpty(ID))
            {
                #region 顯示信息
                CraftHeaderInfo info = BLLFactory<CraftHeader>.Instance.FindByID(ID);
                if (info != null)
                {
                    this.fpSpread1_Sheet1.Cells[1, 9].Text = info.ProductName;
                    this.fpSpread1_Sheet1.Cells[1, 12].Text = info.StyleNo;
                    this.fpSpread1_Sheet1.Cells[1, 15].Text = info.Material;

保存的時候,把對應的內容保存到實體類進行數據保存操作即可。

        /// <summary>
        /// 編輯或者保存狀態下取值函數
        /// </summary>
        /// <param name="info"></param>
        private void SetInfo(CraftHeaderInfo info)
        {
            info.ProductName = this.fpSpread1_Sheet1.Cells[1, 9].Text;//品名
            info.StyleNo = this.fpSpread1_Sheet1.Cells[1, 12].Text;//款號
            info.Material = this.fpSpread1_Sheet1.Cells[1, 15].Text;//面料

更多數據的時候,我們把內容保存分開,各個函數負責不同的部分即可,在主表保存后繼續保存其他部分的內容,例如紅色部分就是其他部分的保存操作。

        private void btnSave_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(ID))
            {
                CraftHeaderInfo info = BLLFactory<CraftHeader>.Instance.FindByID(ID);
                if (info != null)
                {
                    SetInfo(info);

                    try
                    {
                        #region 更新數據
                        bool succeed = BLLFactory<CraftHeader>.Instance.Update(info, info.ID.ToString());
                        if (succeed)
                        {
 SaveProcess(info.ID); SaveAccessories(info.ID); SaveIndicateSize(info.ID); SaveColorPair(info.ID); //可添加其他關聯操作
                            ProcessDataSaved(this.btnSave, new EventArgs());

                            MessageDxUtil.ShowTips("保存成功");
                        }
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        LogTextHelper.Error(ex);
                        MessageDxUtil.ShowError(ex.Message);
                    }
                }
            }
            else
            {

例如工藝過程是一個列表數據,保存的時候,需要指定行列的屬性進行操作,而且我們添加一個Seq的序列號,用來保存內容的順序,這樣加載的時候,我們就按照這個循序進行加載顯示,否則會出現問題。

        private void SaveProcess(string headerId)
        {
            string condition = string.Format("Header_ID = '{0}' ", headerId);
            List<CraftProcessInfo> list = BLLFactory<CraftProcess>.Instance.Find(condition);
            //(e.Column == 1 && (e.Row >= 6 && e.Row < 30))
            int i = 0;
            for (int row = 6; row < 30; row++)
            {
                CraftProcessInfo info = GetProcess(i++, list);
                info.Header_ID = headerId;

                int col = 0;
                info.HandNo = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Process = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Models = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.NeedleWork = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Flower = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.DownLine = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.PinCode = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.KnifeGate = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Note = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Item1 = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Item2 = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Item3 = this.fpSpread1_Sheet1.Cells[row, col++].Text;

                BLLFactory<CraftProcess>.Instance.InsertUpdate(info, info.ID);
            }
        }

其中GetProcess函數,就是一個列表中查找對應順序的內容,如果有,那么我們更新這個對應順序的內容,如果沒有,那么我們認為它是新的數據,這樣就新增到數據庫中,所以最后用了InserUpdate就是這個道理。其中GetProcess函數邏輯代碼如下所示。

        private CraftProcessInfo GetProcess(int index, List<CraftProcessInfo> list)
        {
            CraftProcessInfo info = new CraftProcessInfo();
            if (list.Count > index)
            {
                info = list[index];
            }
            info.Seq = index + 1;//重新調整順序號

            return info;
        } 

另外注意的時候,有些單元格是合並列的,所以一定要注意算好他的行列號哦。有些地方可能需要跳行。

        private void SaveAccessories(string headerId)
        {
            string condition = string.Format("Header_ID = '{0}' ", headerId);
            List<AccessoriesInfo> list = BLLFactory<Accessories>.Instance.Find(condition);
            //(e.Column == 1 && (e.Row >= 31 && e.Row < 35))
            int i = 0;
            for (int row = 31; row < 35; row++)
            {
                AccessoriesInfo info = GetAccessories(i++, list);
                info.Header_ID = headerId;

                int col = 1;
                info.Name = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Consumption = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Position = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                col++;//空跳一列
                info.Item1 = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                col++;//空跳一列
                col++;//空跳一列
                info.Item2 = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Item3 = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Item4 = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Item5 = this.fpSpread1_Sheet1.Cells[row, col++].Text;
                info.Item6 = this.fpSpread1_Sheet1.Cells[row, col++].Text;

                BLLFactory<Accessories>.Instance.InsertUpdate(info, info.ID);
            }
        }

5、Excel表格的數據打印及導出。

使用這個Farpoint Spread的空間,對於里面的內容進行打印或者導出Excel非常方便,代碼也不多,如下所示。

        private void btnPrint_Click(object sender, EventArgs e)
        {
            PrintInfo pi = new PrintInfo();
            pi.Header = "成衣工藝單";
            pi.JobName = "成衣工藝單";
            pi.Orientation = PrintOrientation.Auto;
            pi.PageOrder = PrintPageOrder.Auto;
            pi.ShowPrintDialog = true;
            pi.PrintNotes = PrintNotes.AtEnd;

            for (int i = 0; i < this.fpSpread1.Sheets.Count; i++)
            {
                pi.ShowPrintDialog = (i == 0);
                this.fpSpread1.Sheets[i].PrintInfo = pi;
                fpSpread1.PrintSheet(i);
            }
        }

        private void btnExport_Click(object sender, EventArgs e)
        {
            string file = FileDialogHelper.SaveExcel("成衣工藝單.xls");
            if (!string.IsNullOrEmpty(file))
            {
                try
                {
                    bool success = this.fpSpread1.SaveExcel(file);
                    if (success)
                    {
                        if (MessageDxUtil.ShowYesNoAndTips("導出成功,是否打開文件?") == System.Windows.Forms.DialogResult.Yes)
                        {
                            System.Diagnostics.Process.Start(file);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogTextHelper.Error(ex);
                    MessageDxUtil.ShowError(ex.Message);
                }
            }
        }

導出的效果和界面顯示的效果基本上一致的,而且圖片等特殊的格式,也是正常保留在Excel里面,總體感覺不錯。

最后附上這個系統的一些截圖作為補充了解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM