委托该如何应对与通用和可扩展的类中的多个事件? [英] How can a delegate respond to multiple events with a generic and extensible class?

查看:137
本文介绍了委托该如何应对与通用和可扩展的类中的多个事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我七拼八凑的技术来处理在RDLC报告中的多个子报表,但我试图使它通用且可重复的,我反而不得不采取的模式,稍微调整它的每一种情况。



例如,如果我定义一个抽象的接口,像这样的,我只是削减,并从WinForm的粘贴到WinForm的需要:

 抽象类ISolutionStrategy 
{
公共抽象无效AlgorithmInterface(Int64的searchCriteria,SubreportProcessingEventArgs E);
}






首先,我想能够通过包括有,目的是把这个变成每个窗体。我也想封装由代表处理调度的行为,并作出处理方法通用以及



因此,设计要求是:




  • 创建可包含在一个WinForm来处理多个报表处理

  • 实例化和配置的对象对象在WinForm

  • 生成调度表或开关/ case语句在WinForm

  • 转至所有来处理,具体的质量要求的方法的winform的报表查看器



目标是使可测试独立并取得稳健的对象,也没有削减和粘贴轮,做了一堆每个新的winform手动调整的。



在我看来,有人已经找到了更好的设计,一个比我现在有在那里。



创建一个可以包含在一个WinForm来处理多个报表处理



到目前为止,我有一个对象在当地的形式加载事件的委托:

  this.reportViewer1.LocalReport.SubreportProcessing + =新SubreportProcessingEventHandler(LocalReport_SubreportProcessing); 



这是由switch语句中的* LocalReport_SubreportProcessing *方法。

$ B $处理b

方法的主体包含一个switch语句:

 无效LocalReport_SubreportProcessing(对象发件人,SubreportProcessingEventArgsê )

{
字符串commonSubreportKey = _commonSubreportKey;

开关(e.ReportPath)
{
案rptSubAlternateParts:
runSubAlternatePart(即commonSubreportKey,新GetAlternateParts());
中断;
案rptSubGetAssemblies:
runSubFailurePart(即commonSubreportKey,新GetAssemblies());
中断;
案rptSubGetAssemblies:
runSubGetGetEndItemLRMFailureInfo(即commonSubreportKey,新GetEndItemLRMFailureInfo());
中断;
案rptSubGetAssemblies:
runSubGetSubAssemblies(即commonSubreportKey,新GetSubAssemblies());
中断;
默认:
中断;
}




旁白:

在我看来,交换机相比,
替代我认为主要是人类可读。我认为使用哈希与报告
的名字作为键和函数调用数据的价值。不过,我
真的不知道该怎么做,我认为这会为
别人理解更难。


$ B

$ b:
$ b


之后,就会调用重新排列从switch语句中的函数调用中传递的信息的功能做
$ b

 私有静态无效runSubAlternatePart(SubreportProcessingEventArgs E1,字符串commonReportKey,GetAlternatePart MYAP)
{
myAP.AlgorithmInterface(commonReportKey,E1);
}

这重排绝对是代码口吃,不过是一个看似必要的中间向战略模式我试图实现:

 抽象类IStrategy 
{
公共抽象无效AlgorithmInterface(字符串searchParam ,SubreportProcessingEventArgs E);

}

下面是的一个具体实施战略报告:

 类GetAlternatePart:IStrategy 
{
私人BLL.AlternatePartBLL DS =新BLL.AlternatePartBLL( );


公共覆盖无效AlgorithmInterface(字符串searchParam,SubreportProcessingEventArgs E)
{

e.DataSources.Clear();
的DataTable myDataTable = ds.GetAlternativePart(searchParam);
的DataSet myDataSet =新的DataSet();
myDataSet.Tables.Add(myDataTable);
e.DataSources.Add(新ReportDataSource(BLL_AlternatePartBLL,myDataSet.Tables [0]));
}

}
}

在任何情况下,我的愿望是没有手线相同的逻辑多次报道的,因为我有多个子报表许多报告。



我想库质量的方法使用类动态创建所在的口吃发生的中间部分,我想在这实际上实现了详细的连接子报表到相应的数据源的匿名功能可按通行。



有关与子报表,甚至几个一次性的报告单报告,我在做什么是好的,但怎么能做出减少人工,更强大和更容易测试?



我的环境是Visual Studio 2008中与.NET 3.5的目标;似乎有在课堂上如何抽象声明的差异以及它们如何编译。


解决方案

我建议的解决方案是一个很简单的重构基类,它减少了你就需要在每个WinForm的写两件事情的代码:1)用这种形式报告的设置; 2)如何获得这种形式报告数据的定义。



假设每个WinForm的从名为ReportForm一个基类,每个WinForm的会看的代码继承像这样的:

 公共部分Form1类:ReportForm 
{
公共Form1中()
{
//线了使用Visual Studio的设计报表查看器基类
base.WinFormReport = reportViewer1.LocalReport的报告;

的InitializeComponent();
}

//搜索参数将是每一个WinForm的不同,大概会
//来自该表格上的一些WinForm的UI元素,例如,parentPartTextBox.Text
保护覆盖DataResult GetReportData(SubreportProcessingEventArgs E)
{
//返回数据结果,其中包含一个数据表和标签,这将是
//传递到报告数据源
//你可以在DataResult使用的数据集,而不是数据表,如果需要
开关(e.ReportPath)
{
案rptSubAlternateParts:
返回新DataResult(
新BLL.AlternatePartBLL()GetAlternativePart(parentPartTextBox.Text)
,BLL_AlternatePartBLL
)。

案rptSubGetAssemblies:
返回新DataResult(
新BLL.SubAssemblyBLL()GetSubAssemblies(someOtherTextBox.Text)
,BLL_SubAssemblyBLL
。 );

默认:
抛出新NotImplementedException(的String.Format(子报表{0}未实现,e.ReportPath));

}
}





上面的代码做这些事:



1)通知该报告已在表单中使用的基类(ReportForm)。你可以重构报告到ReportForm,以及如果你喜欢,但我的方法可以让你还创建和操纵您的ReportViewer及其在Visual Studio报表。但是,如果以编程方式,而不是通过在设计报告,你可能想从派生的WinForm类送报告到基类。



2)定义如何报告将得到所有的子报告的数据。为此,我们只需要返回一个DataTable和标签,因为这是将最终由报表数据源所需的全部。结合数据表和标签的RDLC数据源代码属于基础类(ReportForm),因为该绑定代码将共同为所有的WinForms。



现在中,ReportForm代码应该如下所示:

  ///<总结> 
///不要剪切和放大器;粘贴到任何Windows窗体,继承你从一个基类
想要的行为///< /总结>
公共抽象类ReportForm:System.Windows.Forms.Form中
{
//我不知道究竟这是什么的用途,但我把它放在基类的情况下,有一些使用了在这里
保护字符串_commonSubreportKey =12345;

//这将是在每个WinForm的所需的一行代码 - 提供基类的引用
//报告,因此它具有访问SubreportProcessing事件$ B保护报告WinFormReport $ b {搞定;组; }

//制作这种抽象的,需要每个派生的WinForm实现GetReportData - 万无一失!
保护抽象DataResult GetReportData(SubreportProcessingEventArgs E);

//线了subreport_processing处理程序时,任何WinForm的负荷
//你可以在派生类的WinForms,如果你需要一些的WinForms不同的行为,
覆盖此//但我打赌此默认行为在大多数或所有情况下,
起到很好的保护虚拟无效Form1_Load的(对象发件人,EventArgs五)
{
Report.SubreportProcessing + =新SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
}

//当子报表处理事件触发,处理它这里
//你也可以覆盖在派生类中此方法如果需要的话
受保护的虚拟无效LocalReport_SubreportProcessing(对象发件人,SubreportProcessingEventArgs E)
{
//获取所需报表
dataResult dataResult = this.GetReportData(E)中的数据;

e.DataSources.Clear();
e.DataSources.Add(新ReportDataSource(dataResult.Label,dataResult.Table));
}
}

注意ReportForm基类表继承,和那么所有的WinForms将继承ReportForm - 这是关键,整个设计。下面是这个ReportForm基类是如何工作的:



1)当在WinForm被实例化,基础属性WinFormReport设置,因此基本对象知道该报告是在使用



2)当在WinForm负载,Form Load事件被称为上的基类,因为它不在派生类中定义。在窗体加载,报告的Subreport_Processing事件接线。



3)当用户输入参数,点击东西在报表查看器来创建报告,最终的子报表通过RDLC实例化,并为每个子报表中的Subreport_Processing事件触发多次,一次。



4)当事件触发时,基类的事件处理程序调用GetReportData(E),其中将调用在WinForm定义的GetReportData方法。注意,此方法是在基类抽象,因此它不能在基类来定义的,但必须在派生类来定义



5)GetReportData( e)在WinForm的方法使用您最初指定的调度逻辑,返回一个DataTable(也可以是一个DataSet如果需要)和文本字符串的基本处理程序。



6)基本处理程序需要的数据表/ DataSet和文本字符串,将它们送至该报告作为报表数据源,并有也可以做其他需要显示的报告。



左思右想,我决定推荐的共同行为的一个相当简单的重构为一个基类,因为我认为这会工作给予你的要求,我没有看到任何东西更复杂,将需要。我想你会发现这种方法非常具有可读性,有它绝对减少所需要的每个新的WinForm,和高于一切,感到非常扩展的;也就是说,当你继续发展该系统,你总是会问:这是新的代码的东西,将需要在每个WinForm的重复,或者是常见的,例如,它应该进入的基类?



欢迎,如果您有关于这种方法的任何问题或疑虑,添加评论,我祝你好运吧。我希望这是你正在寻找的地方!


I have rigged up a technique to handle multiple subreports in an rdlc report, but as I have tried to make it generic and repeatable, I have instead had to take the model and tweak it slightly for each case.

For example, if I define an abstract interface, like such, I just cut and paste it from winform to winform as needed:

abstract class ISolutionStrategy
{
    public abstract void AlgorithmInterface(Int64 searchCriteria, SubreportProcessingEventArgs e);
}


First, I want to be able to bring this into each form by including an has-a object. I also want to encapsulate the behaviors of handling the dispatching by the delegate, and make the handling methods "generic" as well.

So, the design requirements are:

  • Create an object that can be included in a winform to handle multiple subreport processing
  • Instantiate and configure the object in the winform
  • Build the dispatch table or switch/case statement in the winform
  • Pass in all the methods to handle the specific requirments of that winform's report viewer

The GOAL is to make an object that can be tested standalone and made robust, and also to not have to cut and paste the wheel and do a bunch of manual tweaking for each new winform.

It seems to me that someone has found a better design out there than the one I currently have.

Create an object that can be included in a winform to handle multiple subreport processing

So far, I have a delegate in the local forms load event:

this.reportViewer1.LocalReport.SubreportProcessing += new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);

which is handled by a switch statement in the *LocalReport_SubreportProcessing* method.

The body of the method contains a switch statement:

void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)

        {
            String commonSubreportKey = _commonSubreportKey;

            switch (e.ReportPath)
            {
                case "rptSubAlternateParts":
                    runSubAlternatePart(e, commonSubreportKey, new GetAlternateParts());
                    break;
                case "rptSubGetAssemblies":
                    runSubFailurePart(e, commonSubreportKey, new GetAssemblies());
                    break;
                case "rptSubGetAssemblies":
                    runSubGetGetEndItemLRMFailureInfo(e, commonSubreportKey, new GetEndItemLRMFailureInfo());
                    break;
                case "rptSubGetAssemblies":
                    runSubGetSubAssemblies(e, commonSubreportKey, new GetSubAssemblies());
                    break;
                default:
                    break;
            }

Aside:

In my opinion, the switch is mostly human readable compared to the alternative I considered. I considered using a hash with the report name as the key and the function call data as the value. However, I did not really know how to do it and I thought it would be harder for someone else to understand.


After that, a call is made to a function that rearranges the information passed from the function call in the switch statement:

    private static void runSubAlternatePart(SubreportProcessingEventArgs e1, String  commonReportKey, GetAlternatePart myAP)
            {
                myAP.AlgorithmInterface(commonReportKey, e1);
            }

This rearrangement is definitely code stuttering, but is a seemingly necessary intermediate to the Strategy pattern I am attempting to implement:

     abstract class IStrategy
        {
            public abstract void AlgorithmInterface(String searchParam, SubreportProcessingEventArgs e);

        }

Here is a concrete implementation of the Strategy for one of the reports:

class GetAlternatePart : IStrategy
{
private BLL.AlternatePartBLL ds = new BLL.AlternatePartBLL();


public override void AlgorithmInterface(String searchParam, SubreportProcessingEventArgs e)
              {

                    e.DataSources.Clear();
                    DataTable myDataTable = ds.GetAlternativePart(searchParam);
                    DataSet myDataSet = new DataSet();
                    myDataSet.Tables.Add(myDataTable);
                e.DataSources.Add(new ReportDataSource("BLL_AlternatePartBLL", myDataSet.Tables[0]));
            }

        }
    }

In any case, my desire is to not have to hand wire the same logic repeatedly between reports, as I have many reports with multiple subreports.

I would like a library quality way of using a class to dynamically create the middle parts where the stuttering occurs, and I would like to pass in an "anonymous" funciton which actually implements the detailed connecting of the subreport to its corresponding data source.

For a single report with subreports, or even a few one-off reports, what I am doing is ok, but how can it be made less manual, more robust and more testable?

My environment is Visual Studio 2008 with a target of .NET 3.5; there seems to be a difference in how abstract classes are declared and how they are compiled.

解决方案

The solution I would suggest is a very simple refactoring to a base class, and it reduces the code you would need to write in each WinForm to two things: 1) the setting of the Report used for that form; and 2) the definition of how to get the report data for that form.

Assuming that each WinForm inherits from a base class called ReportForm, the code for each WinForm will look like this:

public partial class Form1 : ReportForm
{
    public Form1()
    {
        // Wire up the report used by the Visual Studio-designed report viewer to the base class
        base.WinFormReport = reportViewer1.LocalReport;

        InitializeComponent();
    }

    // The search parameters will be different for every winform, and will presumably
    //  come from some winform UI elements on that form, e.g., parentPartTextBox.Text
    protected override DataResult GetReportData(SubreportProcessingEventArgs e)
    {
        // Return the data result, which contains a data table and a label which will be
        //  passed to the report data source
        // You could use DataSet in DataResult instead of DataTable if needed
        switch (e.ReportPath)
        {
            case "rptSubAlternateParts":
                return new DataResult(
                    new BLL.AlternatePartBLL().GetAlternativePart(parentPartTextBox.Text)
                    , "BLL_AlternatePartBLL"
                );

            case "rptSubGetAssemblies":
                return new DataResult(
                    new BLL.SubAssemblyBLL().GetSubAssemblies(someOtherTextBox.Text)
                    , "BLL_SubAssemblyBLL"
                );

            default:
                throw new NotImplementedException(string.Format("Subreport {0} is not implemented", e.ReportPath));

        }
    }
                                .
                                .
                                .

The above code does these things:

1) Tells the base class (ReportForm) which Report has been used in the Form. You could refactor Report down to ReportForm as well if you like, but my approach allows you to still create and manipulate your ReportViewer and its Reports in Visual Studio. But if you are passing the Report programmatically and not in the designer, you might want to send Report from the derived WinForm classes into the base class.

2) Defines how the report will get all of its subreports' data. For that, we just need to return a DataTable and a label, as that is all that will eventually be required by the report data source. The code which binds the DataTable and label to the RDLC data source belongs in the base class (ReportForm), as that binding code will be common for all your WinForms.

Now, the ReportForm code should look as follows:

/// <summary>
/// Don't cut & paste into any Windows Forms, inherit the behavior you want from a base class
/// </summary>
public abstract class ReportForm : System.Windows.Forms.Form
{
    // I'm not sure exactly what this is used for, but I put it in base class in case there is some use for it here
    protected string _commonSubreportKey = "12345";

    // This will be the one line of code needed in each WinForm--providing the base class a reference
    //  to the report, so it has access to the SubreportProcessing event
    protected Report WinFormReport { get; set; }

    // Making this abstract requires each derived WinForm to implement GetReportData--foolproof!
    protected abstract DataResult GetReportData(SubreportProcessingEventArgs e);

    // Wire up the subreport_processing handler when any WinForm loads
    // You could override this in derived WinForms classes if you need different behavior for some WinForms,
    //  but I would bet this default behavior will serve well in most or all cases
    protected virtual void Form1_Load(object sender, EventArgs e)
    {
        Report.SubreportProcessing += new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
    }

    // When the Subreport processing event fires, handle it here
    // You could also override this method in a derived class if need be
    protected virtual void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
    {
        // Get the data needed for the subreport
        DataResult dataResult = this.GetReportData(e);

        e.DataSources.Clear();
        e.DataSources.Add(new ReportDataSource(dataResult.Label, dataResult.Table));
    }
}

Notice that the ReportForm base class inherits from Form, and then all WinForms will inherit from ReportForm--that is the key to the whole design. Here's how this ReportForm base class works:

1) When the WinForm is instantiated, the base property WinFormReport is set, so the base object knows which Report is in use.

2) When the WinForm loads, the Form Load event is called on the base class since it is not defined in the derived class. On form load, the report's Subreport_Processing event is wired up.

3) When the user enters parameters and clicks something to create the report in the report viewer, eventually the subreports are instantiated by RDLC and the Subreport_Processing event fires multiple times, once for each subreport.

4) When the event fires, the base class event handler calls GetReportData(e), which will invoke the GetReportData method defined on the WinForm. Note that this method is abstract in the base class, so it cannot be defined on the base class, but must be defined in the derived class.

5) The GetReportData(e) method in the WinForm uses the dispatcher logic you initially indicated, to return a DataTable (could also be a DataSet if you need) and a text string to the base handler.

6) The base handler takes the DataTable/DataSet and the text string, feeds them to the report as the report data source, and there could also do whatever else is needed to display the report.

After much thought, I decided on recommending a fairly straightforward refactoring of common behavior into a base class, because I thought it would work given your requirements, and I didn't see where anything more complicated would be needed. I think you will find this approach very readable, have it absolutely minimize what is needed in each new WinForm, and above all, find it extremely extensible; that is, as you continue to develop the system, you will always ask "is this new code something that will need repeating in each WinForm, or is it common such that it should go into the base class?"

Feel free to add a comment if you have any questions or concerns about this approach, and I wish you the best of luck with it. I hope it is just what you are looking for!

这篇关于委托该如何应对与通用和可扩展的类中的多个事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆