FORM执行查询的各种方法

本文介绍如何在FORM中实现复杂的查询功能,包括FORM间调用后的查询、条件查询及更新后的重新查询等,确保用户操作后数据能准确显示。


一.FORM调用FORM后执行查询

1.打开 APPSTAND.fmb,把 Object Groups 下的 QUERY_FIND 对象组拖动到自己的 form 中的
Object Groups 下,点击 copy。


2.  这时在 block、  canvases 和 windows 下多了 QUERY_FIND
修改 Data Blocks 的 QUERY_FIND 下 NEW 按钮和 FIND 按钮的 when-button-pressd 的代码,
New 下的代码:app_find.new('要执行查询的数据块')
FIND 下的代码:
:parameter.G_query_find := 'TRUE';
app_find.find('要执行查询的数据块');
:parameter.G_query_find := 'FALSE';


3.  修改 QUERY_FIND 数据块下的 KEY-NXTBLK 的代码,如下:
:parameter.G_query_find := 'TRUE';
app_find.find('要执行查询的数据块');
:parameter.G_query_find := 'FALSE';


4.  在 QUERY_FIND数据块下创建查询条件所需的ITEM,设置数据库属性为否,画布属性为null


5.  在数据块下创建 user_named 触发器,改名为 QUERY_FIND,添加如下代码
app_find.query_find('MAIN','QUERY_FIND','QUERY_FIND');
第一个参数 MAIN 为主窗口的窗口名
第二个参数 QUERY_FIND 为查找所在的窗口名(拖拽时在 windows 下自动生成的)
第三个参数 QUERY_FIND 为 QUERY_FIND 数据块名(拖拽时在 data block 下自动生成的)


6.在所要查找的数据块下添加 pre-query tirgger,代码如下

if :parameter.g_query_find = 'TRUE' then
copy(name_in('query_find.ORG_ID'),'HEADER.ORG_ID');
 copy(name_in('query_find.VENDOR_ID'),'HEADER.VENDOR_ID');
/* app_find.query_range(name_in('query_find.AVAILABLE_AMOUNT'),
  name_in('query_find.AMOUNT_LOWER_RANGE'),'query_find.AMOUNT_HEIGHT_RANGE');*/
  app_find.query_range(:query_find.AMOUNT_LOWER_RANGE, :query_find.AMOUNT_HEIGHT_RANGE,'HEADER.AVAILABLE_AMOUNT');
:parameter.G_query_find := 'FALSE';
end if;


说明:
copy 可查询等于某值的条件
app_find.query_range 可以设置所要查找的数据范围,
第一个参数为起始值所在项,
第二个参数为终止值所在项,(起始终止一样时就是查找当前值)
第三个参数为查找数据块的 item,起始值和终止值针对数据块的 item 而设置。如下图

7.设置所查找的数据块的 when-new-record-instance trigger 的执行等级为 after
8.在FORM级触发器WHEN-NEW-FORM-INSTANCE中将调用FORM传过来的参数付给QUERY_FIND数据块的ITEM

:query_find.INVOICE_ID    := :parameter.G_INVOICE_ID;
:query_find.org_id    := :parameter.g_org_id;
:query_find.vendor_id := :parameter.g_vendor_id;

9.在FORM级触发器WHEN-NEW-FORM-INSTANCE中执行查询
go_block('HEADER');
 
    :parameter.g_query_find := 'TRUE';
    app_find.find('HEADER');
    -- app_find.find('HEADER');
    :parameter.g_query_find := 'FALSE';



二.Form中Block的带条件重新查询



Form中某些按钮可能调用了Package对表中某些字段进行更新,但是数据库中字段的修改不会马上反映到form的界面上,所以要进行重新查询,但是用户可能使用了查询窗口进行查询之后然后再点击按钮动作,如果简单的使用execute_query进行查询的话那么就会把原来的查询条件冲掉。所以这里写了一个公用的包进行查询

procedure query_block(p_block_name varchar2)
is
  l_cursor_block     varchar2(50);
  l_cursor_record    number;
  l_cursor_item      varchar2(50);
  l_trigger_record   number;
  l_default_where    varchar2(1000);
  l_last_query       varchar2(2000);
  l_where_anchor     number;
  l_order_anchor     number;
  l_where_clause     varchar2(1000);
  l_message_level    number;
begin
  l_cursor_item    := name_in(‘SYSTEM.CURSOR_ITEM’);
  l_cursor_record  := name_in(‘SYSTEM.CURSOR_RECORD’);
  l_cursor_block   := name_in(‘SYSTEM.CURSOR_BLOCK’);
  l_trigger_record := l_cursor_record;
  l_message_level  := :system.message_level;
  if l_cursor_block <> p_block_name then
     l_trigger_record := get_block_property(p_block_name, CURRENT_RECORD);
     go_block(p_block_name);
       if name_in(‘SYSTEM.CURSOR_BLOCK’) <> p_block_name then
         fnd_message.debug(‘DEVELOPER ERROR: To select records ‘||
                          ‘in another block, you must be able to navigate.’);
         raise FORM_TRIGGER_FAILURE;
       end if;
  end if;
  l_default_where := get_block_property(p_block_name, DEFAULT_WHERE);
  l_last_query    := get_block_property(p_block_name, LAST_QUERY);               
  //检查是否有Order By语句
  if instr(upper(l_last_query), ‘ORDER BY’) = 0 then
    l_order_anchor := length(l_last_query);
  else
    l_order_anchor := instr(upper(l_last_query), ‘ORDER BY’) – 1;
  end if;
  if instr(upper(l_last_query), ‘WHERE’) = 0 then
    l_where_anchor := l_order_anchor – 5;
  else
    l_where_anchor := instr(upper(l_last_query), ‘WHERE’) + 1;
  end if; 
  l_where_clause  := substr(l_last_query, l_where_anchor+6, l_order_anchor-l_where_anchor-5);       
 
  set_block_property(p_block_name,default_where,l_where_clause);
  :system.message_level := 25;
  execute_query;
  :system.message_level := l_message_level;
  set_block_property(p_block_name,default_where,l_default_where);
  go_block(p_block_name);
  go_record(l_trigger_record);
  go_block(l_cursor_block);
  go_record(l_cursor_record);
  go_item(l_cursor_item);
end query_block;


程序首先保存当前的block,record以及Item使得在执行完之后光标依然停留在原位置而不会跳来挑去。之后判断当前所在的Block是否和参数传进来的P_Block一致,如果不一致,那么就goP_Block上,然后获取该BlockLast_Query,由于可能查询带有Order By,所以需要判断是否带有Order By来决定截取的最终位置。取得了where条件之后,通过使用set_block_property(p_block_name,default_where,l_where_clause)以及execute_queryblock进行查询,在这之前需要先保存原来的default where,在查询之后将default_where设置为默认的。最后讲光标定位到原位置。


调用的时候输入Block Name,便可以截取到Block的查询条件查询该Block





<think>根据用户的问题,需要在Form2中调用Form1中定义的方法。结合提供的引用,特别是引用[1]、[2]和[3],可以看出实现的关键在于如何让Form2能够访问到Form1的实例。以下是几种常见的方法: ### 方法1:通过构造函数传递Form1的实例 在创建Form2时,将Form1的实例传递给Form2。这样Form2内部就可以通过这个引用来调用Form1的方法。 **步骤:** 1. 在Form2类中定义一个私有字段来存储Form1的引用。 2. 修改Form2的构造函数,使其接收一个Form1类型的参数,并将该参数赋值给私有字段。 3. 在Form2中需要调用Form1方法的地方,使用这个私有字段来调用。 **代码示例:** 在Form1中打开Form2并传递自身引用: ```csharp // Form1.cs private void Button1_Click(object sender, EventArgs e) { Form2 form2 = new Form2(this); // 将当前Form1实例传递给Form2 form2.Show(); } // 在Form1中定义一个需要被Form2调用的方法 public void MyMethod(string message) { MessageBox.Show(message); } ``` 在Form2中: ```csharp // Form2.cs private Form1 _form1; // 用于存储Form1的引用 public Form2(Form1 form1) { InitializeComponent(); _form1 = form1; } private void CallForm1MethodButton_Click(object sender, EventArgs e) { _form1.MyMethod("Hello from Form2!"); } ``` ### 方法2:使用静态成员(如果Form1是单例或特定实例) 如果Form1在整个应用程序中只有一个实例(例如主窗体),可以在Form1中定义一个静态属性指向自身,这样Form2就可以通过这个静态属性访问Form1的实例。 **步骤:** 1. 在Form1类中创建一个静态属性(例如`Instance`),并在Form1实例化时将其赋值。 2. 在Form2中通过`Form1.Instance`来访问Form1的实例,并调用其方法。 **代码示例:** 在Form1中: ```csharp // Form1.cs public static Form1 Instance { get; private set; } public Form1() { InitializeComponent(); Instance = this; // 在构造函数中将当前实例赋值给静态属性 } // 需要被调用的方法 public void MyMethod(string message) { MessageBox.Show(message); } ``` 在Form2中调用: ```csharp // Form2.cs private void CallForm1MethodButton_Click(object sender, EventArgs e) { if (Form1.Instance != null) { Form1.Instance.MyMethod("Called from Form2"); } } ``` ### 方法3:通过事件或委托(更松散的耦合) 如果希望Form1和Form2之间解耦,可以使用事件机制。Form2触发一个事件,Form1订阅该事件并在事件处理中执行相应的方法。 **步骤:** 1. 在Form2中定义一个事件。 2. 在Form1中创建Form2实例时,订阅Form2的事件。 3. 当Form2需要调用Form1的方法时,触发事件。 4. Form1的事件处理程序执行自己的方法。 **代码示例:** 在Form2中定义事件: ```csharp // Form2.cs public event Action<string> OnCallForm1Method; private void Button_Click(object sender, EventArgs e) { OnCallForm1Method?.Invoke("Message from Form2"); } ``` 在Form1中: ```csharp // Form1.cs private void OpenForm2Button_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.OnCallForm1Method += (message) => MyMethod(message); form2.Show(); } public void MyMethod(string message) { MessageBox.Show(message); } ``` ### 注意事项 - **方法选择**:如果Form1和Form2有明确的父子关系,方法1(通过构造函数传递)是最直接的方式。如果Form1是主窗体且唯一,方法2(静态实例)比较方便。如果希望降低耦合度,方法3(事件)更合适。 - **内存泄漏**:在方法3中,需要注意事件的取消订阅,避免因Form2未被释放而导致Form1也无法释放。可以在Form2关闭时取消事件订阅。 ### 引用说明 以上方法参考了引用[1]和[2]中的思路:通过传递窗体实例(如引用[1]中`frm2.FromForm1`的调用)或使用静态成员(如引用[2]中`Form1.sform1`的静态引用)来实现跨窗体调用[^1][^2]。 §§相关问题§§ 1. C#中如何实现窗体间的数据传递? 2. 在C# WinForms中,如何避免窗体间引用导致的内存泄漏? 3. 除了传递窗体实例,还有哪些方法可以在不同窗体间调用方法?</think>### C# Form2 调用 Form1 方法实现指南 在 C# WinForms 中,Form2 调用 Form1 的方法主要有以下几种实现方式: #### 方法1:通过 Form1 实例传递引用(推荐) ```csharp // Form1.cs public partial class Form1 : Form { public void ShowMessage(string msg) // 需要被调用的方法 { MessageBox.Show(msg); } private void OpenForm2Button_Click(object sender, EventArgs e) { Form2 form2 = new Form2(this); // 将当前Form1实例传递给Form2 form2.Show(); } } // Form2.cs public partial class Form2 : Form { private Form1 _form1; // 存储Form1引用 public Form2(Form1 form1) { InitializeComponent(); _form1 = form1; // 接收Form1实例 } private void CallForm1MethodButton_Click(object sender, EventArgs e) { _form1.ShowMessage("从Form2调用成功!"); // 调用Form1的方法 } } ``` **优点**:类型安全,直接访问实例方法 **缺点**:需要维护引用关系[^1] #### 方法2:使用静态成员(适合单例场景) ```csharp // Form1.cs public partial class Form1 : Form { public static Form1 Instance; // 静态实例引用 public Form1() { InitializeComponent(); Instance = this; // 初始化时赋值 } public void CustomMethod() { // 方法实现 } } // Form2.cs private void CallMethodButton_Click(object sender, EventArgs e) { if(Form1.Instance != null) { Form1.Instance.CustomMethod(); // 通过静态实例调用 } } ``` **优点**:全局可访问 **缺点**:需处理空引用和多实例问题[^2] #### 方法3:使用事件机制(解耦方案) ```csharp // Form2.cs public partial class Form2 : Form { public event Action<string> RequestForm1Action; // 定义事件 private void TriggerEventButton_Click(object sender, EventArgs e) { RequestForm1Action?.Invoke("需要处理的数据"); // 触发事件 } } // Form1.cs private void OpenForm2Button_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.RequestForm1Action += HandleForm2Request; // 订阅事件 form2.Show(); } private void HandleForm2Request(string data) { // 处理Form2的请求 MessageBox.Show($"收到数据: {data}"); } ``` **优点**:松耦合,符合事件驱动模型 **缺点**:需要额外的事件处理逻辑 #### 方法4:通过控件Owner属性 ```csharp // Form1中打开Form2 Form2 form2 = new Form2(); form2.Show(this); // 传递owner // Form2中调用 private void CallMethodButton_Click(object sender, EventArgs e) { if(this.Owner is Form1 mainForm) { mainForm.ShowMessage("通过Owner调用"); } } ``` **优点**:无需额外字段 **缺点**:依赖窗体关系 ### 最佳实践建议 1. **简单场景**:使用构造函数传递引用(方法1) 2. **主窗体调用**:静态实例模式(方法2) 3. **解耦需求**:事件驱动(方法3) 4. **避免**:直接使用`Application.OpenForms`查找窗体(类型不安全) > 注意:所有方法都需要确保 Form1 实例已创建且未被释放。跨线程调用时需使用`Invoke`方法更新 UI[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值