在DataGridView中使用自定义控件

时间:2013-11-11 20:03:43

标签: c# winforms datagridview custom-controls

我已经用谷歌搜索了这个问题的答案,但我似乎找不到任何好的实例。我创建了一个名为StarControl的自定义星级用户控件。控件基本上是五个相互水平相邻的图片框,我有以下代码:

public partial class StarControl : UserControl
{
    private enum StarTypes
    {
        Hollow,
        Filled
    }

    private readonly StarTypes[] _stars;
    private int _rating;

    public StarControl()
    {
        InitializeComponent();
        Locked = false;
        _stars = new StarTypes[5];
        _stars[0] = StarTypes.Hollow;
        _stars[1] = StarTypes.Hollow;
        _stars[2] = StarTypes.Hollow;
        _stars[3] = StarTypes.Hollow;
        _stars[4] = StarTypes.Hollow;
        Rating = 0;
        SetStars();
    }

    public bool Locked
    {
        get;
        set;
    }

    public int Rating
    {
        get { return _rating; }
        set { _rating = value; SetRating(); }
    }

    private void SetRating()
    {
        if (_rating == 0)
        {
            _stars[0] = StarTypes.Hollow;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;
        }
        if (_rating == 1)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;
        }
        if (_rating == 2)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;
        }
        if (_rating == 3)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Filled;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;
        }
        if (_rating == 4)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Filled;
            _stars[3] = StarTypes.Filled;
            _stars[4] = StarTypes.Hollow;
        }
        if (_rating == 5)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Filled;
            _stars[3] = StarTypes.Filled;
            _stars[4] = StarTypes.Filled;
        }
        SetStars();
    }

    private void SetStars()
    {
        pbStar1.Image = _stars[0] == StarTypes.Hollow
            ? Properties.Resources.star_hollow
            : Properties.Resources.star_filled;

        pbStar2.Image = _stars[1] == StarTypes.Hollow
            ? Properties.Resources.star_hollow
            : Properties.Resources.star_filled;

        pbStar3.Image = _stars[2] == StarTypes.Hollow
            ? Properties.Resources.star_hollow
            : Properties.Resources.star_filled;

        pbStar4.Image = _stars[3] == StarTypes.Hollow
            ? Properties.Resources.star_hollow
            : Properties.Resources.star_filled;

        pbStar5.Image = _stars[4] == StarTypes.Hollow
            ? Properties.Resources.star_hollow
            : Properties.Resources.star_filled;
    }

    private void PbStar1MouseEnter(object sender, EventArgs e)
    {
        if (!Locked)
        {
            pbStar1.Image = Properties.Resources.star_filled;
            pbStar2.Image = Properties.Resources.star_hollow;
            pbStar3.Image = Properties.Resources.star_hollow;
            pbStar4.Image = Properties.Resources.star_hollow;
            pbStar5.Image = Properties.Resources.star_hollow;
        }
    }

    private void PbStar1MouseLeave(object sender, EventArgs e)
    {
        if (!Locked)
        {
            SetStars();
        }
    }

    private void PbStar2MouseEnter(object sender, EventArgs e)
    {
        if (!Locked)
        {
            pbStar1.Image = Properties.Resources.star_filled;
            pbStar2.Image = Properties.Resources.star_filled;
            pbStar3.Image = Properties.Resources.star_hollow;
            pbStar4.Image = Properties.Resources.star_hollow;
            pbStar5.Image = Properties.Resources.star_hollow;
        }
    }

    private void PbStar2MouseLeave(object sender, EventArgs e)
    {
        if (!Locked)
        {
            SetStars();
        }
    }

    private void PbStar3MouseEnter(object sender, EventArgs e)
    {
        if (!Locked)
        {
            pbStar1.Image = Properties.Resources.star_filled;
            pbStar2.Image = Properties.Resources.star_filled;
            pbStar3.Image = Properties.Resources.star_filled;
            pbStar4.Image = Properties.Resources.star_hollow;
            pbStar5.Image = Properties.Resources.star_hollow;
        }
    }

    private void PbStar3MouseLeave(object sender, EventArgs e)
    {
        if (!Locked)
        {
            SetStars();
        }
    }

    private void PbStar4MouseEnter(object sender, EventArgs e)
    {
        if (!Locked)
        {
            pbStar1.Image = Properties.Resources.star_filled;
            pbStar2.Image = Properties.Resources.star_filled;
            pbStar3.Image = Properties.Resources.star_filled;
            pbStar4.Image = Properties.Resources.star_filled;
            pbStar5.Image = Properties.Resources.star_hollow;
        }
    }

    private void PbStar4MouseLeave(object sender, EventArgs e)
    {
        if (!Locked)
        {
            SetStars();
        }
    }

    private void PbStar5MouseEnter(object sender, EventArgs e)
    {
        if (!Locked)
        {
            pbStar1.Image = Properties.Resources.star_filled;
            pbStar2.Image = Properties.Resources.star_filled;
            pbStar3.Image = Properties.Resources.star_filled;
            pbStar4.Image = Properties.Resources.star_filled;
            pbStar5.Image = Properties.Resources.star_filled;
        }
    }

    private void PbStar5MouseLeave(object sender, EventArgs e)
    {
        if (!Locked)
        {
            SetStars();
        }
    }

    private void PbStar1MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left && !Locked)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 1;
        }
        if (e.Button == MouseButtons.Right && !Locked)
        {
            _stars[0] = StarTypes.Hollow;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 0;
        }
        SetStars();
    }

    private void PbStar2MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left && !Locked)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 2;
        }
        if (e.Button == MouseButtons.Right && !Locked)
        {
            _stars[0] = StarTypes.Hollow;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 0;
        }
        SetStars();
    }

    private void PbStar3MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left && !Locked)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Filled;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 3;
        }
        if (e.Button == MouseButtons.Right && !Locked)
        {
            _stars[0] = StarTypes.Hollow;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 0;
        }
        SetStars();
    }

    private void PbStar4MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left && !Locked)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Filled;
            _stars[3] = StarTypes.Filled;
            _stars[4] = StarTypes.Hollow;

            Rating = 4;
        }
        if (e.Button == MouseButtons.Right && !Locked)
        {
            _stars[0] = StarTypes.Hollow;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 0;
        }
        SetStars();
    }

    private void PbStar5MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left && !Locked)
        {
            _stars[0] = StarTypes.Filled;
            _stars[1] = StarTypes.Filled;
            _stars[2] = StarTypes.Filled;
            _stars[3] = StarTypes.Filled;
            _stars[4] = StarTypes.Filled;

            Rating = 5;
        }
        if (e.Button == MouseButtons.Right && !Locked)
        {
            _stars[0] = StarTypes.Hollow;
            _stars[1] = StarTypes.Hollow;
            _stars[2] = StarTypes.Hollow;
            _stars[3] = StarTypes.Hollow;
            _stars[4] = StarTypes.Hollow;

            Rating = 0;
        }
        SetStars();
    }
}

控件工作得很好。在我的表单上,我有一个DataGridView控件,我正在尝试使用集合中的行动态填充DataGridView。该集合只是这个类的集合:

[Serializable]
public class Rating
{
    public string VendorName { get; set; }
    public int VendorRating { get; set; }
}

public List<Rating> _myRatings;

VendorName只是一个字符串,VendorRating是一个int,表示0-5之间的数字。通过在我的Rating上设置StarControl属性,它会显示星数。我要做的是弄清楚如何让StarControl用户控件显示在DataGridView上。有人可以请我提供一个如何做到这一点的例子吗?

我在这个主题上看过this MSDN article,但我不认为这适用于我,因为它们只是继承自DataGridViewTextBoxCell,我的控制权比日期/时间更复杂文本框。

2 个答案:

答案 0 :(得分:8)

出于这样的目的,您根本不需要这样的控制。与DataGridView的附件有点棘手。我不想在这种方法后给你一个解决方案,但我只想分享一些关于如何实现它的信息。首先,您必须计算总显示的单元格,您需要 List<YourControl> 来存储所有需要的控件。这些控件必须将DataGridView作为Parent。这些控件的数量必须等于显示的单元格数。然后在CellPainting事件处理程序中,您必须更新列表中所有控件的位置。我们在 CellPainting 事件处理程序中添加位置更新代码,因为无论何时更新单元格值和边界,都会触发 CellPainting 并相应地更新控件“Location 。这有点棘手但确实有效。您可以通过某些引用属性将每个控件与每个单元格相关联,例如Tag属性。

现在,我想与您分享这种更好的方法。您只需为自定义DataGridViewCell创建用作CellTemplate的自定义DataGridViewColumn。我们必须使用GDI+进行一些绘画并编写相当多的代码。请注意,我为自己编写了这个演示,但是在阅读完问题之后就开始了。用这样一个完整的代码回答问题并不是真的搞笑,它耗费了很多时间,但正如我所说,我打算自己使用这个演示,现在我只想和你分享。事实上,为了给你一些想法,代码可以简化得更多:

public class DataGridViewRatingColumn : DataGridViewColumn {
    public DataGridViewRatingColumn() : base(new DataGridViewRatingCell()) {
        base.ReadOnly = true;
        RatedStarColor = Color.Green;
        GrayStarColor = Color.LightGray;
        StarScale = 1;            
    }
    bool readOnly;
    public new bool ReadOnly
    {
        get {
            return readOnly;
        }
        set {
            readOnly = value;                
        }
    }
    Color ratedStarColor;
    Color grayStarColor;
    float starScale;
    public Color RatedStarColor {
        get { return ratedStarColor; }
        set {
            if (ratedStarColor != value) {
                ratedStarColor = value;
                if (DataGridView != null) DataGridView.InvalidateColumn(Index);
            }
        }
    }
    public Color GrayStarColor
    {
        get { return grayStarColor; }
        set {
            if (grayStarColor != value){
                grayStarColor = value;
                if(DataGridView != null) DataGridView.InvalidateColumn(Index);
            }
        }
    }
    public float StarScale {
        get { return starScale; }
        set {
            if (starScale != value) {
                starScale = value;
                DataGridViewRatingCell.UpdateBrushes(value);
                if (DataGridView != null) DataGridView.InvalidateColumn(Index);
            }
        }
    }
}    
public class DataGridViewRatingCell : DataGridViewTextBoxCell {
    static DataGridViewRatingCell() {
        //Init star            
        List<PointF> points = new List<PointF>();
        bool largeArc = true;
        R = 10;
        r = 4;
        center = new Point(R, R);
        for (float alpha = 90; alpha <= 414; alpha += 36)
        {
            int d = largeArc ? R : r;
            double radAlpha = alpha * Math.PI / 180;
            float x = (float)(d * Math.Cos(radAlpha));
            float y = (float)(d * Math.Sin(radAlpha));
            points.Add(new PointF(center.X + x, center.Y + y));
            largeArc = !largeArc;
        }
        star.AddPolygon(points.ToArray());
        star.Transform(new Matrix(1, 0, 0, -1, 0, center.Y * 2));             
        //Init stars
        UpdateBrushes(1);                   
    }
    public DataGridViewRatingCell() {
        ValueType = typeof(int);
        ratedStarColor = Color.Green;
        grayStarColor = Color.LightGray;
        starScale = 1;
        UseColumnStarColor = true;
        UseColumnStarScale = true;            
    }                
    public override object DefaultNewRowValue {
        get {
            return 0;
        }
    }
    internal static void UpdateBrushes(float scale) {
        int space = 2*R;
        for (int i = 0; i < 5; i++) {
            if (stars[i] != null) stars[i].Dispose();
            stars[i] = (GraphicsPath)star.Clone();
            stars[i].Transform(new Matrix(scale, 0, 0, scale, space * i * scale, 0));                
            brushes[i] = CreateBrush(new RectangleF(center.X - R + space * i * scale, center.Y - R, R * 2 * scale, R * 2 * scale));
        }
    }
    private static LinearGradientBrush CreateBrush(RectangleF bounds)
    {
        var brush = new LinearGradientBrush(bounds,Color.White, Color.Yellow, LinearGradientMode.ForwardDiagonal);
        ColorBlend cb = new ColorBlend();
        Color c = Color.Green;
        Color lightColor = Color.White;
        cb.Colors = new Color[] { c, c, lightColor, c, c };
        cb.Positions = new float[] { 0, 0.4f, 0.5f, 0.6f, 1 };
        brush.InterpolationColors = cb;            
        return brush;
    }
    private void AdjustBrushColors(LinearGradientBrush brush, Color baseColor, Color lightColor)
    {
        //Note how we adjust the colors, using brush.InterpolationColors directly won't work.
        ColorBlend cb = brush.InterpolationColors;
        cb.Colors = new Color[] { baseColor, baseColor, lightColor, baseColor, baseColor };
        brush.InterpolationColors = cb;
    }        
    static GraphicsPath star = new GraphicsPath();
    static GraphicsPath[] stars = new GraphicsPath[5];
    static LinearGradientBrush[] brushes = new LinearGradientBrush[5];
    static Point center;
    static int R, r;
    int currentValue = -1;
    bool mouseOver;
    protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, 
        int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, 
        string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
    {
        base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue,
            errorText, cellStyle, advancedBorderStyle, paintParts & ~DataGridViewPaintParts.SelectionBackground & ~DataGridViewPaintParts.ContentForeground);             
        if (rowIndex == RowIndex && (paintParts & DataGridViewPaintParts.ContentForeground) != 0) {
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            if(Value != null) Value = Math.Min(Math.Max(0, (int)Value), 5);
            if (!mouseOver) currentValue = (int)(Value ?? 0);  
            PaintStars(graphics, cellBounds, 0, currentValue, true);
            PaintStars(graphics, cellBounds, currentValue, 5 - currentValue, false);
            graphics.SmoothingMode = SmoothingMode.Default;             
        }
    }
    protected override void OnMouseMove(DataGridViewCellMouseEventArgs e) {
        base.OnMouseMove(e);
        if (!mouseOver) mouseOver = true;
        if (IsReadOnly()) return;
        var lastStar = stars.Select((x, i) => new { x, i })
                            .LastOrDefault(x => x.x.IsVisible(e.Location));
        if (lastStar != null) {
            currentValue = lastStar.i + 1;                
            DataGridView.Cursor = Cursors.Hand;
        }
        else if(RowIndex > -1) {
            currentValue = (int)(Value ?? 0);
            DataGridView.Cursor = Cursors.Default;
        }
        DataGridView.InvalidateCell(this);
    }        
    protected override void OnClick(DataGridViewCellEventArgs e) {
        base.OnClick(e);
        if (IsReadOnly()) return;
        Value = currentValue == 1 && (int?) Value == 1 ? 0 : currentValue;
    }
    protected override void OnMouseLeave(int rowIndex) {
        base.OnMouseLeave(rowIndex);
        mouseOver = false;
        if (IsReadOnly()) return;
        if (rowIndex == RowIndex) {
            currentValue = (int)(Value ?? 0);
            DataGridView.InvalidateCell(this);
        }            
    }        
    private bool IsReadOnly() {
        var col = OwningColumn as DataGridViewRatingColumn;
        return col != null ? col.ReadOnly : false;
    }
    private void PaintStars(Graphics g, Rectangle bounds, int startIndex, int count, bool rated) {
        GraphicsState gs = g.Save();
       g.TranslateTransform(bounds.Left, bounds.Top);           
        var col = OwningColumn as DataGridViewRatingColumn;
        Color ratedColor = col == null ? Color.Yellow :
            UseColumnStarColor ? col.RatedStarColor : RatedStarColor;
        Color grayColor = col == null ? Color.LightGray :
            UseColumnStarColor ? col.GrayStarColor : GrayStarColor;
        float starScale = col == null ? 1 :
            UseColumnStarScale ? col.StarScale : StarScale;
        UpdateBrushes(starScale);
       for(int i = startIndex; i < startIndex + count; i++) {
           AdjustBrushColors(brushes[i], rated ? ratedColor : grayColor, rated ? Color.White : grayColor);
           g.FillPath(brushes[i], stars[i]);
           //g.DrawPath(Pens.Green, stars[i]);
       }
       g.Restore(gs);
    }        
    Color ratedStarColor;
    Color grayStarColor;
    float starScale;
    public Color RatedStarColor {
        get { return ratedStarColor; }
        set {
            if (ratedStarColor != value) {
                ratedStarColor = value;
                var col = OwningColumn as DataGridViewRatingColumn;
                if (col != null && col.RatedStarColor != value) {
                    UseColumnStarColor = false;
                    DataGridView.InvalidateCell(this);
                }
            }
        }
    }
    public Color GrayStarColor {
        get { return grayStarColor; }
        set {
            if (grayStarColor != value) {
                grayStarColor = value;
                var col = OwningColumn as DataGridViewRatingColumn;
                if (col != null && col.GrayStarColor != value) {
                    UseColumnStarColor = false;
                    DataGridView.InvalidateCell(this);
                }
            }
        }
    }
    //Change the star size via scaling factor (default by 1)
    public float StarScale {
        get { return starScale; }
        set {
            if (starScale != value) {
                starScale = value;
                var col = OwningColumn as DataGridViewRatingColumn;
                if (col != null && col.StarScale != value) {
                    UseColumnStarScale = false;                        
                    DataGridView.InvalidateCell(this);                        
                }
            }
        }
    }
    public bool UseColumnStarColor { get; set; }
    public bool UseColumnStarScale { get; set; }
}

注意:2个类DataGridViewRatingColumnDataGridViewRatingCell应该放在同一个文件中,因为我在{{1}中声明了一个静态内部方法UpdateBrushes并且在类DataGridViewRatingCell中使用它,如果要将它们放在单独的文件中,可以更改修饰符。查看提供的属性并使用它们来自定义星星的外观和感觉。它们以不言自明的方式命名。以下是一些显示用法的代码:

DataGridViewRatingColumn

enter image description here

答案 1 :(得分:2)

在datagridview中显示星级的最佳方式 使用unicode char

private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
    {
        if (e.ColumnIndex == dataGridView1.Columns["Rating"].Index
          && e.Value != null)
        {
            switch (e.Value.ToString())
            {
                case "1":
                    e.CellStyle.SelectionForeColor = Color.Red;
                    e.CellStyle.ForeColor = Color.Red;
                    //e.CellStyle.Font
                    e.Value = (char)9733;
                    break;
                case "2":
                    e.CellStyle.SelectionForeColor = Color.Brown;
                    e.CellStyle.ForeColor = Color.Yellow;
                    e.Value = (char)9733;
                    break;
                case "3":
                    e.CellStyle.SelectionForeColor = Color.Green;
                    e.CellStyle.ForeColor = Color.Green;
                    e.Value = (char)9733;
                    break;
                case "4":
                    e.CellStyle.SelectionForeColor = Color.Blue;
                    e.CellStyle.ForeColor = Color.Blue;
                    e.Value = (char)9733;
                    break;
                case "5":
                    e.CellStyle.SelectionForeColor = Color.Gold;
                    e.CellStyle.ForeColor = Color.Gold;
                    e.Value = (char)9733;
                    break;
            }
        }
    }