How can a delegate respond to multiple events with a common and extensible class?

I developed a method for processing multiple subscriptions in an rdlc report, but since I tried to make it universal and repeatable, I had to take a model and tweak it a little for each case.

For example, if I define an abstract interface, for example, I just cut and paste it from winform into winform as needed:

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

First, I want to be able to bring this into every form by including a has-a object. I also want to encapsulate the dispatch processing behavior of the delegate and make the processing methods "generic" as well.

So, design requirements:

  • Create an object that can be included in winform to handle multiple subscription processing
  • Create and customize an object in winform
  • Create a distribution table or switch / case statement in winform
  • Pass all the processing methods for specific requirements of this winform report viewer

The goal is to make an object that you can test autonomously and make reliable, and also do not need to cut and paste the wheel and perform a bunch of manual settings for each new winform.

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

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

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

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

which is processed by the 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 was considering. I examined the use of a hash with the report name as a key and function call data as a value. However, I did not know how to do this, and I thought it would be harder for someone else to understand.


After that, the function is called, which reorders 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 certainly a stuttering code, but it seems to be a necessary gap to the strategy template that I am trying 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 not to translate the same logic between reports, since I have many reports with several reports.

I would like the library quality method to use a class to dynamically create middle parts where stuttering occurs, and I would like to pass a “anonymous” funciton that actually implements a detailed subheading connection with its corresponding source data.

For one report with subtitles or even several one-time reports, what I do is all right, but how can it be made less manual, more reliable and more verifiable?

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

+6
source share
1 answer

The solution I would suggest is a very simple refactoring for the base class, and it reduces the code that you need to write in each WinForm to two things: 1) setting up the report used for this form; and 2) determining how to obtain report data for this 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, eg, 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 the following:

1) Tells the base class (ReportForm) which report was used in the Form. You can also reorganize the report in ReportForm if you want, but my approach allows you to create and process ReportViewer and its reports in Visual Studio. But if you pass the report programmatically, and not in the constructor, you can send the report from WinForm derived classes to the base class.

2) Determines how the report will receive all the data of its subscriptions. To do this, we just need to return the DataTable and the label, since this is all that the report data source will ultimately need. The code that binds the DataTable and label to the RDLC data source belongs to the base class (ReportForm), as this binding code will be distributed to all your WinForms.

The ReportForm code should now look like this:

 /// <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)); } } 

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

1) When a WinForm instance is created, the base WinFormReport property is set, so the base object knows which report is being used.

2) When WinForm is loaded, the Form Load event is raised in the base class, since it is not defined in the derived class. When the form loads, the Subreport_Processing event of the report is loaded.

3) When a user enters parameters and clicks something to create a report in the report viewer, ultimately the subreports are created by the RDLC instance, and the Subreport_Processing event fires several times, once for each report.

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

5) The GetReportData (e) method in WinForm uses the dispatcher logic you specified to return a DataTable (it can also be a DataSet if you need it) and a text string for the base handler.

6) The basic handler accepts a DataTable / DataSet and a text string, passes them to the report as a report data source, and can also do whatever is necessary to display the report.

After much deliberation, I decided to recommend a fairly simple refactoring of general behavior into a base class, because I thought it would work based on your requirements, and I did not see where there would be something more complex. I think you will find this approach very readable, completely minimize what is needed in every 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 that will be repeated in every WinForm, or is it common to get 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. Hope this is exactly what you are looking for!

+4
source

All Articles