winforms 基于具有有限选择的复选框更改行颜色

k10s72fa  于 2022-11-17  发布在  其他
关注(0)|答案(2)|浏览(196)

CurrentCellDirtyStateChanged事件代码将复选框的数量限制为(2).具有选中框的行包含可编辑数据,这些数据用于计算其余未选中的只读行中的值。我希望datagrid通过更改选中行的颜色来指示可编辑行,并使其余行为只读。CurrentCellDirtyStateChanged事件用于管理复选框,但与更改行颜色的CellClick事件冲突。框不能一致地选中和更改颜色。如有任何帮助,将不胜感激。

using System.Linq;
    private void dgvParameters_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        if (dgvParameters.CurrentCell is DataGridViewCheckBoxCell && dgvParameters.IsCurrentCellDirty && !(bool)dgvParameters.CurrentCell.FormattedValue)
        {
            var count = dgvParameters.Rows.Cast<DataGridViewRow>()
                .SelectMany(r => r.Cells.OfType<DataGridViewCheckBoxCell>()
                .Where(c => (bool)c.FormattedValue))
                .Count();

            if (count == 2) dgvParameters.CancelEdit(); 

        }
    }

    private void dgvParameters_CellClick(object sender, DataGridViewCellEventArgs e)
    {
        int index = dgvParameters.CurrentRow.Index;

        DataGridViewCellStyle CheckedStyle;
        DataGridViewCellStyle UnCheckedStyle;
        CheckedStyle = new DataGridViewCellStyle();
        UnCheckedStyle = new DataGridViewCellStyle();
        CheckedStyle.BackColor = Color.White;
        CheckedStyle.ForeColor = Color.Black;
        UnCheckedStyle.BackColor = Color.LightGray;
        UnCheckedStyle.ForeColor = Color.Black;
        bool isSelect = dgvParameters[5, index].Value as bool? ?? false;

        if (Convert.ToBoolean(row.Cells[5].Value))
        {
            dgvParameters.Rows[index].Cells[1].Style = CheckedStyle;
            dgvParameters.Rows[index].Cells[3].Style = CheckedStyle;
            dgvParameters.Rows[index].Cells[1].ReadOnly = false;
            dgvParameters.Rows[index].Cells[3].ReadOnly = false;

        }
        else
        {
            dgvParameters.Rows[index].Cells[1].Style = UnCheckedStyle;
            dgvParameters.Rows[index].Cells[3].Style = UnCheckedStyle;
            dgvParameters.Rows[index].Cells[1].ReadOnly = true;
            dgvParameters.Rows[index].Cells[3].ReadOnly = true;
        }
    }
pgccezyw

pgccezyw1#

您可以继续使用CurrentCellDirtyStateChanged方法,并处理CellValueChanged事件,以便根据DataGridViewCheckBoxCell的新值设置同一行的相关单元格的属性。
首先,确保您通过设计器或代码设置了目标单元格的默认值。我假设复选框单元格的初始值为false

public YourForm()
{
    InitializeComponent();

    foreach (int i in new[] { 1, 3 })
    {
        dgvParameters.Columns[i].DefaultCellStyle.BackColor = Color.LightGray;
        dgvParameters.Columns[i].DefaultCellStyle.ForeColor = Color.Gray;
        dgvParameters.Columns[i].ReadOnly = true;
    }
}

其次,修改CurrentCellDirtyStateChanged事件如下...

private void dgvParameters_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dgvParameters.CurrentCell is DataGridViewCheckBoxCell &&
        dgvParameters.IsCurrentCellDirty)
    {
        if (!(bool)dgvParameters.CurrentCell.FormattedValue)
        {
            var count = dgvParameters.Rows.Cast<DataGridViewRow>()
                .SelectMany(r => r.Cells.OfType<DataGridViewCheckBoxCell>()
                .Where(c => (bool)c.FormattedValue))
                .Count();

            if (count == 2) dgvParameters.CancelEdit();
        }

        dgvParameters.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
}

请注意,dgvParameters.CommitEdit(DataGridViewDataErrorContexts.Commit);行是用来应用的,并在切换值时查看结果。如果需要保持当前行的脏状态并在单元格离开时提交更改,请注解该行。
最后,如果e.ColumnIndex是复选框单元格的索引,则处理CellValueChanged事件以更新目标单元格...

private void dgvParameters_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.RowIndex >= 0 && e.ColumnIndex == 5)
    {
        var cell5 = dgvParameters.Rows[e.RowIndex].Cells[e.ColumnIndex];
        bool isReadOnly = !(bool)cell5.FormattedValue;
        Color backColor, foreColor;

        if (isReadOnly)
        {
            backColor = Color.LightGray;
            foreColor = Color.Gray;
        }
        else
        {
            backColor = Color.White;
            foreColor = Color.Black;
        }

        foreach (int i in new[] { 1, 3 })
        {
            dgvParameters.Rows[e.RowIndex].Cells[i].Style.BackColor = backColor;
            dgvParameters.Rows[e.RowIndex].Cells[i].Style.ForeColor = foreColor;
            dgvParameters.Rows[e.RowIndex].Cells[i].ReadOnly = isReadOnly;
        }
    }
}

这样,您只需处理一行,而无需考虑其余行。

6jygbczu

6jygbczu2#

您的问题是希望未选中的行设置为只读。一个明显的冲突是,如果某行的整体确实是只读的,那么就没有办法选中它使其可编辑。我的第一个建议是创建一个refreshStyles方法,该方法考虑了哪些单元格是DataGridViewCheckBoxCell,这样,无论行。

private void refreshStyles()
{
    for (int i = 0; i < DataSource.Count; i++)
    {
        var item = DataSource[i];
        var row = dataGridView.Rows[i];
        // Use System.Linq to get all of the row cells that aren't checkboxes.
        var cells = 
            row.Cells.Cast<DataGridViewCell>()
            .Where(_ => !(_ is DataGridViewCheckBoxCell));
        if(item.IsChecked)
        {
            row.DefaultCellStyle.BackColor = Color.Azure;
            foreach (var cell in cells) cell.ReadOnly = false;
        }
        else
        {
            dataGridView.Rows[i].DefaultCellStyle.BackColor = 
                Color.FromArgb(0xf0, 0xf0, 0xf0);
            foreach (var cell in cells) cell.ReadOnly = true;
        }
    }
}

这是使DGV以只读行和可编辑行的混合形式显示的一个步骤。

这一切都是基于将DGV的DataSource属性设置为类DgvItem的绑定列表,该类的最低实现如图所示。将IsChecked设置为绑定属性的原因是为了让DataSource在其值更改时发送通知(否则它只在添加或删除项时通知)。

class DgvItem : INotifyPropertyChanged
{
    bool _IsChecked = false;
    public bool IsChecked
    {
        get => _IsChecked;
        set
        {
            if (!Equals(_IsChecked, value))
            {
                _IsChecked = value;
                OnPropertyChanged();
            }
        }
    }
    public string Description { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(
        [CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

初始化

剩下的唯一事情就是在MainForm中的Load事件的override中将它们粘合在一起。

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    dataGridView.AllowUserToAddRows = false;

    // Set the DataSource of DGV to a binding list of 'DgvItem' instances.
    dataGridView.DataSource = DataSource;
    for (int i = 1; i <= 5; i++)
    {
        DataSource.Add(new DgvItem { Description = $"Item {i}" });
    }

    // Do a little column formatting
    dataGridView.Columns[nameof(DgvItem.IsChecked)].Width = 50;
    dataGridView.Columns[nameof(DgvItem.IsChecked)].HeaderText = string.Empty;
    dataGridView.Columns[nameof(DgvItem.Description)].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

    // Ensure the row isn't stuck in edit mode when a checkbox changes
    dataGridView.CurrentCellDirtyStateChanged += (sender, e) =>
    {
        if(dataGridView.CurrentCell is DataGridViewCheckBoxCell)
        {
            dataGridView.EndEdit();
        }
    };

    // Get notified when a checkbox changes
    DataSource.ListChanged += (sender, e) =>
    {
        switch (e.ListChangedType)
        {
            case ListChangedType.ItemChanged:
                // Make changes to row styles when that happens.
                refreshStyles();
                break;
        }
    };

    // Init the styles
    refreshStyles();
}

我希望这能给你一些实现你想要的结果的想法。

相关问题