繁体   English   中英

试图弄清楚如何根据组合框选择动态更改用于 label 的数组?

[英]Trying to figure out how to dynamically change which array is used for a label based on combo box selection?

所以不幸的是,我现在有点超级菜鸟哈哈所以如果可能的话请多多包涵。

我试图制作的程序是一个钢琴音符随机发生器,用于有效地记忆音符/音阶。 我认为这将是一个有趣的小程序,可以帮助我的钢琴之旅和编程之旅哈哈。

节目形象

所以在这个程序中,我放了一个组合框,每个钢琴音阶都有一个选项,如下所示:

组合框的图片

然后我继续为每个音阶和该音阶的音符制作一个数组,如下所示:

 //Arrays to store each scale
            string[] cMajor = { "C", "D", "E", "F", "G", "A", "B", "C", };
            string[] dMajor = { "D", "E", "F#", "G", "A", "B", "C#", "D", };
            string[] eMajor = { "E", "F#", "G#", "A", "B", "C#", "D#", "E", };
            string[] fMajor = { "F", "G", "A", "Bb", "C", "D", "E", "F", };
            string[] gMajor = { "G", "A", "B", "C", "D", "E", "F#", "G", };

然后我使用一个随机数生成器作为 arrays 的索引,这样它会随机 select 一个介于 0 和 7 之间的数字,理想情况下 select 是一个随机数组索引,然后我们可以将其传递到 label 这将是钢琴音符显示在屏幕上。

//Number Generator to pass into the newNumber variable which will act as an index for the array
            Random generator = new Random();
            int newNumber  = generator.Next(0, 7); // Generating a number that equates to the number of notes in a particular scale

然后我输入代码来检查所选输入的 combobox,然后根据所选的比例创建 label:

 if (comboBox1.SelectedIndex == 0)
            {
                OUTPUTLABEL1.Font = new Font("Segoe UI", 100);
                OUTPUTLABEL1.Location = new Point(80, 5);
                OUTPUTLABEL1.Text = cMajor[newNumber];

            }

在大多数情况下,一切都按预期工作。 我可以在屏幕上弹出 select 比例和注释,问题是其中没有一个是动态的,哈哈。 如您所见,比例选择被硬编码到程序中。 cMajor 的规模也是硬编码的。

我的想法是,我可以从 ComboBox 中选择一个比例尺 select,并且文本会根据在 ComboBox 中选择的比例尺动态变化。我能想到的唯一实现这一点的方法会产生大量多余的 if 语句,我觉得就像我可能有更好的方法来完成这个。

非常感谢你们,如果这是显而易见的或我可能犯的任何其他错误,我再次道歉。 这是我在这里的第一篇文章,我希望我已经为你们提供了足够的信息! 再次感谢你!

完整代码(忽略计时器):

        public void RandomizeButton_Click(object sender, EventArgs e)
        {

            //Arrays to store each scale
            string[] cMajor = { "C", "D", "E", "F", "G", "A", "B", "C", };
            string[] dMajor = { "D", "E", "F#", "G", "A", "B", "C#", "D", };
            string[] eMajor = { "E", "F#", "G#", "A", "B", "C#", "D#", "E", };
            string[] fMajor = { "F", "G", "A", "Bb", "C", "D", "E", "F", };
            string[] gMajor = { "G", "A", "B", "C", "D", "E", "F#", "G", };

            //Number Generator to pass into the newNumber variable which will act as an index for the array
            Random generator = new Random();
            int newNumber  = generator.Next(0, 7); // Generating a number that equates to the number of notes in a particular scale

            if (comboBox1.SelectedIndex == 0)
            {
                OUTPUTLABEL1.Font = new Font("Segoe UI", 100);
                OUTPUTLABEL1.Location = new Point(80, 5);
                OUTPUTLABEL1.Text = cMajor[newNumber];

            }

            if (TimerCheckbox.Checked)
            {
                seconds = int.Parse(timeInterval.Text);
                timer1.Start();
            }         
        }

如果我对 arrays 和组合框的思考方式不太理想,那么如果需要,我愿意接受一个全新的解决方案! 非常感谢你们!

看起来你想要一个链接的数据结构。 在一个变量和另一个变量之间设置 map 的最常见方法是使用字典

//you can use a dictionary
public Dictionary<string, string[]> scalesDict = new Dictionary<string, string[]>() {
        {"a", new string[]   { "C", "D", "E", "F", "G", "A", "B", "C", } },
        {"b", new string[]  { "D", "E", "F#", "G", "A", "B", "C#", "D", } },
        {"c", new string[]  { "E", "F#", "G#", "A", "B", "C#", "D#", "E", } },
        {"d", new string[]  { "F", "G", "A", "Bb", "C", "D", "E", "F", } },
        {"e", new string[]  { "G", "A", "B", "C", "D", "E", "F#", "G", } } };
private string[] GetScale(string comboBoxValue) {
    return scalesDict[comboBoxValue];
}

上面的代码将根据 combobox 值为您提供数组(将 a、b、c、d 和 e 替换为 combobox 使用的任何值)

字典是非常强大的工具 c#

不过你的问题有点含糊,所以我不确定我是否已经完全解决了你的问题,请告诉我

您的问题和代码涵盖了几个相关领域:

  • 根据组合框选择,动态更改用于 label 的(音符)数组。
  • 正确使用Random class实现Randomize function。
  • 使用计时器自动执行“随机化”按钮。

好吧,你确实说过如果需要的话我愿意接受一个全新的解决方案! 所以这个答案探讨关于这三件事的其他方法 go 并为您提供一些新技能来尝试。 特别是,如果您尽早了解如何使用数据绑定(如本示例所示),它可能会加速您在 WinForms 中所做的其他所有事情。

截屏


将秤绑定到 ComboBox

一种方法是定义一个class来表示一个 Scale。 这比字典查找更紧密地关联信息。 ToString方法将确定组合框中显示的内容。

enum ScaleForm { Major, Minor }
enum Key { A, B, C, D, E, F, G }
enum Signature { Natural, [Description("\u266F")] Sharp, [Description("\u266D")] Flat }
class Scale
{
    public Key Key{ get; set; }
    public ScaleForm Form { get; set; }
    public Signature Signature { get; set; }
    public string[] Notes { get; set; } = new string[0];
    // Determines what displays in the ComboBox
    public override string ToString()=> $"{Key}{Signature.ToUnicode()} {Form}";
}
static class Extensions
{
    public static string ToUnicode(this Signature signature)
    {
        switch (signature)
        {
            default: return string.Empty;
            case Signature.Sharp: return "\u266F";
            case Signature.Flat: return "\u266D";
        }
    }
}

接下来制作一个Scale对象列表,它将成为组合框的动态源:

BindingList<Scale> Scales = new BindingList<Scale>();

在加载主窗体的方法中初始化各个比例尺比例尺列表。

public partial class MainForm : Form
{
    public MainForm() => InitializeComponent();

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Scales.Add(new Scale
        {
            Key = Key.C,
            Notes = new[] { "C", "D", "E", "F", "G", "A", "B", "C", },
        });            
        Scales.Add(new Scale
        {
            Key = Key.D,
            Notes = new[] 
            { 
                "D", "E", 
                $"F{Signature.Sharp.ToUnicode()}", 
                "G", "A", "B", 
                $"C{Signature.Sharp.ToUnicode()}", 
                "D", 
            },
        });
        Scales.Add(new Scale
        {
            Key = Key.E,
            Notes = new[] 
            { 
                "E", 
                $"F{Signature.Sharp.ToUnicode()}", 
                $"G{Signature.Sharp.ToUnicode()}", 
                "A", "B", 
                $"C{Signature.Sharp.ToUnicode()}", 
                $"D{Signature.Sharp.ToUnicode()}", 
                "E", 
            },
        });
        Scales.Add(new Scale
        {
            Key = Key.F,
            Notes = new[] 
            { 
                "F", "G", "A", 
                $"B{Signature.Flat.ToUnicode()}", 
                "C", "D", "E", "F",
            },
        });
        Scales.Add(new Scale
        {
            Key = Key.G,
            Notes = new[] 
            { 
                "G", "A", "B", "C", "D", "E", 
                $"F{Signature.Sharp.ToUnicode()}", 
                "G", 
            },
        });
        Scales.Add(new Scale
        {
            Key = Key.B, Signature = Signature.Flat, 
            Notes = new[] { 
                $"B{Signature.Flat.ToUnicode()}", 
                "C", "D", 
                $"E{Signature.Flat.ToUnicode()}", 
                "F", "G", "A",
                $"B{Signature.Flat.ToUnicode()}"},
        });
        comboBoxScales.TabStop= false;
        comboBoxScales.DropDownStyle= ComboBoxStyle.DropDownList;
        // Attach the list of scales
        comboBoxScales.DataSource= Scales;
        // Initialize the value
        onScaleSelectionChanged(this, EventArgs.Empty);
        // Respond to combo box changes
        comboBoxScales.SelectedIndexChanged += onScaleSelectionChanged;
        // Respond to click randomize
        buttonRandomize.Click += onClickRandomize;
        // Respond to automated timer checkbox changes
        checkBoxTimer.CheckedChanged += onTimerCheckedChanged;
    }
    .
    .
    .
}

同样在同一方法中,附加了事件处理程序。 例如,当在组合框中选择新比例时,首先发生的事情是将 label 设置为根。

private void onScaleSelectionChanged(object? sender, EventArgs e)
{
    labelCurrentNote.Text =
        ((Scale)comboBoxScales.SelectedItem).Notes[0];
}

使用Random class

您只需要一个Random实例。 为了进行测试,您每次都可以生成相同的伪随机数字序列,方法是用一个 int 作为种子,例如new Random(1) 但如此处所示,每次运行时,种子都是从系统时钟派生的,用于不同的序列。

private readonly Random _rando = new Random();
private void onClickRandomize(object? sender, EventArgs e) =>
    execNextRandom(sender, e);

一种实现方式是获取介于 0 和 7 之间的数字,并使用它从组合框中的当前选择中取消对数组值的引用。 这也使得在每次点击时得到一个的注释很重要,这样用户在点击时就会感到自信。

private void execNextRandom(object? sender, EventArgs e)
{
    string preview;
    do
    {
        // Randomize, but do not repeat because it makes
        // it seem like the button doesn't work!
        preview =
            ((Scale)comboBoxScales.SelectedItem)
            .Notes[_rando.Next(0, 8)];  // Will never return 8!
    } while (preview.Equals(labelCurrentNote.Text));
    labelCurrentNote.Text = preview;
}

自动定时器

获得重复 function 的一种更简单的方法是为计时器复选框创建一个异步处理程序。 无需启动和停止计时器或以这种方式处理事件,但它仍然提供了通过“不”阻塞线程来保持 UI 响应的优势,除非获取了新的随机笔记。

private async void onTimerCheckedChanged(object? sender, EventArgs e)
{
    while(checkBoxTimer.Checked) 
    {
        execNextRandom(sender, e);
        await Task.Delay(TimeSpan.FromSeconds((double)numericUpDownSeconds.Value));
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM