Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实现相同界面的多个自定义用户控件;有没有办法避免在每个代码中重复相同的代码?_C#_User Controls_Multiple Inheritance - Fatal编程技术网

C# 实现相同界面的多个自定义用户控件;有没有办法避免在每个代码中重复相同的代码?

C# 实现相同界面的多个自定义用户控件;有没有办法避免在每个代码中重复相同的代码?,c#,user-controls,multiple-inheritance,C#,User Controls,Multiple Inheritance,对不起,我在StackOverflow中找不到这个问题的答案,尽管这一定是重复的。我知道多重继承是不可能的 我的应用程序有50类自定义用户控件,所有这些控件都实现了我的IParamConfig接口 IParamConfig定义了5个属性和8个方法 在这些自定义用户控件中,有些从文本框继承,有些从按钮继承,有些从复选框继承,有些从组合框继承,等等 我可以创建一个ParamConfig类来实现IParamConfig的方法和属性;但是我知道我不能让我的每个自定义用户控件类同时从ParamConfig

对不起,我在StackOverflow中找不到这个问题的答案,尽管这一定是重复的。我知道多重继承是不可能的

我的应用程序有50类自定义用户控件,所有这些控件都实现了我的IParamConfig接口

IParamConfig定义了5个属性和8个方法

在这些自定义用户控件中,有些从文本框继承,有些从按钮继承,有些从复选框继承,有些从组合框继承,等等

我可以创建一个ParamConfig类来实现IParamConfig的方法和属性;但是我知道我不能让我的每个自定义用户控件类同时从ParamConfig和TextBox或其他用户控件类继承

因此,我不得不多次复制IParamConfig的方法和属性的实现

例如,我创建了一个抽象类AbtrParamConfigTextBox,它继承了TextBox并实现了IParamConfig的方法和属性;然后,我创建了许多类似文本框的自定义用户控件,这些控件继承自这个抽象类

类似地,我创建了一个抽象类AbtrParamConfigComboBox,它继承自ComboBox,还实现了IParamConfig的方法和属性

总的来说,我不得不将通用代码复制12次(图中红色的框)

如果我能让通用代码只出现一次,我会很高兴的

例如,如果我能让所有自定义用户控件从同一个类(ParamConfig)继承,我会很高兴

我尝试通过创建一个从UserControl继承的ParamConfig类,然后创建一个从ParamConfig继承的ParamConfigTextBox来实现这一点,该类包含一个文本框

但这导致了棘手的解决办法,因为我不再能够直接访问ParamConfigTextBox中TextBox的属性,因此我必须在ParamConfigTextBox中复制它们,并添加代码将属性从ParamConfigTextBox复制到其TextBox。丑陋的

问题:

我是否被困在必须将通用代码复制12次的困境中

我应该采用从UserControl继承的公共类方法吗

还是有更清洁的解决方案

编辑:用户界面的屏幕截图,如要求。 它有大约100个选项卡,每个选项卡都有用户控件来配置产品

编辑:

接口:

    // Functions
    string BmsParamName { get; } // Name of the parameter in the BMS's memory associated with this control
    void InitControlOnLoad(); // Initialize the control after load
    void ClearCtrl(); // Clear this control
    uint BitsUsedMask(); // Return the mask for the bits used in memory 
    void ShowValue16(ushort paramWord, bool isBadData); // Update the value or state displayed by this control
    void ShowValue32(uint paramValue32, bool isBadData); // Update the value or state displayed by this control
    uint NoOfBmsMemWords(); // Return the number of BMS memory words that this set uses
    void ShowParamArray(uint[] paramValues, uint bmsMemOfst, bool isBadData); // Update the value or state displayed by this control
    ushort GetEntry16(ushort configWord); // Receive the full word of configuration data and return it after replacing those bits that this configuration control is responsible for
    uint GetEntry32(); // Return the value in this control as a 16 bit unsigned integer
    ushort[] GetEntryArray(); // Get the set of configuration words for this configuration control
特性:

    #region Properties (Protected properties, available to inherited classes)

    // Name of BMS variable
    protected string bmsParamName = ""; 
    [DefaultValueAttribute(0), Category("_Vinci"),
    Description("Name of the variable in the BMS's memory associated with this control")]
    public string BmsParamName
    {
        get { return bmsParamName; }
        set { bmsParamName = value; }
    }

    // Conversion factor
    protected float conversionFactor = 1F;
    [DefaultValueAttribute(1),
     Description("To convert the BMS value to the value shown in this setting, divide by this factor"),
     Category("_Vinci")]
    public float ConversionFactor
    {
        get { return conversionFactor; }
        set { conversionFactor = value; }
    }

    // Width of the data
    protected VinciForm.DataWidth dataWidth = VinciForm.DataWidth.Data16; 
    [DefaultValueAttribute(VinciForm.DataWidth.Data16),
     Description("Whether the data in the BMS fill an entire 32 bit word, a 16 bit Word, just the top 8 bits (MSB), the lower 8 bits (LSB), or specific bits"),
     Category("_Vinci")]
    public VinciForm.DataWidth DataWidth
    {
        get { return dataWidth; }
        set
        {
            dataWidth = value;
            firstBitNo = 0u;
            switch (dataWidth)
            {
                case VinciForm.DataWidth.Data32:
                    noOfBits = 32u;
                    break;

                case VinciForm.DataWidth.Data16:
                    noOfBits = 16u;
                    break;

                case VinciForm.DataWidth.Data8LSB:
                    noOfBits = 8u;
                    break;

                case VinciForm.DataWidth.Data8MSB:
                    firstBitNo = 8u;
                    noOfBits = 8u;
                    break;

                case VinciForm.DataWidth.DataBits:
                    noOfBits = 16u;
                    break;
            }
        }
    }

    // Number of the least significant bit used
    protected uint firstBitNo = 0u; 
    [DefaultValueAttribute(0), Category("_Vinci"),
    Description("Number of the least significant bit used")]
    public uint FirstBitNo
    {
        get { return firstBitNo; }
        set { firstBitNo = value; }
    }

    // Number of bits used
    protected uint noOfBits = 16u; 
    [DefaultValueAttribute(3), Category("_Vinci"),
    Description("Number of bits used")]
    public uint NoOfBits
    {
        get { return noOfBits; }
        set { noOfBits = value; }
    }

    // Display format: unsigned, signed or hex
    protected VinciForm.DisplayFormat displayFrmtCode = VinciForm.DisplayFormat.UnsignedFormat; 
    [DefaultValueAttribute(VinciForm.DataWidth.Data16),
     Description("Display format: unsigned, signed or hex"),
     Category("_Vinci")]
    public VinciForm.DisplayFormat DisplayFormat
    {
        get { return displayFrmtCode; }
        set { displayFrmtCode = value; }
    }

    // Format string
    protected string displayFrmtStr = ""; 
    [DefaultValueAttribute(""),
     Description("Display format string (e.g.: F4 for 4 decimal places); leave blank for automatic"),
     Category("_Vinci")]
    public virtual string DisplayFrmtStr
    {
        get { return displayFrmtStr; }
        set { displayFrmtStr = value; }
    }

    #endregion

您的接口似乎无法访问实现控件的任何属性(至少不能访问示例中的属性)。这意味着将它们封装在自己的类中并重用它应该相当容易。即使它将访问控件的某些属性,您也可以在单独的类中实现接口,并使用回调方法等技术来访问控件特定的部分(不能给您一个示例,因为对于您刚才在文章中为我们提供接口的方法)

然后可以在控件中使用此类的实例,从而将控件保存到实现中并重用代码。这实际上就是许多常见的设计模式(如桥接器、访问者或适配器)所做的:用组合替换继承

为了说明我的观点,我使用了您界面的简化版本:

interface IParamConfig
{
    string BmsParamName
    {
        get;
        set;
    }

    float ConversionFactor
    {
        get;
        set;
    }
}
您可以在单独的帮助器类中实现此功能:

public class ParamConfig : IParamConfig
{
    private string bmsParamName = ""; 
    [DefaultValueAttribute(0),
    Description("Name of the variable in the BMS's memory associated with this control")]
    public string BmsParamName
    {
        get { return bmsParamName; }
        set { bmsParamName = value; }
    }

    // Conversion factor
    private float conversionFactor = 1F;
    [DefaultValueAttribute(1),
     Description("To convert the BMS value to the value shown in this setting, divide by this factor")]
    public float ConversionFactor
    {
        get { return conversionFactor; }
        set { conversionFactor = value; }
    }
}
public class ParamConfigTextBox : TextBox, IParamConfig
{
    private readonly ParamConfig paramConfig = new ParamConfig();

    [DefaultValueAttribute(0), Category("_Vinci"),
    Description("Name of the variable in the BMS's memory associated with this control")]
    public string BmsParamName
    {
        get { return paramConfig.BmsParamName; }
        set { paramConfig.BmsParamName = value; }
    }

    // Conversion factor
    [DefaultValueAttribute(1),
     Description("To convert the BMS value to the value shown in this setting, divide by this factor"),
     Category("_Vinci")]
    public float ConversionFactor
    {
        get { return paramConfig.ConversionFactor; }
        set { paramConfig.ConversionFactor = value; }
    }
}
我为属性编辑器保留了一些属性,即使它是一个帮助器类。稍后你会明白我为什么这么做。如果您想接近当前的实现,可以在控件上实现
IParamConfig
,并转发helper类的功能:

public class ParamConfig : IParamConfig
{
    private string bmsParamName = ""; 
    [DefaultValueAttribute(0),
    Description("Name of the variable in the BMS's memory associated with this control")]
    public string BmsParamName
    {
        get { return bmsParamName; }
        set { bmsParamName = value; }
    }

    // Conversion factor
    private float conversionFactor = 1F;
    [DefaultValueAttribute(1),
     Description("To convert the BMS value to the value shown in this setting, divide by this factor")]
    public float ConversionFactor
    {
        get { return conversionFactor; }
        set { conversionFactor = value; }
    }
}
public class ParamConfigTextBox : TextBox, IParamConfig
{
    private readonly ParamConfig paramConfig = new ParamConfig();

    [DefaultValueAttribute(0), Category("_Vinci"),
    Description("Name of the variable in the BMS's memory associated with this control")]
    public string BmsParamName
    {
        get { return paramConfig.BmsParamName; }
        set { paramConfig.BmsParamName = value; }
    }

    // Conversion factor
    [DefaultValueAttribute(1),
     Description("To convert the BMS value to the value shown in this setting, divide by this factor"),
     Category("_Vinci")]
    public float ConversionFactor
    {
        get { return paramConfig.ConversionFactor; }
        set { paramConfig.ConversionFactor = value; }
    }
}
诚然,这两个属性似乎有点太简单,无法卸载到helper类中,但您将把它们都放在那里,也包括更复杂的属性,因此总体而言,您将实现最佳的代码重用

此解决方案的缺点是,您仍然必须在所有控件中实现整个接口。该功能在您的helper类中,但是您仍然需要多次进行实现的准备工作。您可以通过创建一个更易于实现的提供程序接口来简化任务(这次是完整的接口,没有简化的示例):


您仍然可以在可视化设计器中导航
IParamConfig
的属性(还记得我保留了一些设计器属性吗?-现在您知道原因了:您仍然可以在编辑器中看到属性)。这是在控制级别实现此功能的最简单方法,但是需要更改访问
IParamConfig
的代码,才能通过
IParamConfigProvider
。最后,您可以选择更好的服务。

最好能看到界面。否则回复就会模糊不清。为什么你不能直接访问文本框?你把它保密了吗?如果您将其添加为带有受保护访问修饰符的属性,则子类应该能够直接使用它。@Sefe我添加了用户界面的屏幕截图I的意思是
IParamConfig
@chadnt类ParamConfigTextBox继承自UserControl,而非TextBox,因此它无权访问TextBox的属性。它包含一个文本框,但它不是一个文本框的孩子。哇!好的,我差不多明白了。我需要一段时间才能完全消化。我知道你要去,我很喜欢。玩过之后我会再打给你的,谢谢!太遗憾了,你没有使用XAML,你可以只使用附加属性。@Michael Puckett II:谢谢你让我意识到这一点。