Délégation des responsabilités aux contrôles WinForm - les contrôles doivent-ils être conscients des actions de chacun?

J'ai une poignée de contrôles sur un formulaire:

  • Case à cocher permettant d'activer/de désactiver tous les autres contrôles de la page en fonction de son état vérifié.
  • Quelques boutons radio qui permettent d'activer/désactiver des contrôles spécifiques sur la page en fonction de leurs états vérifiés.
  • Autres contrôles manipulés par les contrôles ci-dessus.

Quelques scénarios se présentent:

  • Lorsque le formulaire s'initialise, je charge l'état de la case à cocher. Ensuite, il active ou désactive le reste des contrôles sur le formulaire.
  • Pendant que le formulaire continue à s'initialiser, je charge l'état des boutons radio. Cela a la possibilité de défaire l'exigence précédente si un bouton radio est coché mais désactivé. En tant que tel, je vérifie pour s'assurer que le bouton radio est activé en premier.
  • Une fois le formulaire chargé, l'utilisateur peut cocher ou décocher les cases d'option. Ceci est un cas trivial, je viens d'exécuter le code qui a rempli la dernière exigence. Un autre cas, cependant, est que l'utilisateur peut cocher/décocher la case. Lorsque la case à cocher est activée, elle souhaite réactiver tous les contrôles de la page, car elle les a désactivés. Cela va briser les exigences du bouton radio, cependant.

Ce scénario est assez trivial à gérer avec une force brute. J'ai créé quelques méthodes pour mettre en évidence:

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

    foreach (Control control in grpBxSNMPv3.Controls)
    {
        if (control != sender)
            control.Enabled = ((CheckBox)sender).Checked;
    }
}

private void rdBtnAuthNoPriv_CheckedChanged(object sender, EventArgs e)
{
    RadioButton authNoPrivRadioButton = ((RadioButton)sender);

    if (authNoPrivRadioButton.Enabled)
    {
        bool isChecked = authNoPrivRadioButton.Checked;

        SetControlState(cmbBxAuthProtocol, isChecked);
        SetControlState(mskdTxtBxAuthPassword, isChecked);
        SetControlState(mskdTxtBxAuthPasswordConfirm, isChecked);

        SetControlState(cmbBxPrivacyProtocol, !isChecked);
        SetControlState(mskdTxtBxPrivacyPassword, !isChecked);
        SetControlState(mskdTxtBxPrivacyPasswordConfirm, !isChecked);
    }
}
//More methods for other checkedChange and also for when rdBtn's enable.

Idée approximative de la mise en page:

enter image description here

Avec tout cela, ma question est "simple":

  • Les méthodes devraient fonctionner sans l'hypothèse que d'autres méthodes existent. Pourtant, si je garde la logique de l'existence de chkBx de rdBtn, alors je vais avoir un code qui doit se battre les uns les autres.

Je pourrais écrire mon code comme ceci:

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

    txtBxEngineID = ((CheckBox)sender).Checked;
    rdBtnAuthNoPriv = ((CheckBox)sender).Checked;
    rdBtnAuthPriv = ((CheckBox)sender).Checked;
    rdBtnNoAuthNoPriv = ((CheckBox)sender).Checked;

    //Pass work for enabling Auth and Priv fields to rdBtn events.
}

Cette solution est plus efficace et garantit que je ne verrai aucun scintillement. Pourtant, cela signifie aussi que pour une 'réussite' d'activer tous les contrôles sur la page, mon chkBx doit maintenant s'appuyer sur la logique de rdBtn. Est-ce une bonne pratique de programmation?

3
Hey désolé. Je suis d'accord - mon message initial n'a pas assez de sens. Je vais le modifier aujourd'hui quand j'ai un peu de temps pour le rendre plus compréhensible à des fins héritées. Je suis confus par votre deuxième point, cependant. Si les événements ne sont jamais utilisés si une seule classe est impliquée, alors? Avez-vous lu pour moi de sauvegarder cela? Le code est sur un contrôle utilisateur qui est chargé sur un formulaire.
ajouté l'auteur Sean Anderson, source
La question n'a pas beaucoup de sens, ce sont déjà des extraits de code qui existent dans la forme . Ou UserControl, ne peut pas dire. Pas de code à l'intérieur de la classe de contrôle. Quels sont les événements, en laissant un autre code savoir que quelque chose d'intéressant s'est passé? Ce code appartient vraiment au formulaire, la seule classe qui sait quelque chose sur la collection de contrôles qu'il héberge.
ajouté l'auteur Hans Passant, source

2 Réponses

Je pense que c'est un code sensible à garder sous la forme, cependant je ferais quelques suggestions;

1) Le casting a un coût de traitement mineur donc vous devriez éviter de lancer à l'intérieur d'une boucle. En fait, en tant que concept général, vous devriez éviter d'effectuer une action répétée dans une boucle lorsque le résultat est garanti pour rester le même. Donc vous pourriez améliorer votre première méthode comme ça;

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

   //cast the sender once only
    CheckBox cb = sender as CheckBox;
    if (null == cb) return;

    foreach (Control control in grpBxSNMPv3.Controls)
    {
        if (control != sender)
            control.Enabled = cb.Checked;
    }
}

2) Je suggère de déplacer la logique d'activation/désactivation dans une méthode séparée, puis de l'appeler à partir de vos gestionnaires d'événements de contrôle. Cela vous permettra de réutiliser la même logique si vous décidez de, à partir d'un autre contrôle. Comportements de couplage serrés pour contrôler les événements, je trouve conduit à un code dupliqué. Ainsi;

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

   //cast the sender once only
    CheckBox cb = sender as CheckBox;
    if (null == cb) return;

    SetEnabled(grpBxSNMPv3, cb.Checked, new[] { cb });
}

private void SetEnabled(Control parent, bool isEnabled, Control[] exludeControls)
{
    if (null == parent) return;

    foreach (Control control in parent.Controls)
    {
        if (!excludeControls.Contains(control))
            control.Enabled = isEnabled;
    }
}

Vous avez maintenant une méthode réutilisable pour activer/désactiver tous les contrôles contenus par un autre.

3) En ce qui concerne votre dernière question, oui je pense que cette approche est bonne. Moins de couplage est toujours une bonne chose. Réfléchissez à la façon de concevoir vos méthodes pour qu'elles soient plus réutilisables, et je pense que vous allez trouver une solution propre.

1
ajouté
+1 pour les suggestions. En fait, j'ai fini par suivre toutes ces suggestions avant de mettre à jour mon fil - de sorte que les grands esprits se ressemblent. Je suis allé de l'avant et posté une solution propre pour les personnes futures. Faites-moi savoir si vous voyez quelque chose de pauvre à ce sujet. :)
ajouté l'auteur Sean Anderson, source

C'est ce que j'ai fini par faire avec. Je suis pas mal avec ça à part les deux listes que j'initialise. Ils devraient probablement être dans leurs propres contrôles, mais je ne peux pas me résoudre à le faire tout à fait encore.

public partial class DeviceSnmpSettings : UserControl, INotifyPropertyChanged
{
    private readonly List AuthenticationControls = new List(6);
    private readonly List PrivacyControls = new List(6);
    public event PropertyChangedEventHandler PropertyChanged;

    public DeviceSnmpSettings()
    {
        InitializeComponent();
        InitializeAuthControls();
        InitializePrivacyControls();
    }

    public DeviceSnmpSettings(Point location)
        : this()
    {
        Location = location;
    }

    //TODO: Move out into sub-user control?
    private void InitializeAuthControls()
    {
        AuthenticationControls.Add(lblAuthPassword);
        AuthenticationControls.Add(mskdTxtBxAuthPassword);
        AuthenticationControls.Add(lblAuthProtocol);
        AuthenticationControls.Add(cmbBxAuthProtocol);
        AuthenticationControls.Add(lblAuthPasswordConfirm);
        AuthenticationControls.Add(mskdTxtBxAuthPasswordConfirm);
    }
    //TODO: Move out into sub-user control?
    private void InitializePrivacyControls()
    {
        PrivacyControls.Add(lblPrivacyPassword);
        PrivacyControls.Add(mskdTxtBxPrivacyPassword);
        PrivacyControls.Add(lblPrivacyProtocol);
        PrivacyControls.Add(cmbBxPrivacyProtocol);
        PrivacyControls.Add(lblPrivacyPasswordConfirm);
        PrivacyControls.Add(mskdTxtBxPrivacyPasswordConfirm);
    }

    private bool SNMPv3Enabled
    {
        get { return chkBxSNMPv3.Checked; }
        set { chkBxSNMPv3.Checked = value; }
    }

    private SNMPV3Mode SecurityMode
    {
        get
        {
            SNMPV3Mode mode = SNMPV3Mode.NoAuthNoPriv;

            if (rdBtnAuthNoPriv.Checked)
                mode = SNMPV3Mode.AuthNoPriv;
            else if(rdBtnAuthPriv.Checked)
                mode = SNMPV3Mode.AuthPriv;

            return mode;
        }
        set
        {
            switch (value)
            {
                case SNMPV3Mode.NoAuthNoPriv:
                    rdBtnNoAuthNoPriv.Checked = true;
                    break;
                case SNMPV3Mode.AuthNoPriv:
                    rdBtnAuthNoPriv.Checked = true;
                    break;
                default:
                    rdBtnAuthPriv.Checked = true;
                    break;
            }

            OnSecurityModeChanged();
        }
    }

    protected virtual void OnSecurityModeChanged()
    {
        AuthenticationControls.ForEach(control => SetControlEnabledState(control, AuthenticationEnabled));
        PrivacyControls.ForEach(control => SetControlEnabledState(control, PrivacyEnabled));
        NotifyPropertyChanged("SecurityMode");
    }

    private void NotifyPropertyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

    private bool AuthenticationEnabled
    {
        get
        {
            return SNMPv3Enabled && (SecurityMode == SNMPV3Mode.AuthPriv || SecurityMode == SNMPV3Mode.AuthNoPriv);
        }
    }

    private bool PrivacyEnabled
    {
        get { return SNMPv3Enabled && SecurityMode == SNMPV3Mode.AuthPriv; }
    }

    private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
    {
        SetControlEnabledStates();
    }

    private void SetControlEnabledStates()
    {
        snmpSettingsErrorProvider.Clear();

        foreach (Control control in grpBxSNMPv3.Controls)
        {
            //Check each of the lists for the control to prevent flickering.
            if (control != chkBxSNMPv3 && !AuthenticationControls.Contains(control) && !PrivacyControls.Contains(control))
                control.Enabled = SNMPv3Enabled;
        }

        //Need to validate that our radio button's checked state is reflected properly.
        AuthenticationControls.ForEach(control => SetControlEnabledState(control, AuthenticationEnabled));
        PrivacyControls.ForEach(control => SetControlEnabledState(control, PrivacyEnabled));
    }

    public void LoadFields(NetworkDiscovery networkDiscovery)
    {
        SNMPv3Enabled = networkDiscovery.Snmpv3Enabled;
        SecurityMode = networkDiscovery.SecurityMode;
        txtBxSNMPv3Username.Text = networkDiscovery.Username;
        mskdTxtBxAuthPassword.Text = networkDiscovery.AuthPassword;
        mskdTxtBxAuthPasswordConfirm.Text = networkDiscovery.AuthPassword;
        cmbBxAuthProtocol.SelectedItem = networkDiscovery.AuthProtocol.ToString();
        mskdTxtBxPrivacyPassword.Text = networkDiscovery.PrivacyPassword;
        mskdTxtBxPrivacyPasswordConfirm.Text = networkDiscovery.PrivacyPassword;
        cmbBxPrivacyProtocol.SelectedItem = networkDiscovery.PrivacyProtocol.ToString();

        SetControlEnabledStates();
    }

    private void SetControlEnabledState(Control control, bool enabled)
    {
        control.Enabled = enabled;
                    //Clear errors set on errorProvider when control is disabled.
        if (!control.Enabled)
            snmpSettingsErrorProvider.SetError(control, string.Empty);
    }

    private void rdBtnNoAuthNoPriv_CheckedChanged(object sender, EventArgs e)
    {
        if (((RadioButton)sender).Checked)
            SecurityMode = SNMPV3Mode.NoAuthNoPriv;
    }

    private void rdBtnAuthNoPriv_CheckedChanged(object sender, EventArgs e)
    {
        if (((RadioButton)sender).Checked)
            SecurityMode = SNMPV3Mode.AuthNoPriv;
    }

    private void rdBtnAuthPriv_CheckedChanged(object sender, EventArgs e)
    {
        if (((RadioButton)sender).Checked)
            SecurityMode = SNMPV3Mode.AuthPriv;
    }
}
0
ajouté