views:

323

answers:

2

Hi all,

I found a question that I belive is what I was looking for, but there were certain things that I was not following in the answers. Therefore, I'd like to ask the question in a different way (Thanks for your patience). Here is the link I am referring to:

http://stackoverflow.com/questions/259508/how-to-avoid-duplicating-logic-on-two-similar-winforms

OK. I have a dialog that I created. We've got controls for User Input, buttons to display other dialogs (to gain other input), etc. Aesthetically, I prefer the dialog with the controls laid out vertically. Anyhow, I was also thinking of creating a UserControl version of this dialog. This UserControl would have all the same controls, and all the same logic, but the controls would be laid out entirely different (more horizontal, then vertical).

So, I can't just create another (3rd) UserControl that I drop on the orignal form, and on the UserControl I want to create. (This 3rd UserControl would then contain all logic - thus, sharing between the two). I can't do this because of the different layouts.

I have no problem creating the two (Form, UserControl), with the controls laid out differently, but I don't want to 'cut-and-paste' all the logic from one to the other.

This does not seem like a case for MVP, or MVC. My model is the dialog itself. The dialog is intialized with some values, yes, but once initialized the "Model" becomes further User Input (which I then grab when they press the OK button).

Take for example this code (an event for one of my buttons on this dialog):

    private void EditQuery_Click(object sender, EventArgs e)
    {
        try
        {
            EditQueryParameters();
        }
        catch (System.Exception ex)
        {
            // TODO: Write ErrMsg to Log file.
            MessageBox.Show("Edit Query Parameters Error:\n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void EditQueryParameters()
    {
        if (m_ReportType.QueryScoreDetails && optPickDetail.Checked)
        {
            // This brings up a different type of dialog
            QueryDetails();
            return;
        }

        // DateRange, StartDate, and EndDate are all saved from the last time
        // I called this dialog
        DateType DtType = new DateType(m_ReportType.DBDateRangeField,
            m_DateRange, m_StartDate, m_EndDate);
        // StartTime, EndTime too!
        TimeType TmType = new TimeType(m_ReportType.DBTimeRangeField,
            m_StartTime, m_EndTime);

        List<AdvancedFilter> Filters = null;
        if (lstAdvancedQuery.Items.Count > 0)
        {
            Filters = new List<AdvancedFilter>();
        }
        for (int i = 0; i < lstAdvancedQuery.Items.Count; ++i)
        {
            Filters.Add((AdvancedFilter)lstAdvancedQuery.Items[i]);
        }

        // QueryType is also saved from the last time I called QueryBuilder
        QueryBuilder QryBuilder = new QueryBuilder(m_ReportType.DBCatalog, m_ReportType.DBTable,
            m_QueryType, ref DtType, ref TmType, ref Filters);

        // I am using Visual WebGUI, I have to do it this way
        QryBuilder.Closed += new EventHandler(QryBuilder_Closed);
        QryBuilder.ShowDialog();
    }

I mean, I suppose I could have some "logic" class, which exposes something like:

    public void EditQueryParameters(ref ReportType RptType, bool PickDetail,
         string DateRange, DateTime StartDate, DateTime EndDate,
         DateTime StartTime, DateTime EndTime, string QueryType)
    {
        if (ReportType.QueryScoreDetails && PickDetail)
        {
            // This brings up a different type of dialog
            QueryDetails();
            return;
        }


        DateType DtType = new DateType(ReportType.DBDateRangeField,
            DateRange, StartDate, EndDate);
        TimeType TmType = new TimeType(ReportType.DBTimeRangeField,
            StartTime, EndTime);

        // Yikes, more stuff to add to the signature of my method
        // Will have to pull this outside the method and pass in Filters
        List<AdvancedFilter> Filters = null;
        if (lstAdvancedQuery.Items.Count > 0)
        {
            Filters = new List<AdvancedFilter>();
        }
        for (int i = 0; i < lstAdvancedQuery.Items.Count; ++i)
        {
            Filters.Add((AdvancedFilter)lstAdvancedQuery.Items[i]);
        }

        // QueryType is also saved from the last time I called QueryBuilder
        QueryBuilder QryBuilder = new QueryBuilder(ReportType.DBCatalog, ReportType.DBTable,
            QueryType, ref DtType, ref TmType, ref Filters);

        // I am using Visual WebGUI, I have to do it this way
        QryBuilder.Closed += new EventHandler(QryBuilder_Closed);
        QryBuilder.ShowDialog();
    }

There's a lot of set-up to use this method. I don't know, maybe I'm looking for something more .. "slick"?

On top of that, look at some (not all) of my init code (this is called from constructor or form_Load; it doesn't seem worth it to add this to the logic class, so that's all still "cut and paste" between the two):

    private void InitializeUserDefinedTitle()
    {
        txtUserTitle.Text = m_UserTitle;
    }

    private void InitializePrintSelectionCriteria()
    {
        // Print Selection Criteria
        chkSelectionCriteria.Checked = m_printSelectionCriteria;
    }

    private void InitializeTrendBy()
    {
        cmbTrend.Items.AddRange(Enum.GetNames(typeof(TrendBy)));
        cmbTrend.SelectedIndex = (int)m_TrendBy;
        cmbTrend.Visible = m_ReportType.TrendVisible;
        lblTrend.Visible = m_ReportType.TrendVisible;
    }

In summary, the original WinForm is a dialog that is intialized with data (constructor), is displayed to the user for input, when they OK the dialog that data is retrieved (and that data is stored outside the dialog, in member variables, for the next time they call the dialog - this is because we want to show what they last picked/entered).

That type of dialog I just described will also be a user control, and the logic should be shared between the two.

Thanks.

+1  A: 

Instead of encapsulating the logic, I would encapsulate the layout. Use a property of the user control to specify which layout yout want. Then wherever it is (standalone form, one of three instances on the same form, whatever) you access it and specify the layout the same way.

As for how to encapsulate the layout, there are a bunch of possibilities. You could just do it programatically, i.e. write each version of the layout code. (The programamatic version would be cleaner if you used some kind of layout containers, like the Panels in WPF.) You could draw the layout in a designer, and copy the generated code. The different versions of the layout logic could be stuffed into private methods, or encapsulated into objects.

Eric
This is a good idea too. I'm not using WPF, and I like the "copy the genrated code" idea (though not sold on it). You gave me some ideas to mess around with today.
JustLooking
+2  A: 

You can make two controls A and B, each containing the same buttons and/or other input controls arranged differently. Controls A and B will have identical properties, and events. The form (or third control) will contain the event handlers that allow the logic to be contained in only one place.

You can display either control A or B using the visible property or by adding one to the container.controls property, the container being the containing form or control.

And, for example, instead of having a handler for button1 in controls A and B that handles the complete logic of the button press, the handlers for button1 in control A and B would just raise an event that will be handled by the container of control A or B.

xpda
Interesting idea, I will try it out today.
JustLooking