最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

winforms - C# – FlowLayoutPanel and UI stacking When Adding Multiple User Controls - Stack Overflow

programmeradmin4浏览0评论

I want to create an Employee Manager UI for my desktop application to make it visually appealing and well-structured. So, I started by creating my first UserControl to function as a DataGridView, incorporating a FlowLayoutPanel inside it for proper anization. Then, I created another UserControl to serve as an employee card, naming it EmployeeCard.

For every record retrieved from the database, I generate the same number of EmployeeCard instances. However, the UI becomes slow when generating these EmployeeCard instances, even though the dataset is relatively small—only about 5 to 10 rows.

How can I make the UI smoother and more efficient without encountering performance issues?

Thanks in advance.

I tried one thing that take it from chatgpt but, it doesn't work will that I use Task and make the method asyc.

public void GetEmpCards(DataTable dt)
{
    var Emp_dgv = empDataGridView1;
    if(dt.Rows.Count > 0)
    {
        Emp_dgv.flowLayoutPanel1.Controls.Clear();
        for(int i = 0; i < dt.Rows.Count; i++)
        {
            EmpDataCard empDataCard = new EmpDataCard()
            {
                ID = Convert.ToInt32(dt.Rows[i][0]),
                EmpName = dt.Rows[i][1].ToString(),
                EmpSatatues = dt.Rows[i][2].ToString(),
                EmpGender = dt.Rows[i][3].ToString(),
                EmpPhone = dt.Rows[i][4].ToString(),
                EmpJob = dt.Rows[i][5].ToString(),
                EmpSalary = dt.Rows[i][6].ToString()
            };
            Emp_dgv.flowLayoutPanel1.Controls.Add(empDataCard);
        }
    }
}
// Code that will bring data from SQL.
public DataTable Get_All_Employees()
{
    DataTable dt = new DataTable();

    SqlCommand cmd = new SqlCommand("Select E.Employee_id, E.Employee_Name, E_S.Status_Ar_Name, E.Employee_Sex, E.Phone_Number, J.JobTital, E.Employee_Salary from Employees E, Jobs J, Employees_Statuses E_S Where E.JobID = J.JobID and E.EmployeeStatus_ID = E_S.EmployeeStatus_ID Order By Employee_id;", SQL_Connection.SQL_Link);

    SQL_Connection.SQL_Link.Open();
    dt.Load(cmd.ExecuteReader());
    SQL_Connection.SQL_Link.Close();
    return dt;
}



// Code of EmployeeCard userControl.
public int ID { get; set; }
public string EmpName { get; set; }
public string EmpSatatues { get; set; }
public string EmpGender { get; set; }
public string EmpPhone { get; set; }
public string EmpJob { get; set; }
public string EmpSalary { get; set; }

private void ShowData()
{
    EmpName_lbl.Text = EmpName;
    EmpStatues_lbl.Text = EmpSatatues;
    EmpGender_lbl.Text = EmpGender;
    EmpPhone_lbl.Text = EmpPhone;
    EmpJob_lbl.Text = EmpJob;
    EmpSalary_lbl.Text = EmpSalary;
}

private void EmpDataCard_Load(object sender, EventArgs e)
{
    ShowData();
}

I want to create an Employee Manager UI for my desktop application to make it visually appealing and well-structured. So, I started by creating my first UserControl to function as a DataGridView, incorporating a FlowLayoutPanel inside it for proper anization. Then, I created another UserControl to serve as an employee card, naming it EmployeeCard.

For every record retrieved from the database, I generate the same number of EmployeeCard instances. However, the UI becomes slow when generating these EmployeeCard instances, even though the dataset is relatively small—only about 5 to 10 rows.

How can I make the UI smoother and more efficient without encountering performance issues?

Thanks in advance.

I tried one thing that take it from chatgpt but, it doesn't work will that I use Task and make the method asyc.

public void GetEmpCards(DataTable dt)
{
    var Emp_dgv = empDataGridView1;
    if(dt.Rows.Count > 0)
    {
        Emp_dgv.flowLayoutPanel1.Controls.Clear();
        for(int i = 0; i < dt.Rows.Count; i++)
        {
            EmpDataCard empDataCard = new EmpDataCard()
            {
                ID = Convert.ToInt32(dt.Rows[i][0]),
                EmpName = dt.Rows[i][1].ToString(),
                EmpSatatues = dt.Rows[i][2].ToString(),
                EmpGender = dt.Rows[i][3].ToString(),
                EmpPhone = dt.Rows[i][4].ToString(),
                EmpJob = dt.Rows[i][5].ToString(),
                EmpSalary = dt.Rows[i][6].ToString()
            };
            Emp_dgv.flowLayoutPanel1.Controls.Add(empDataCard);
        }
    }
}
// Code that will bring data from SQL.
public DataTable Get_All_Employees()
{
    DataTable dt = new DataTable();

    SqlCommand cmd = new SqlCommand("Select E.Employee_id, E.Employee_Name, E_S.Status_Ar_Name, E.Employee_Sex, E.Phone_Number, J.JobTital, E.Employee_Salary from Employees E, Jobs J, Employees_Statuses E_S Where E.JobID = J.JobID and E.EmployeeStatus_ID = E_S.EmployeeStatus_ID Order By Employee_id;", SQL_Connection.SQL_Link);

    SQL_Connection.SQL_Link.Open();
    dt.Load(cmd.ExecuteReader());
    SQL_Connection.SQL_Link.Close();
    return dt;
}



// Code of EmployeeCard userControl.
public int ID { get; set; }
public string EmpName { get; set; }
public string EmpSatatues { get; set; }
public string EmpGender { get; set; }
public string EmpPhone { get; set; }
public string EmpJob { get; set; }
public string EmpSalary { get; set; }

private void ShowData()
{
    EmpName_lbl.Text = EmpName;
    EmpStatues_lbl.Text = EmpSatatues;
    EmpGender_lbl.Text = EmpGender;
    EmpPhone_lbl.Text = EmpPhone;
    EmpJob_lbl.Text = EmpJob;
    EmpSalary_lbl.Text = EmpSalary;
}

private void EmpDataCard_Load(object sender, EventArgs e)
{
    ShowData();
}
Share Improve this question edited Mar 21 at 11:49 Malik_Almrzwqy asked Mar 20 at 20:42 Malik_AlmrzwqyMalik_Almrzwqy 111 silver badge3 bronze badges 2
  • Controls are expensive, you've got a lot of them. Beyond 50 or 100 of them, their paint behavior becomes pretty noticeable. Note how DataGridView is just a single control, displaying many items. You've got to do the same thing, get there by replacing labels with TextRenderer.DrawText and pictureboxes with Graphics.DrawImage, etc. – Hans Passant Commented Mar 20 at 22:19
  • Yikes. That A, B syntax for joins has been obsolete for more than 30 years (!) now. – Joel Coehoorn Commented Mar 21 at 13:44
Add a comment  | 

1 Answer 1

Reset to default 2

There are five things I want to look at here to make it better and more responsive.

First and easiest, you want to call SupsendLayout() at the beginning of a process to add several controls, with a corresponding ResumeLayout() at the end.

Second, commonly the database is a factor for this kind of performance issue. So we'd want to review the code that populates the dt DataTable you're passing in. Unfortunately, that code is not available to us in the question, so all I can do is point it out as an item of interest.

Third, you want to look at the EmployeeCard itself, and how it is built. It's possible it is doing things that are making the UI drag... but just like the second item this code is not available to us, so all I can do it mention it as an item of interest.

Fourth, I want to know how the GetEmpCards() method is called. It's possible this is being repeated more often than necessary... as often as every paint or scroll, which would be bad.

Fifth and finally, this method should be limited to a transform operation from a DataTable (collection of IDataRow objects) into a corresponding collection of EmpDataCard objects, with no interaction with the UI at all. This will also make it easier to convert to an async option if needed. Even this is a bit off: we should be creating basic POCO Employee objects, and there should be a constructor or Load() method for the EmpDataCard control to accept the Employee. This Employee type can mediate and share between your data code (IDataRow) and your UI (EmpDataCard), as well as other applications. For example, if you were ever to build web or mobile views, the Employee type and everything upstream might not have to change much.

But one step at a time. We want the method to look more like this:

public IEnumerable<EmpDataCard> EmpCardsFromDataTable(DataTable dt)
{
    // AsEnumerable() is defined here:
    // https://learn.microsoft/en-us/dotnet/api/system.data.datatableextensions.asenumerable
    return dt.AsEnumerable().Select( row => new EmpDataCard() {
                ID = Convert.ToInt32(row[0]),
                EmpName = row[1].ToString(),
                EmpSatatues = row[2].ToString(),
                EmpGender = row[3].ToString(),
                EmpPhone = row[4].ToString(),
                EmpJob = row[5].ToString(),
                EmpSalary = row[6].ToString()
            });
}

We can improve this further by adding a method to translate ONE row into a card:

public EmpDataCard EmpDataCardFromIDataRow(IDataRow row)
{
    return new EmpDataCard() {
        ID = Convert.ToInt32(row[0]),
        EmpName = row[1].ToString(),
        EmpSatatues = row[2].ToString(),
        EmpGender = row[3].ToString(),
        EmpPhone = row[4].ToString(),
        EmpJob = row[5].ToString(),
        EmpSalary = row[6].ToString()
    };
}

This lets us rewrite the previous method:

public IEnumerable<EmpDataCard> EmpCardsFromDataTable(DataTable dt)
{
    return dt.AsEnumerable().Select(EmpDataCardFromIDataRow);
}

At this point, we might even not define that method at all, as it's a short one-liner.

Now the code that used call this method needs to expand a little bit. Instead of just this:

GetEmpCards(dt);

it will look like this:

if(dt.Rows.Count > 0)
{
    SuspendLayout();
    empDataGridView1.flowLayoutPanel1.Controls.Clear();
    foreach(var card in dt.AsEnumerable().Select(EmpDataCardFromIDataRow))
    {
        empDataGridView1.flowLayoutPanel1.Controls.Add(card);
    }
    ResumeLayout();
}

or this:

if(dt.Rows.Count > 0)
{
    SuspendLayout();
    empDataGridView1.flowLayoutPanel1.Controls.Clear();
    empDataGridView1.flowLayoutPanel1.Controls.AddRange(dt.AsEnumerable().Select(EmpDataCardFromIDataRow).ToArray());
    ResumeLayout();
}

Yes, this is more code in that place, but it's still a win, because we have achieved a better separation of concerns, where the translation code ONLY does translation, and the UI code is better able to manage the UI.

As a concrete example of why this is a win, if there are other control changes taking place at the same time, the SuspendLayout() and ResumeLayout() calls can now be easily moved to wrap all of them and avoid accidently resuming the layout too soon.

发布评论

评论列表(0)

  1. 暂无评论