我如何引用父容器和子容器并对其进行操作

How do I reference and act on parent and child containers

说出您对 JavaScript 的看法,但导航层次结构轻而易举。也许 C# 也是,但我不知道如何。我有一个包含一系列控件的程序,其中控件一遍又一遍地重复:

所有按钮的操作都相同,所以我重复了很多代码,但效率似乎很低:

        // There's a function just like this for EVERY button
        private void FixedF1(bool isFixed)
        {
            if (isFixed)
            {
                F1FixerBtnDot.ForeColor = Color.FromArgb(80, 214, 0);
                F1FixerBtnDot.TextAlign = ContentAlignment.TopLeft;
                F1FixerBtnArea.BackgroundImage = Properties.Resources.green_btn_bk_sm;
                F1FixerBtnBox.Text = "Fixed!";
                PrefMatch("KillF1Help", "yes", F1FixerBtnDot);
            }
            else
            {
                F1FixerBtnDot.ForeColor = Color.FromArgb(244, 180, 0);
                F1FixerBtnDot.TextAlign = ContentAlignment.TopRight;
                F1FixerBtnArea.BackgroundImage = Properties.Resources.yellow_btn_bk_sm;
                F1FixerBtnBox.Text = "Fix it!";
                PrefMatch("KillF1Help", "no", F1FixerBtnDot);
            }

        }

        private void F1FixerBtnDot_Click(object sender, EventArgs e)
        {
            if (regStuff.F1HelpActive())
            {
                // Save the new setting
                prefs.SetPref("KillF1Help", "no");
                // Toggle it
                regStuff.RestoreF1();
                FixedF1(false);
            }
            else
            {
                prefs.SetPref("KillF1Help", "yes");
                regStuff.KillF1();
                FixedF1(true);
            }
        }

在第一个函数中,我有将按钮“打开”和“关闭”的代码。这些按钮中的每一个基本上都重复该操作,我可能有 30 个或更多。我正在尝试像这样重新编码(基于我在 JS 中的做法):

        private void ToggleFixedButton(GroupBox which, bool isFixed, string prefName)
        {
            which.child(islabel).ForeColor = isFixed ? Color.FromArgb(80, 214, 0) : Color.FromArgb(244, 180, 0);
            which.child(islabel).TextAlign = isFixed ? ContentAlignment.TopLeft : ContentAlignment.TopRight;
            which.child(isPanel).BackgroundImage = isFixed ? Properties.Resources.green_btn_bk_sm : Properties.Resources.yellow_btn_bk_sm;
            which.Text = isFixed ? "Fixed!" : "Fix it!";
            PrefMatch("KillF1Help", isFixed ? "yes" : "no", which.child(islabel));
        }

        private void F1FixerBtnDot_Click(object sender, EventArgs e)
        {
            if (regStuff.F1HelpActive())
            {
                prefs.SetPref("KillF1Help", "no");
                regStuff.RestoreF1();
                ToggleFixedButton(sender.Parent, false, "KillF1Help");
            }
            else
            {
                prefs.SetPref("KillF1Help", "yes");
                regStuff.KillF1();
                ToggleFixedButton(sender.Parent, true, "KillF1Help");
            }
        }

最重要的是,当我应该能够用 ONE 做到这一点时,假设我能弄清楚如何向它发送它需要的信息,那么拥有一个单独的函数来为 30 个控件做同样的事情似乎并不明智访问涉及的表单元素。

我假设您只使用按钮和面板。

查看控件设计器的“属性”选项卡并注意标记 属性。您可以使用 F1、F2、F3 等键填充它们。对按钮和面板执行此操作以将它们关联起来。

然后用一个方法处理Buttons的所有Click事件,并使用类似这样的代码获取事件的关联面板;

var panelOfInterest = Parent.Controls.OfType<Panel>().Single(c => c.Tag == sender.Tag);

如果关联的控件不共享直接父级,那么您将需要指定一些其他控件进行搜索。

典型的 c# 方法是 create a custom control 用于您的自定义按钮。

这样您就可以在自定义控件中放置任何自定义逻辑来更改颜色和其他属性。

可能可以使用 is operator, .Controls, and .OfType() 的组合来遍历控件树并执行您想要的操作。但我不会推荐它,因为它会使您的按钮更难在其他控件中重用。

当您单击 FixIt 按钮时,它的变化方式是表单的 属性,还是按钮的 属性?如果您决定所有的 FixIt 按钮都应该变成蓝色,这是否意味着您的表单上的代码应该更改?

我的建议是创建一个 class FixitButton:

class FixitButton : UserControl
{
    ...
}

您可以从 Button 派生 FixitButton,但是您确定要向 FixitButton 的用户公开 Button 的所有属性吗?这样,FixitButtons 的用户可以改变行为,这样一个 FixitButton 的行为就会与另一个不同,这可能不是你想要的。

在FixitButton class中,您可以编写点击事件的事件处理程序,让它们来改变布局。同时创建一个事件来通知 FixitButton 的用户它被点击了,或者它可能改变了状态:

public bool IsSwitchedOn {get; set;}

public event EventHandler IsSwitchedOnChanged;

protected virtual void OnIsSwitchedOnChanged()
{
    this.IsSwitchedOnChanged?.Invoke(this, EventArgs.Empty);
}

在您处理点击事件的方法中:

... // do the changing of the layout, colours, etc
this.IsSwitchedOn = ...
this.OnIsSwitchedOnChanged();

考虑让 属性 OnIsSwitchedOn 调用 Changed 方法:

private bool isSwitchedOn = false;

public bool IsSwitchedOn
{
    get => this.isSwitchedOn;
    set => if (this.IsSwitchedOn != value)
    {
        this.isSwitchedOn = value;
        this.IsSwitchedOnChanged();
    }
}

OnIsSwitchedOnChanged 被定义为受保护的虚拟,因此派生的 classes 可以根据需要决定做一些额外的事情。

您的表单将为它感兴趣的所有 FixitButton 订阅 IsSwitchedOnChanged 事件。它们都可以使用相同的事件处理程序:

IEnumerable<FixitButton> fixitButtonsToReactOn = ...
foreach (FixitButton button in fixitButtonsToReactOn)
{
    button.IsSwitchedOnChanged = this.FixitButtonStateChanged;
}

和事件处理程序:

private void FixitButtonStateChanged(object sender, EventArgs e)
{
    FixitButton changedButton = (FixitButton)sender;
    bool state = changedButton.IsSwitchedOn;

    this.ProcessChangedButtonState(changedButton, state);
}

因此您的表单对着色、背景等一无所知。这是 FixitButton class 的任务。如果您更改 class,您应用程序中所有表单上的所有 FixitButton 都会改变行为。

ProcessChangedButtonState 所要做的就是确定哪个按钮已更改状态,并采取相应的行动。