【问题标题】:Cell selection hides behind image DataGridViewImageCell单元格选择隐藏在图像 DataGridViewImageCell 后面
【发布时间】:2022-12-10 12:54:09
【问题描述】:

我正在使用 Winform DataGridView 来显示图像。但是当图像填满单元格时,我看不到蓝色选择或数量非常少。请看:

当一个单元格被选中时,我预计使单元格整个透明蓝色,而不仅仅是侧面或未被图像占据的侧面。喜欢:

目前我尝试在绘画事件中自己涂上蓝色,但更新太频繁导致软件挂起。

我还修改了图像,使其在选择更改事件中看起来偏蓝,但它再次减慢了软件速度。

有解决办法吗?任何解决方法或什么?在不影响性能的情况下?

编辑:这是我如何在 datagridview 上显示图像的源代码:

int colms = 4; // total no. of columns in our datagridview

//this create 4 image columns in datagridview
for (int c = 0; c < colms; c++)
{
    var imgColm = new DataGridViewImageColumn();
    imgColm.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
    imgColm.ImageLayout = DataGridViewImageCellLayout.Zoom;
    grid.Columns.Add(imgColm);
}

int colm = 0;
int row = 0;

//this get all images and display on datagridview 
foreach (var img in Directory.GetFiles(@"C:\Users\Administrator\Desktop\images"))
{
    if (colm >= colms)
    {
        row++;
        colm = 0;
        grid.Rows.Add();
    }
    ((DataGridViewImageCell)grid.Rows[row].Cells[colm]).Value = Thumb.GetThumbnail(img, ThumbSize.LowRes);
    colm++;
}

目前细胞绘画我只使用一种解决方法,that draws border on selected cell。但是当数据很大并且其次也利用未选择的单元格时它很慢。

【问题讨论】:

  • 该图像不是单元格背景的一部分。但在此之前,您如何填充网格? .DataSource.Rows.Add?如何处理CellPainting事件?您可以处理 RowPostPaint 事件,而不是用半透明的蓝色填充选定的单元格。请编辑以添加相关详细信息。
  • 至于性能较慢的部分,可能是加载了大量图片显示所致。考虑Implementing Virtual Mode in the Windows Forms DataGridView Control
  • @dr.null 请看我的编辑。 VirtualMode 看起来很有前途。

标签: c# winforms datagridview datagridviewimagecolumn


【解决方案1】:

这里有两个例子来测试你自己关于所选单元格的性能和填充样式。由于您的代码 sn-p 没有显示在什么上下文中调用代码,特别是创建图像列部分,并且为了避免重复不必要的例程,请使用网格设计器添加 4 列类型 DataGridViewImageColumn 并设置自动大小和那里的布局属性。

正常模式
在 Form 的 ctor 中,使用 Reflection 启用网格的 DoubleBuffered 属性以减少闪烁。 emptyImage 位图是空单元格的空值。

public partial class SomeForm : Form
{
    private Bitmap emptyImage;

    public SomeForm()
    {
        dgv.GetType()
            .GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
            .SetValue(dgv, true);

        emptyImage = new Bitmap(1, 1);

        foreach (var col in dgv.Columns.OfType<DataGridViewImageColumn>())
            col.DefaultCellStyle.NullValue = emptyImage;
    }

覆盖 OnLoad 方法以填充网格或为此调用方法。

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        var imgFolder = @"Your-Image-Folder-Path";
        LoadImages(imgFolder);
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        base.OnFormClosed(e);
        emptyImage.Dispose();
    }

    private void LoadImages(string path)
    {            
        string[] allFiles = Directory.GetFiles(path);

        for (int i = 0; i < allFiles.Length; i += 4)
        {
            var files = allFiles.Skip(i).Take(4);

            dgv.Rows.Add(
                files.Select(f =>
                {
                    using (var img = Image.FromFile(f, true))
                        return Thumb.GetThumbnail(img, ThumbSize.LowRes);
                }).ToArray());
        }
    }

请注意,您的 Thumb.GetThumbnail 方法返回一个新图像,因此您需要处理原始图像。

实施 CellPainting 事件以绘制除 DataGridViewPaintParts.SelectionBackground 之外的所有内容,并用半透明颜色填充选定的单元格。

    private void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    {    
        e.Paint(e.ClipBounds, e.PaintParts & ~DataGridViewPaintParts.SelectionBackground);
    
        if (e.RowIndex >= 0 &&
            e.Value != null && e.Value != emptyImage &&
            (e.State & DataGridViewElementStates.Selected) > 0)
        {            
            using (var br = new SolidBrush(Color.FromArgb(100, SystemColors.Highlight)))
                e.Graphics.FillRectangle(br, e.CellBounds);
        }

        e.Handled = true;
    }
}

虚拟模式
你需要在这里有数据存储来缓存你需要显示的图像。可见行的每个单元格的图像。为此,Cache 类是 创建用于管理相关功能,包括:

  • 正在计算每个可见行显示 4 个图像所需的总行数。因此,SetMaxRows 方法应该在网格首次创建和调整大小时重新计算可见行时调用。
  • Dictionary&lt;int, Image&gt; 中加载、创建和缓存当前可见行图像,其中键是单元格编号。
  • 在引发 CellValueNeeded 事件时传递请求的图像。
public partial class SomeForm : Form
{
    private readonly Cache cache;

    public SomeForm()
    {
        dgv.GetType()
            .GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
            .SetValue(dgv, true);
        dgv.VirtualMode = true;

        var imgFolder = @"Your-Image-Folder-Path";
        cache = new Cache(imgFolder, dgv.ColumnCount);
        dgv.RowCount = cache.GetRowCount();
        SetMaxRows();
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        base.OnFormClosed(e);
        cache.Dispose();
    }

    private void dgv_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) =>
        e.Value = cache.GetImage(e.RowIndex, e.ColumnIndex);

    private void dgv_Resize(object sender, EventArgs e) => SetMaxRows();

    // Change dgv.RowTemplate.Height as needed...
    private void SetMaxRows() =>
        cache.MaxRows = (int)Math
        .Ceiling((double)dgv.ClientRectangle.Height / dgv.RowTemplate.Height);

    private class Cache : IDisposable
    {
        private readonly Dictionary<int, Image> dict;
        private readonly Bitmap nullImage;
        private int currentRowIndex = -1;

        private Cache()
        {
            dict = new Dictionary<int, Image>();
            nullImage = new Bitmap(1, 1);
        }

        public Cache(string path, int columnCount) : this()
        {
            ImageFolder = path;                
            ColumnCount = columnCount;
        }

        public string ImageFolder { get; set; }
        public int ColumnCount { get; set; }
        public int MaxRows { get; set; }
        public Bitmap NullImage => nullImage;

        public Image GetImage(int rowIndex, int columnIndex)
        {
            var ri = rowIndex - (rowIndex % MaxRows);

            if (ri != currentRowIndex)
            {
                foreach (var img in dict.Values) img?.Dispose();
                currentRowIndex = ri;
                dict.Clear();
            }

            var i = (rowIndex * ColumnCount) + columnIndex;
            Image res = nullImage;

            if (!dict.ContainsKey(i))
            {
                var file = Directory.EnumerateFiles(ImageFolder)
                    .Skip(i).FirstOrDefault();

                if (file != null)
                {
                    using (var img = Image.FromFile(file, true))
                        dict[i] = res = Thumb.GetThumbnail(img, ThumbSize.LowRes);
                }
            }
            else
            {
                res = dict[i];
            }

            return res;
        }

        public int GetRowCount()
        {
            var count = Directory.EnumerateFiles(ImageFolder).Count();
            return (int)Math.Ceiling((double)count / ColumnCount);
        }

        public void Dispose()
        {
            foreach (var img in dict.Values) img?.Dispose();
            nullImage.Dispose();
        }
    }

最后,CellPainting 事件几乎保持不变,只是您从 cache 实例获取空图像。

    private void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    {
        e.Paint(e.ClipBounds, e.PaintParts & ~DataGridViewPaintParts.SelectionBackground);

        if (e.RowIndex >= 0 &&
            e.Value != null && e.Value != cache.NullImage &&
            (e.State & DataGridViewElementStates.Selected) > 0)
        {                
            using (var br = new SolidBrush(Color.FromArgb(100, SystemColors.Highlight)))
                e.Graphics.FillRectangle(br, e.CellBounds);
        }

        e.Handled = true;
    }
}

【讨论】:

    猜你喜欢
    • 2017-10-24
    • 2018-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 2014-12-08
    • 1970-01-01
    • 2013-08-03
    相关资源
    最近更新 更多