Подскажите, пожалуйста, возможно ли увязать сторонний контролл с DatagridView? В частности, RatingControl от DevExpress. Пример ниже:
Ответ
На просторах большого SO был найден похожий на ваш вопрос и довольно не плохой ответ. Звучит вопрос так: «Using a custom control in a DataGridView» (Использование настраиваемого элемента управления в DataGridView). Собственно говоря, ссылка на источник: Using a custom control in a DataGridView и перевод ответа на русский язык:
Для начала необходимо вычислить общее количество ячеек отображения, и вам необходим список List чтобы хранить все необходимые элементы управления. Эти элементы управления должны иметь свой DataGridView как и их Parent. Количество этих элементов должно быть равно количеству отображаемых ячеек. Затем в обработчике события CellPainting вы должны обновить расположение всех элементов управления в списке. В обработчика события CellPainting добавим код обновления, потому что всякий раз, когда значение ячейки и границы обновляются, то CellPainting выполнится и обновит средства управления Location. Этот процесс немного сложнее, но это работает. Вы можете связать каждый элемент управления с каждой ячейкой с помощью свойства reference , у такого элемента как Tag свойство.
Подход. Вам просто нужно создать пользовательский DataGridViewCell используемый в качестве CellTemplate для обычного DataGridViewColumn. Далее довольно много кода:
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 points = new List();
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; }
}
Примечания: два класса DataGridViewRatingColumn и DataGridViewRatingCell должны быть размещены в одном и том же файле, потому что внутри объявлен статический метод UpdateBrushes в DataGridViewRatingCell используемый в классе DataGridViewRatingColumn. Можно изменить модификатор доступа, если нужно разместить их в отдельных файлах. Посмотрите на предоставленные классы, свойства и методы - их названия говорят сами за себя. Их можно использовать,
чтобы настроить внешний вид контрола (звезд). Пример кода, показывающий использование:
dataGridView1.Columns.Add(new DataGridViewRatingColumn(){
// свойства DataGridViewRatingColumn можно инициализировать здесь...
});
// Чтобы изменить ReadOnly, которое позволяет пользователю писать или нет, вам нужно
// сначала преобразовать столбец в DataGridViewRatingColumn, это поведение вызвано
// ненормальным поведением переопределения ReadOnly (поэтому пришлось использовать новый).
((DataGridViewRatingColumn)dataGridView1.Columns[0]).ReadOnly = true; (default by false)
// также вы должны включить DoubleBuffered в DataGridView для устранения мерцания
typeof(Control).GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(dataGridView1, true, null);
Возможно перевод не идеален, поэтому заранее прошу прощения. Главное, что суть ответа ясна и все довольно подробно изложено. Мелкие комментарии в коде переводить не стал.
Комментариев нет:
Отправить комментарий