【问题标题】:Datagrid from list crashes when item selected选择项目时列表中的数据网格崩溃
【发布时间】:2021-02-08 03:43:37
【问题描述】:

BindingList 是正确的答案,它解决了所有问题,包括能够格式化列。这是我必须更改的唯一行,现在一切正常。

//This went at the top:
BindingList<Ingredient> selectedIngredients = new BindingList<Ingredient>();

//This went in the page Load method:
dgvRecipeIngredients.DataSource = new BindingSource() { DataSource = selectedIngredients};

//These three went as appropriate in methods that added or removed items from the list. 
//The ResetBindings removed items from the list, which wasn’t happening before without resetting the page. 

selectedIngredients.Add(ingr);
selectedIngredients.RemoveAt(idxSelectedIngr);
selectedIngredients.ResetBindings();

我已将其他所有内容保持原样,因此人们可以看到问题,以及这是如何解决问题的。

Picture of UI with some data in second grid (bottom) and an ingredient ready to be added 我有一个网格视图,我正在将我的成分列表放入其中,它工作得很好。 我有第二个 gridvew,我希望能够从第一个 gridview 中添加成分。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HealthierRecipes
{
    public partial class AddRecipe : Form
    {
        public static int ingrId = 0;
        public static int availIngrId = -1;
        private static int idxSelectedIngr = 0;
        private static int idxSelectedAvailIngr = -1;
        private List<Ingredient> selectedIngredients = new List<Ingredient>();
        
        public AddRecipe()
        {
            InitializeComponent();
        }

        private void AddRecipe_Load(object sender, EventArgs e)
        {
            
            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {
                dgvAvailIngredients.DataSource = dbContext.Ingredients.OfType<Ingredient>().ToList();
                this.dgvRecipeIngredients.DataSource = selectedIngredients;
                   
                formatPage();
                txtTotCals.Text = "0";
                txtTotCarbs.Text = "0";
                txtTotProtein.Text = "0";
                txtTotFat.Text = "0";   
            }    
        }

        private void dgvRecipeIngredients_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {
                idxSelectedIngr = e.RowIndex;
                ingrId = Convert.ToInt32(dgvRecipeIngredients.Rows[e.RowIndex].Cells[0].Value);

                //var ingr = (from r in dbContext.Ingredients where r.IngredientId == ingrId select r).First();
                //txtAddIngredient.Text = ingr.Name;
                //txtAddUnit.Text = ingr.Units;
                //txtAddAmount.Text = ingr.Amount.ToString();
            } 
        }

        private void dgvAvailIngredients_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {
                idxSelectedAvailIngr = e.RowIndex;
                availIngrId = Convert.ToInt32(dgvAvailIngredients.Rows[e.RowIndex].Cells[0].Value);

                var ingr = (from r in dbContext.Ingredients where r.IngredientId == availIngrId select r).First();
                txtAddIngredient.Text = ingr.Name;
                txtAddUnit.Text = ingr.Units;
                txtAddAmount.Text = ingr.Amount.ToString();
            }    
        }
        private void bnAddIngredient_Click(object sender, EventArgs e)
        {
            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {
                var ingr = (from r in dbContext.Ingredients where r.IngredientId == availIngrId select r).First();

                if (availIngrId > 0)
                {
                    float amountNumber = 0;
                    bool amountValid = float.TryParse(txtAddAmount.Text, out amountNumber);
                    if (amountValid == false)
                    {
                        MessageBox.Show("Please only use numbers in the amount box.");
                        return;
                    }

                    ingr.Amount = amountNumber;
                    selectedIngredients.Add(ingr);
                    
                    txtAddIngredient.Text = "";
                    txtAddUnit.Text = "";
                    txtAddAmount.Text = "";

                    dgvRecipeIngredients.DataSource = null;
                    dgvRecipeIngredients.DataSource = selectedIngredients;


                    //TODO This is where the code to multiply the amount happens, then add the ingredient to the recipe. 
                    //Includes updating recipe nutrients.

                    float ingrcals = ingr.Calories * ingr.Amount;
                    float ingrcarbs = ingr.Calories * ingr.Amount;
                    float ingrpro = ingr.Protein * ingr.Amount;
                    float ingrfat = ingr.Fat * ingr.Amount;

                    float totcals = float.Parse(txtTotCals.Text);
                    float totcarbs = float.Parse(txtTotCarbs.Text);
                    float totpro = float.Parse(txtTotProtein.Text);
                    float totfat = float.Parse(txtTotFat.Text);

                    txtTotCals.Text = (totcals + ingrcals).ToString();
                    txtTotCarbs.Text = (totcarbs + ingrcarbs).ToString();
                    txtTotProtein.Text = (totpro + ingrpro).ToString();
                    txtTotFat.Text = (totfat + ingrfat).ToString();

                }
                else { MessageBox.Show("Please select an ingredient to add."); }

            }
        }


        private void bnSaveRecipe_Click_1(object sender, EventArgs e)
        {
            //TODO save recipe
            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {

                Recipe r = new Recipe();

                if (ValidateForm())
                {
                    r.Name = txtRecName.Text;
                    r.Dish = cbDish.SelectedItem.ToString();
                    r.Servings = Int32.Parse(txtServings.Text);
                    r.TotalCalories = Int32.Parse(txtTotCals.Text);
                    r.TotalFat = Int32.Parse(txtTotFat.Text);
                    r.TotalCarbs = Int32.Parse(txtTotCarbs.Text);
                    r.TotalProtein = Int32.Parse(txtTotProtein.Text);
                    r.Instructions = rtxtInstructions.Text;

                    dbContext.Recipes.InsertOnSubmit(r);
                    dbContext.SubmitChanges();
                    MessageBox.Show("Record is saved", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);

                    int id = r.RecipeId;

                    // r.IngredientList = selectedIngredients;

                    IngredientList il = new IngredientList();
                    foreach (Ingredient ingr in selectedIngredients)
                    {
                        il.RecipeId = id;
                        il.IngredientId = ingr.IngredientId;
                        il.IngredientAmount = ingr.Amount;
                    }


                    txtRecName.Text = "";
                    txtServings.Text = "";
                    txtTotCals.Text = "0";
                    txtTotFat.Text = "0";
                    txtTotCarbs.Text = "0";
                    txtTotProtein.Text = "0";
                    rtxtInstructions.Text = "";
                    dgvRecipeIngredients.DataSource = null;
                    dgvRecipeIngredients.DataSource = selectedIngredients;
                }
                else
                {
                    MessageBox.Show("Please fill in all boxes on form with valid values.");
                }
            }


        }

        private bool ValidateForm()
        {
            bool output = true;

            if (txtRecName.Text.Length == 0)
            {
                output = false;
            }

            if (cbDish.SelectedItem == null)
            {
                output = false;
            }


            int servingsNumber = 0;
            bool servingsValid = int.TryParse(txtServings.Text, out servingsNumber);
            if (servingsValid == false)
            {
                output = false;
            }

            int caloriesNumber = 0;
            bool caloriesValid = int.TryParse(txtTotCals.Text, out caloriesNumber);
            if (caloriesValid == false)
            {
                output = false;
            }

            int fatNumber = 0;
            bool fatValid = int.TryParse(txtTotFat.Text, out fatNumber);
            if (fatValid == false)
            {
                output = false;
            }

            int carbsNumber = 0;
            bool carbsValid = int.TryParse(txtTotCarbs.Text, out carbsNumber);
            if (carbsValid == false)
            {
                output = false;
            }

            int proteinNumber = 0;
            bool proteinValid = int.TryParse(txtTotProtein.Text, out proteinNumber);
            if (proteinValid == false)
            {
                output = false;
            }

            return output;
        }

        private void btnAddCancel_Click(object sender, EventArgs e)
        {
            //TODO Change from exit to cancel
            Application.Exit();
        }

        private void bnDelIngr_Click(object sender, EventArgs e)
        {
            //TODO - Add code to remove Nutrition info for removed ingredient - this is done, but it doesn't reomove the item from the list yet.

            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {
                var ingr = (from r in dbContext.Ingredients where r.IngredientId == ingrId select r).First();

                if (ingrId > 0)
                {

                    float ingrcals = ingr.Calories * ingr.Amount;
                    float ingrcarbs = ingr.Calories * ingr.Amount;
                    float ingrpro = ingr.Protein * ingr.Amount;
                    float ingrfat = ingr.Fat * ingr.Amount;

                    float totcals = float.Parse(txtTotCals.Text);
                    float totcarbs = float.Parse(txtTotCarbs.Text);
                    float totpro = float.Parse(txtTotProtein.Text);
                    float totfat = float.Parse(txtTotFat.Text);

                    txtTotCals.Text = (totcals - ingrcals).ToString();
                    txtTotCarbs.Text = (totcarbs - ingrcarbs).ToString();
                    txtTotProtein.Text = (totpro - ingrpro).ToString();
                    txtTotFat.Text = (totfat - ingrfat).ToString();

                    ingr.Amount = 1;
                    selectedIngredients.Remove(ingr);

                    dgvRecipeIngredients.DataSource = null;
                    dgvRecipeIngredients.DataSource = selectedIngredients;
                }
                else { MessageBox.Show("Please select an ingredient to delete."); }

            }
        }

        private void bnEdit_Click(object sender, EventArgs e)
        {
            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {
                //TODO - Add code to remove Nutrition info for Edited ingredient - this is done, but it doesn't reomove the item from the list yet.
                var ingr = (from r in dbContext.Ingredients where r.IngredientId == ingrId select r).First();

                if (ingrId >0)
                {
                    txtAddIngredient.Text = ingr.Name;
                    txtAddUnit.Text = ingr.Units;
                    txtAddAmount.Text = ingr.Amount.ToString();

                    float ingrcals = ingr.Calories * ingr.Amount;
                    float ingrcarbs = ingr.Calories * ingr.Amount;
                    float ingrpro = ingr.Protein * ingr.Amount;
                    float ingrfat = ingr.Fat * ingr.Amount;

                    float totcals = float.Parse(txtTotCals.Text);
                    float totcarbs = float.Parse(txtTotCarbs.Text);
                    float totpro = float.Parse(txtTotProtein.Text);
                    float totfat = float.Parse(txtTotFat.Text);

                    txtTotCals.Text = (totcals - ingrcals).ToString();
                    txtTotCarbs.Text = (totcarbs - ingrcarbs).ToString();
                    txtTotProtein.Text = (totpro - ingrpro).ToString();
                    txtTotFat.Text = (totfat - ingrfat).ToString();
                    
                    selectedIngredients.Remove(ingr);

                    dgvRecipeIngredients.DataSource = null;
                    dgvRecipeIngredients.DataSource = selectedIngredients;
                }
                else { MessageBox.Show("Please select an ingredient to edit."); }
            }
                
        }

        private void formatPage()
        {
            dgvAvailIngredients.Columns[0].Width = 25;
            dgvAvailIngredients.Columns[1].Width = 150;
            dgvAvailIngredients.Columns[2].Width = 75;
            dgvAvailIngredients.Columns[3].Width = 25;
            dgvAvailIngredients.Columns[4].Width = 50;
            dgvAvailIngredients.Columns[5].Width = 50;
            dgvAvailIngredients.Columns[6].Width = 50;
            dgvAvailIngredients.Columns[7].Width = 50;

            dgvRecipeIngredients.Columns[0].Width = 25;
            dgvRecipeIngredients.Columns[1].Width = 150;
            dgvRecipeIngredients.Columns[2].Width = 75;
            dgvRecipeIngredients.Columns[3].Width = 25;
            dgvRecipeIngredients.Columns[4].Width = 50;
            dgvRecipeIngredients.Columns[5].Width = 50;
            dgvRecipeIngredients.Columns[6].Width = 50;
            dgvRecipeIngredients.Columns[7].Width = 50;

        }

        private void btnSearchIngr_Click(object sender, EventArgs e)
        {
            string srch = txtSearchIngr.Text;
            using (RecipeClassesDataContext dbContext = new RecipeClassesDataContext())
            {
                var ingrlist = from r in dbContext.Ingredients where r.Name.Contains(srch) select r;
                dgvAvailIngredients.DataSource = ingrlist;
            }
        }   
    }
}

正在从 LINQ 到 SQL 表中提取成分。成分实际上是一个抽象类,有HealthyIngredient和RegularIngredient子类,但两个子类都存储在同一个SQL表中,在另一个子类有其特定值的地方有空值。

CREATE TABLE [dbo].[Ingredient] (
[IngredientId]    INT           IDENTITY (1, 1) NOT NULL,
[Name]            VARCHAR (50)  NOT NULL,
[Units]           NCHAR (10)    NOT NULL,
[Amount]          FLOAT           DEFAULT ((1)) NOT NULL,
[Calories]        INT           NOT NULL,
[Fat]             INT           NOT NULL,
[Carbs]           INT           NOT NULL,
[Protein]         INT           NOT NULL,
[Discriminator]   NVARCHAR (50) NOT NULL,
[HealthyVariant1] NVARCHAR (50) NULL,
[HealthyVariant2] NVARCHAR (50) NULL,
[HealthyType]     NVARCHAR (50) NULL,
[RegularVariant]  NVARCHAR (50) NULL,
[HealthyVar1Id]   INT           NULL,
[HealthyVar2Id]   INT           NULL,
[RegVarId]        INT           NULL,
PRIMARY KEY CLUSTERED ([IngredientId] ASC)

这似乎也可以正常工作,包括一堆相当复杂的数学运算,包括将表格的插入字段相乘并将它们添加到表单上其他地方的文本框中,并在第一个网格和第二个网格之间更改一些信息,所以数据都被正确传递到第二个数据网格。

但是,如果我尝试从第二个数据网格中选择任何内容,我会收到一个错误,导致整个程序崩溃并将其发送回 Program.cs。

奇怪的是,每隔一段时间,如果我将数据网格设置为 RowHeadderSelect 并且我只单击行标题,它就可以正常工作,包括让我使用编辑按钮并再次正确地进行数学运算,然后它就可以正常工作在该会话期间,无论我单击何处,但是当我重新启动程序时,它再次给我同样的错误。

我已经尝试过 List、IList、ICollection 和 IEnumerable,以防万一是我的列表类型导致了问题,但似乎不是。有人对我做错了什么有一些建议吗?

我添加了更多相关代码。这是目前影响任何事情的仅有的三种方法 - 加载、添加,然后尝试单击数据网格视图。

我不知道在哪里放置任何调试代码,因为它在进入 CellContentClick 方法之前就崩溃了。我在那里有一个断点,但它永远不会到达它。我在想这是我加载数据的内容或方式,但它在页面上的数据网格中看起来都正确。我应该以不同的方式将数据设置到数据网格吗?感谢您的帮助。

System.IndexOutOfRangeException
  HResult=0x80131508
  Message=Index -1 does not have a value.
  Source=System.Windows.Forms
   at System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
   at System.Windows.Forms.CurrencyManager.get_Current()
   at System.Windows.Forms.DataGridView.DataGridViewDataConnection.OnRowEnter(DataGridViewCellEventArgs e)
   at System.Windows.Forms.DataGridView.OnRowEnter(DataGridViewCell& dataGridViewCell, Int32 columnIndex, Int32 rowIndex, Boolean canCreateNewRow, Boolean validationFailureOccurred)
   at System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick)
   at System.Windows.Forms.DataGridView.OnRowHeaderMouseDown(HitTestInfo hti, Boolean isShiftDown, Boolean isControlDown)
   at System.Windows.Forms.DataGridView.OnCellMouseDown(DataGridViewCellMouseEventArgs e)
   at System.Windows.Forms.DataGridView.OnMouseDown(MouseEventArgs e)
   at System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.DataGridView.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at HealthierRecipes.Program.Main() in C:\Users\Tania\source\repos\Old\HealthierRecipes\HealthierRecipes\Program.cs:line 25

【问题讨论】:

  • 您需要更好地跟踪代码。错误似乎很明显……索引超出范围。从发布的代码来看,这个错误只能来自一个地方……dgvRecipeIngredients.Rows[e.RowIndex].Cells[0].Value……并且考虑到执行代码的上下文……网格CellContentClick……我怀疑e.Row或单元格零(0)是无效的单元必须存在才能触发事件。看起来可能有许多事件连接到网格。在每个事件的开头和结尾添加Debug.Writeline 语句,以帮助找出错误的来源。
  • 嗨 John G - 我刚刚在整个 AddRecipeis.cs 页面上输入了所有代码。是的,无论我点击哪里,它都会崩溃。我唯一没有崩溃的情况是偶尔点击行标题时,它根本没有任何文本,如果我打开了 RowHeaderSelect ,但它有时仍然有效,如果我仍然崩溃点击其他任何地方。我将努力尝试调试语句。我还连接了 CellClick 和 CellContentClick 以使用相同的方法。如果您想使用它,我可以向您发送整个项目的 Google Drive 链接。谢谢。
  • 我已经在正在工作的 DGV 上测试了一个和两个,它似乎没有任何不良影响,但我会尝试进一步检查。我以前做过这个,只是为了确保一个人点击的任何地方都会注册。我希望有一个“行中的任何位置”选项,因为我启用了“整行选择”,但似乎没有一个地方。
  • 我添加了一张我的用户界面图片,以便您更好地了解它的工作原理。将数据放入我遇到问题的第二个(底部)网格的唯一方法是使用“添加”按钮。顶部网格是所有可用的成分,底部是用户为此食谱添加的成分。此外,我的添加成分页面以相同的方式使用相同的变量,并且该页面不会崩溃,上部网格也不会崩溃。这就是为什么我想知道问题是否在于我如何将数据添加到网格中。我不确定你在第二个网格中有什么数据。
  • 仅在两个网格上更改为 CellClick。在第一个网格上测试,工作正常,不影响第二个网格的问题。单击网格 1 上的列标题行会得到“索引超出范围”。错误。在网格 2 中没有任何内容的情况下单击它会转到 dgvRecipeIngredients_CellContentClick 方法断点,但标题行 e.rowindex 显然是 -1,因此它得到“索引超出范围”。错误。单击网格 2 中包含数据的列标题行会得到相同的“索引 -1 没有值”。在没有数据的情况下点击其他任何地方没有任何作用。

标签: c# list datagridview linq-to-sql visual-studio-2019


【解决方案1】:

经过多次测试,我将其分解,并可以发布一个非常简单的示例来重现您所描述的内容。但是为了笑容......

在表单“加载”事件中,有一行设置配方成分网格的数据源……

this.dgvRecipeIngredients.DataSource = selectedIngredients;

// 注释掉该行。


此时我们不需要设置网格数据源,因为列表是空的。单击添加按钮时将设置它。

请试试这个,如果有帮助,请告诉我。

这看起来像是另一个很好的例子,其中 BindingList&lt;T&gt; 是 更好的选择。

【讨论】:

  • 是的,删除该行修复了错误。我把它放在那里,所以我可以格式化网格列,但是一旦我把任何东西放在网格中,它们无论如何都会取消格式化。我认为我真正想要的答案是 BindingList。我认为应该有更好的方法来做事,我只是不知道它是什么。谢谢!
  • JohnG, BindingList 运行良好。这就是我一直在寻找的答案。非常感谢你所有的时间。我把我所做的放在页面顶部供其他人查看。我在 StackOverflow 上没有足够的分数来支持答案,所以我要求其他人这样做。再次感谢您。
  • 我花了一点时间才弄明白,但是我能够了解更多关于实体框架的信息。我可以发誓我在使用简单的List&lt;T&gt; 之前已经这样做了。但显然我错了。在任何情况下,使用BindingList&lt;T&gt; 显然比List&lt;T&gt;. 具有许多优势,很高兴它通过简单的修复工作。我参考了你的最后一条评论......当你有代表时你可以投票,但是,因为这是你的问题,你可以将其标记为已回答......What should I do when someone answers my question?......你也得到了一些分数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-30
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多