Fineui Grid 控件添加行, 删除行 ,清空记录,取值

FineUI网格控件实战
本文介绍如何使用FineUI中的网格控件实现借/还业务流程,包括通过条形码扫描添加数据到网格、获取网格中新增数据并保存至数据库、以及删除网格中的新增行。

需要完成一个借/还的业务

(只是总结自己使用fineui过程中遇到的问题和解决的方法,一遍以后使用中少走弯路。)

1.扫描一个条码取数据添加到Grid控件中(一行,这个只是添加到Grid控件中,还没有保存在数据库里。扫描完一批后一起提交保存。)

      需要说明的是 “列” 请使用 <f:RenderField>列中放  <f:TextBox> (文本框控件)。其实就是列要是可编辑列(如果你不想编辑可以设置只读属性).

      如果使用模板列<f:TemplateField>在测试中发现取不到值。 如果想是隐藏列(Hidden="True")也取不到数据,如果想隐藏列可以把width=0(只能这样了,如果有更好的办法的告诉我吧)

前端代码:
<f:Grid ID="Grid2" ShowHeader="False" Title="" EnableCollapse="True"  SortField="YPBH" runat="server" AllowCellEditing="true"  BoxFlex=1 EnableAfterEditEvent="True" OnAfterEdit="Grid2_AfterEdit" EnableCheckBoxSelect="True"     EnableMultiSelect="False"  > 
    <Columns> 
       <f:RowNumberField /> 
       <f:RenderField Width="100px" ColumnID="YPBH" DataField="YPBH" FieldType="String" HeaderText="样品编号"> 
          <Editor> 
             <f:TextBox ID="tbxEditYPBH" Readonly="true" runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField> 
       <f:RenderField Width="190px" ColumnID="YPMC" DataField="YPMC" FieldType="String" HeaderText="样品名称"> 
          <Editor> 
             <f:TextBox ID="tbxEditYPMC" Readonly="true" runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField> 
       <f:RenderField runat="server"  Width="170px" ColumnID="YPHH" DataField="YPHH"  HeaderText="供应商面料编号" EnableLock="False"> 
          <Editor> 
             <f:TextBox ID="tbxEditYPHH" Required="true"  runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField> 
       <f:RenderField Width="0px" ColumnID="JYLSH" DataField="JYLSH" FieldType="String"   > 
          <Editor> 
             <f:TextBox ID="tbxEditJYLSH" Readonly="true" runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField>
       <f:RenderField Width="0px" ColumnID="YPLSH" DataField="YPLSH" FieldType="String"   > 
          <Editor> 
             <f:TextBox ID="tbxEditYPLSH" Readonly="true" runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField> 
       <f:RenderField Width="0px" ColumnID="GHBZ" DataField="GHBZ" FieldType="String"  > 
          <Editor> 
             <f:TextBox ID="tbxEditGHBZ" Readonly="true" runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField> 
       <f:RenderField Width="0px" ColumnID="GRY" DataField="GRY" FieldType="String"  > 
          <Editor> 
             <f:TextBox ID="tbxEditGRY" Readonly="true" runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField> 
       <f:RenderField Width="0px" ColumnID="BZ" DataField="BZ" FieldType="String"  > 
          <Editor> 
             <f:TextBox ID="tbxEditBZ" Readonly="true" runat="server"></f:TextBox> 
          </Editor> 
       </f:RenderField> 
    </Columns> 
</f:Grid>

后台添加到 Grid2中的代码

//dsyp 这个是一个 DataSet 对象 怎么去值我就不多说了。
     
JObject defaultObj = new JObject(); 
defaultObj.ToString(Newtonsoft.Json.Formatting.None); 
defaultObj.Add("YPBH", dsyp.Tables[0].Rows[0]["YPBH"].ToString()); 
defaultObj.Add("YPMC", dsyp.Tables[0].Rows[0]["YPMC"].ToString()); 
defaultObj.Add("YPHH", dsyp.Tables[0].Rows[0]["HH"].ToString()); 
defaultObj.Add("YPLSH", dsyp.Tables[0].Rows[0]["Djlsh"].ToString()); 
defaultObj.Add("GHBZ", JYTYPE.SelectedValue.ToString()); 
defaultObj.Add("GRY", RY.Text.Trim().ToString()); 
defaultObj.Add("BZ", BZ.Text.Trim().ToString()); 
 Grid2.AddNewRecord(defaultObj, true);    //添加一行到Grid2。
    //扫描后为了让光标即焦点继续停在 “条码框” 里,方便继续扫描。(先是使用了以下语句发现不行,只能结束Grid的编辑状态,焦点还是无法聚焦在ttbSearchTM.Text 内,原因不明,经过反复测试发现启用Grid的OnAfterEdit(结束编辑状态)事件 ,在事件里在添加一条 ttbSearchTM.Focus() 就可以了。(原因不明,有谁如果知道可以留言给我,谢谢了!))
TMXS.Text = ttbSearchTM.Text;            //显示扫描得到的条码
 ttbSearchTM.Text = String.Empty;      //清空文本框内扫描得到的条码 
 ttbSearchTM.Focus();      //因为无法直接结束 Grid2 的编辑状态 ,说以用这个语句先结束编辑状态。然后在触发的事件中调整焦点定位。

2 、取Grid中新增加的数据保存到数据库。(重点是取Grid中新增的数据,怎么保存到数据库这个就不多说了)

//保存按钮的点击事件 
   protected void Button_savexyd_OnClick(object sender, EventArgs e)  
  { 
       string RY = "",BZ=""; 
       int YPLSH=0, GHBZ=0; 
       DataRow dr = Session["Admin"] as DataRow; 
       List<Dictionary<string, object>> newAddedList = Grid2.GetNewAddedList();   //取得Grid的新增的所有行数据,只要注意了1中提示的几点 这个里是很容易取到数据的。 
      Model.JY m = new Model.JY(); 
       for (int i = 0; i < newAddedList.Count; i++)      //一行一行循环 
      { 
           if (newAddedList[i].ContainsKey("YPLSH"))  //如果有   ”YPLSH“ 列 
         { 
               YPLSH = int.Parse(newAddedList[i]["YPLSH"].ToString());  //取出这一行这一列的数据。(newAddedList[i]["YPLSH"].ToString() ) 
          } 
           if (newAddedList[i].ContainsKey("GRY"))  
          { 
               RY = newAddedList[i]["GRY"].ToString(); 
           } 
           if (newAddedList[i].ContainsKey("GHBZ")) 
           { 
               GHBZ = int.Parse(newAddedList[i]["GHBZ"].ToString()); 
           }
           if (newAddedList[i].ContainsKey("BZ")) 
           { 
               BZ = newAddedList[i]["BZ"].ToString(); 
           }
           if (GHBZ == 0)  //借样(添加) 
           { 
               m.YPLSH = YPLSH; 
               m.GHBZ = GHBZ; 
               m.CJR = dr["LoginName"].ToString(); 
               m.CJRQ= DateTime.Now; 
               m.BJR = dr["LoginName"].ToString(); 
               m.BJRQ = DateTime.Now; 
               m.JYR = RY; 
               m.JYRQ = DateTime.Now; 
               m.Djlsh = GongYongClass.GetDjlsh("JY"); 
               m.BZ = "借:" + BZ; 
               bll.Add(m); 
           } 
           else//借样(还样) 
           {
       m = bll.GetModel(int.Parse(newAddedList[i]["JYLSH"].ToString())); 
               m.YPLSH = YPLSH; 
               m.GHBZ = GHBZ; 
               m.BJR= dr["LoginName"].ToString(); 
               m.BJRQ = DateTime.Now; 
               m.HYR = RY; 
               m.HYRQ= DateTime.Now; 
               m.BZ = m.BZ + "还:" + BZ; 
               bll.Update(m);

           } 
       } 
       Grid2.RejectChanges(); //清空GRID 
       BindGrid(); 
   }

3、Grid中新增行的删除

if (!IsPostBack) 
       {
  btnDelete.OnClientClick = Grid2.GetNoSelectionAlertReference(" 请至少选择一项!") + GetDeleteScript(Grid2); // 删除选中行客户端脚本定义 .就这一句就可以了。(和官网的一至)
         BindGrid(); 
        }

//GetDeleteScript  这个函数看官网示例中没有,我也是找了好久在网上搜到的也一并写出吧。
protected string GetDeleteScript(Grid grid1) 
      { 
          return Confirm.GetShowReference("删除选中行?", String.Empty, MessageBoxIcon.Question, grid1.GetDeleteSelectedReference(), String.Empty); 
         //以下注释的 是我在网上搜到的用了 grid1.GetDeleteSelectedRowReference(), 方法 并且说6.0的必须要加延迟。我用的fineui 4.2 版的好像没有这个方法,我查了后改了上面的。 
         //string confirmScript=Confirm.GetShowReference("删除选中行?", String.Empty, MessageBoxIcon.Question, grid1.GetDeleteSelectedRowReference(), String.Empty);   
         // return String.Format("F.defer(function(){{{0}}},100);", confirmScript);  //延迟 
      }

附图

jy

这段代码中 副表 取的值 是不是没有从0开始取 import tkinter as tk from tkinter import filedialog, messagebox, ttk import pandas as pd import os import subprocess class ExcelViewerApp: def __init__(self, root): self.root = root self.root.title("TPC效率化工具") self.root.geometry("1000x700") # 使用网格布局的主容器 self.main_frame = tk.Frame(root, bg='#f0f0f0', padx=15, pady=15) self.main_frame.pack(fill=tk.BOTH, expand=True) # 配置网格 self.main_frame.columnconfigure(0, weight=0) self.main_frame.columnconfigure(1, weight=1) self.main_frame.columnconfigure(2, weight=0) self.main_frame.columnconfigure(3, weight=0) # === 主表控件 === # 文件路径 tk.Label(self.main_frame, text="主表文件路径:", bg='#f0f0f0').grid(row=0, column=0, sticky='w', padx=(0, 5)) self.path_label = tk.Label( self.main_frame, text="未选择主表文件", anchor='w', relief=tk.SUNKEN, bg="#ffffff", padx=5, pady=5 ) self.path_label.grid(row=0, column=1, sticky='ew', padx=(0, 10)) # 工作表 tk.Label(self.main_frame, text="主表工作表:", bg='#f0f0f0').grid(row=1, column=0, sticky='w', padx=(0, 5), pady=(10, 0)) self.sheet_label = tk.Label( self.main_frame, text="未选择主表工作表", anchor='w', relief=tk.SUNKEN, bg="#ffffff", padx=5, pady=5 ) self.sheet_label.grid(row=1, column=1, sticky='ew', padx=(0, 10), pady=(10, 0)) # 打开文件按钮 self.open_btn = tk.Button( self.main_frame, text="打开主表文件", command=self.open_excel, bg='#f0f0f0', fg='black', padx=15, pady=8, width=10 ) self.open_btn.grid(row=0, column=2, rowspan=2, sticky='ns', padx=(0, 5)) # 选择主表按钮 self.select_btn = tk.Button( self.main_frame, text="选择主表", command=self.load_excel, bg='#f0f0f0', fg='black', padx=15, pady=8, width=10 ) self.select_btn.grid(row=0, column=3, rowspan=2, sticky='ns', padx=(0, 5)) # === 副表控件 === (添加在下方) tk.Label(self.main_frame, text="副表文件路径:", bg='#f0f0f0').grid(row=2, column=0, sticky='w', padx=(0, 5), pady=(20, 0)) self.aux_path_label = tk.Label( self.main_frame, text="未选择副表文件", anchor='w', relief=tk.SUNKEN, bg="#ffffff", padx=5, pady=5 ) self.aux_path_label.grid(row=2, column=1, sticky='ew', padx=(0, 10), pady=(20, 0)) tk.Label(self.main_frame, text="副表工作表:", bg='#f0f0f0').grid(row=3, column=0, sticky='w', padx=(0, 5), pady=(10, 0)) self.aux_sheet_label = tk.Label( self.main_frame, text="未选择副表工作表", anchor='w', relief=tk.SUNKEN, bg="#ffffff", padx=5, pady=5 ) self.aux_sheet_label.grid(row=3, column=1, sticky='ew', padx=(0, 10), pady=(10, 0)) # 打开副表文件按钮 self.aux_open_btn = tk.Button( self.main_frame, text="打开副表文件", command=self.open_aux_excel, bg='#f0f0f0', fg='black', padx=15, pady=8, width=10 ) self.aux_open_btn.grid(row=2, column=2, rowspan=2, sticky='ns', padx=(0, 5), pady=(20, 0)) # 选择副表按钮 self.aux_select_btn = tk.Button( self.main_frame, text="选择副表", command=self.load_aux_excel, bg='#f0f0f0', fg='black', padx=15, pady=8, width=10 ) self.aux_select_btn.grid(row=2, column=3, rowspan=2, sticky='ns', padx=(0, 5), pady=(20, 0)) # 使用数组存储数据 self.dataset = [] # 主表数据 self.aux_dataset = [] # 副表数据 # 窗口居中 self.center_window(self.root) # 添加对比按钮 self.compare_btn = tk.Button( self.main_frame, text="对比数据", command=self.compare_data, bg='#4CAF50', fg='white', padx=15, pady=8, width=15 ) self.compare_btn.grid(row=4, column=1, columnspan=2, pady=20) # 创建结果显示区域 self.result_frame = tk.Frame(root, bg='#f0f0f0') self.result_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 添加Treeview显示结果 self.tree = ttk.Treeview(self.result_frame) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 添加滚动条 scrollbar = ttk.Scrollbar(self.result_frame, orient="vertical", command=self.tree.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.tree.configure(yscrollcommand=scrollbar.set) # 状态标签 self.status_label = tk.Label(root, text="就绪", bg='#f0f0f0', anchor='w') self.status_label.pack(fill=tk.X, padx=10, pady=5) def center_window(self, window): window.update_idletasks() width = window.winfo_width() height = window.winfo_height() x = (window.winfo_screenwidth() // 2) - (width // 2) y = (window.winfo_screenheight() // 2) - (height // 2) window.geometry(f"+{x}+{y}") # ===== 主表功能 ===== def open_excel(self): """打开主表文件""" file_path = self.path_label.cget("text") if not file_path or file_path == "未选择主表文件": messagebox.showwarning("警告", "请先选择主表Excel文件") return try: if os.name == 'nt': os.startfile(file_path) elif os.name == 'posix': subprocess.call(('open', file_path)) else: subprocess.call(('xdg-open', file_path)) except Exception as e: messagebox.showerror("错误", f"打开主表文件失败: {str(e)}") def load_excel(self): """加载主表Excel""" file_path = filedialog.askopenfilename( title="选择主表Excel文件", filetypes=[("Excel文件", "*.xlsx *.xls")] ) if not file_path: return try: self.path_label.config(text=file_path) self.select_main_sheet(file_path) except Exception as e: messagebox.showerror("错误", f"读取主表文件失败: {str(e)}") def select_main_sheet(self, file_path): """选择主表工作表""" try: xl = pd.ExcelFile(file_path) sheet_names = xl.sheet_names selector = tk.Toplevel(self.root) selector.title("选择主表工作表") selector.geometry("300x150") tk.Label(selector, text="请选择主表工作表:").pack(pady=10) sheet_var = tk.StringVar(selector) combobox = ttk.Combobox( selector, textvariable=sheet_var, values=sheet_names, state="readonly", width=40 ) combobox.pack(pady=10, padx=20, fill=tk.X) combobox.current(0) tk.Button( selector, text="确认选择", command=lambda: self.process_main_sheet_selection( file_path, sheet_var.get(), selector ), bg='#f0f0f0', fg='black', padx=10, pady=5 ).pack(pady=15) self.center_window(selector) except Exception as e: messagebox.showerror("错误", f"读取主表文件失败: {str(e)}") def process_main_sheet_selection(self, file_path, sheet_name, selector): """处理主表工作表选择结果""" try: df = pd.read_excel(file_path, sheet_name=sheet_name, header=0, skiprows=list(range(0,9))) self.dataset = df.to_dict(orient='records') row_count = len(self.dataset) col_count = len(df.columns) if row_count > 0 else 0 self.sheet_label.config(text=f"{sheet_name} ({row_count}×{col_count}列)") selector.destroy() messagebox.showinfo("加载成功", f"主表工作表 [{sheet_name}] 已载入\n" f"数据维度: {row_count} × {col_count}列" ) print(f"主表数据示例: {self.dataset[0] if self.dataset else '空'}") except Exception as e: messagebox.showerror("错误", f"加载主表数据失败: {str(e)}") # ===== 副表功能 ===== def open_aux_excel(self): """打开副表文件""" file_path = self.aux_path_label.cget("text") if not file_path or file_path == "未选择副表文件": messagebox.showwarning("警告", "请先选择副表Excel文件") return try: if os.name == 'nt': os.startfile(file_path) elif os.name == 'posix': subprocess.call(('open', file_path)) else: subprocess.call(('xdg-open', file_path)) except Exception as e: messagebox.showerror("错误", f"打开副表文件失败: {str(e)}") def load_aux_excel(self): """加载副表Excel""" file_path = filedialog.askopenfilename( title="选择副表Excel文件", filetypes=[("Excel文件", "*.xlsx *.xls")] ) if not file_path: return try: self.aux_path_label.config(text=file_path) self.select_aux_sheet(file_path) except Exception as e: messagebox.showerror("错误", f"读取副表文件失败: {str(e)}") def select_aux_sheet(self, file_path): """选择副表工作表""" try: xl = pd.ExcelFile(file_path) sheet_names = xl.sheet_names selector = tk.Toplevel(self.root) selector.title("选择副表工作表") selector.geometry("300x150") tk.Label(selector, text="请选择副表工作表:").pack(pady=10) sheet_var = tk.StringVar(selector) combobox = ttk.Combobox( selector, textvariable=sheet_var, values=sheet_names, state="readonly", width=40 ) combobox.pack(pady=10, padx=20, fill=tk.X) combobox.current(0) tk.Button( selector, text="确认选择", command=lambda: self.process_aux_sheet_selection( file_path, sheet_var.get(), selector ), bg='#f0f0f0', fg='black', padx=10, pady=5 ).pack(pady=15) self.center_window(selector) except Exception as e: messagebox.showerror("错误", f"读取副表文件失败: {str(e)}") def process_aux_sheet_selection(self, file_path, sheet_name, selector): """处理副表工作表选择结果""" try: df = pd.read_excel(file_path, sheet_name=sheet_name, header=0, skiprows=list(range(0,9))) self.aux_dataset = df.to_dict(orient='records') row_count = len(self.aux_dataset) col_count = len(df.columns) if row_count > 0 else 0 self.aux_sheet_label.config(text=f"{sheet_name} ({row_count}×{col_count}列)") selector.destroy() messagebox.showinfo("加载成功", f"副表工作表 [{sheet_name}] 已载入\n" f"数据维度: {row_count} × {col_count}列" ) print(f"副表数据示例: {self.aux_dataset[0] if self.aux_dataset else '空'}") except Exception as e: messagebox.showerror("错误", f"加载副表数据失败: {str(e)}") def compare_data(self): """对比主表和副表数据并设置背景色,仅显示和对比共有列""" if not self.dataset or not self.aux_dataset: messagebox.showwarning("警告", "请先加载主表和副表数据") return # 获取主表和副表的列名 main_columns = set(self.dataset[0].keys()) if self.dataset else set() aux_columns = set(self.aux_dataset[0].keys()) if self.aux_dataset else set() # 获取两个表共有的列(不包括Z2列,因为Z2用于匹配) common_columns = sorted(main_columns & aux_columns - {'Z2'}) # 确保Z2列存在(用于匹配的关键列) if 'Z2' not in main_columns or 'Z2' not in aux_columns: messagebox.showerror("错误", "主表或副表缺少Z2列,无法进对比") return # 配置Treeview列 - 只显示共有列 self.tree["columns"] = common_columns self.tree["show"] = "headings" for col in common_columns: self.tree.heading(col, text=col) self.tree.column(col, width=100, anchor=tk.CENTER) # 添加Z2列作为第一列(用于显示匹配状态) self.tree["columns"] = ["Z2"] + common_columns self.tree.heading("Z2", text="Z2(匹配状态)") self.tree.column("Z2", width=120, anchor=tk.CENTER) # 清空现有数据 for item in self.tree.get_children(): self.tree.delete(item) # 创建用于设置颜色的tag self.tree.tag_configure('match', background='#DFF0D8') # 绿色 self.tree.tag_configure('mismatch', background='#F8D7DA') # 红色 self.tree.tag_configure('not_found', background='#F8D7DA') # 红色 # 统计结果 match_count = 0 mismatch_count = 0 not_found_count = 0 # 创建从Z2值到副表的映射(提高查找效率) aux_map = {} for aux_row in self.aux_dataset: z2_value = aux_row.get('Z2', None) if z2_value is not None: aux_map.setdefault(z2_value, []).append(aux_row) # 遍历主表每一 for main_row in self.dataset: main_z2 = main_row.get('Z2', None) found_in_aux = False all_matched = True # 查找匹配的副表 matching_aux_rows = aux_map.get(main_z2, []) if main_z2 is not None else [] if matching_aux_rows: found_in_aux = True # 检查所有匹配中是否有完全匹配的 row_match = False for aux_row in matching_aux_rows: current_match = True # 只比较共有列 for col in common_columns: if col in main_row and col in aux_row: if main_row[col] != aux_row[col]: current_match = False break if current_match: row_match = True break all_matched = row_match # 准备Treeview数据 z2_display = f"{main_z2} {'✓' if all_matched else '✗'}" if found_in_aux else f"{main_z2} (未找到)" values = [z2_display] + [main_row.get(col, '') for col in common_columns] item_id = self.tree.insert("", "end", values=values) # 根据匹配情况设置背景色 if not found_in_aux: self.tree.item(item_id, tags=('not_found',)) not_found_count += 1 elif all_matched: self.tree.item(item_id, tags=('match',)) match_count += 1 else: self.tree.item(item_id, tags=('mismatch',)) mismatch_count += 1 # 更新状态 self.status_label.config( text=(f"对比完成 | 匹配: {match_count} | 不匹配: {mismatch_count} | 未找到: {not_found_count} | " f"共有列: {', '.join(common_columns)}") ) if __name__ == "__main__": root = tk.Tk() app = ExcelViewerApp(root) root.mainloop()
09-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值