C# - 避免在WinForm和UserControl之间复制逻辑

时间:2009-12-01 22:11:03

标签: c# winforms design-patterns

我发现了一个我相信的问题就是我在寻找的问题,但有些事情我没有在答案中遵循。因此,我想以不同的方式提出这个问题(感谢您的耐心等待)。这是我所指的链接:

How to avoid duplicating logic on two similar WinForms?

行。我有一个我创建的对话框。我们有用户输入的控件,用于显示其他对话框的按钮(以获得其他输入)等。在美学上,我更喜欢带有垂直布局的控件的对话框。无论如何,我还在考虑创建这个对话框的UserControl版本。这个UserControl将具有所有相同的控件和所有相同的逻辑,但控件将完全不同(更水平,然后垂直)。

所以,我不能只创建我放在orignal表单上的另一个(第三个)UserControl,以及我要创建的UserControl。 (此第三个UserControl将包含所有逻辑 - 因此,在两者之间共享)。由于布局不同,我无法做到这一点。

创建两个(Form,UserControl)没有问题,控件布局不同,但我不想将所有逻辑从一个逻辑“剪切并粘贴”到另一个逻辑。

这似乎不是MVP或MVC的情况。我的模型是对话框本身。该对话框初始化了一些值,是的,但是一旦初始化,“模型”就变成了进一步的用户输入(当我们按下OK按钮时我会抓住它)。

以此代码为例(此对话框中我的某个按钮的事件):

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

我的意思是,我想我可以有一些“逻辑”类,它暴露了类似的东西:

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

有很多设置可以使用这种方法。我不知道,也许我正在寻找更多......“光滑的”?

最重要的是,看看我的初始代码中的一些(不是全部)(这是从构造函数或form_Load中调用的;将它添加到逻辑类似乎不值得,所以这仍然是“切割和粘贴“两者之间”:

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

总之,原始的WinForm是一个初始化数据(构造函数)的对话框,显示给用户输入,当它们确定检索数据的对话框时(并且该数据存储在对话框外,在成员中)变量,下次他们调用对话框时 - 这是因为我们想要显示他们上次选择/输入的内容。

我刚才描述的那种对话框也是一个用户控件,逻辑应该在两者之间共享。

感谢。

2 个答案:

答案 0 :(得分:2)

您可以制作两个控件A和B,每个控件包含相同的按钮和/或其他不同的输入控件。控件A和B将具有相同的属性和事件。表单(或第三个控件)将包含允许逻辑仅包含在一个位置的事件处理程序。

您可以使用visible属性显示控件A或B,或者向container.controls属性添加一个,容器是包含的表单或控件。

并且,例如,在控件A和B中处理按钮的处理程序而不是处理按钮按下的完整逻辑,控制器A和B中的button1的处理程序只会引发一个将由对照A或B的容器。

答案 1 :(得分:1)

我会封装布局,而不是封装逻辑。使用用户控件的属性指定所需的布局。然后无论它在哪里(独立表单,同一表单上的三个实例之一,无论如何),您都可以访问它并以相同的方式指定布局。

至于如何封装布局,有很多可能性。您可以以编程方式执行此操作,即编写布局代码的每个版本。 (如果您使用某种布局容器(如WPF中的Panel),则编程版本会更干净。)您可以在设计器中绘制布局,并复制生成的代码。布局逻辑的不同版本可以填充到私有方法中,或封装到对象中。