Страницы

Поиск по вопросам

Показаны сообщения с ярлыком devexpress. Показать все сообщения
Показаны сообщения с ярлыком devexpress. Показать все сообщения

четверг, 23 января 2020 г.

Есть ли способ стилизации системных диалогов через DevExpress под WinForms?

#c_sharp #winforms #devexpress


Доброго дня!

При использовании DevExpress для WinForms есть возможность менять внешний вид приложения
посредством LookAndFeel, но при этом все системные диалоги, например, OpenFileDialog
остаются неизменными (не стилизованными).

Очень интересно, имеется ли возможность средствами самой библиотеки DevExpress привести
внешний вид диалогов в соответствие с внешним видом самого приложения? Если да, то
как это сделать? 

Грубо говоря, если у приложения используется тёмный скин, то и унифицированный вид
диалога тоже должен быть тёмным.
    


Ответы

Ответ 1



К сожалению, нет решения "из коробки". Ждем уже 11 лет: DevExpress Support Center.

среда, 22 января 2020 г.

Как изменить цвет(прогресса/фона) ProgressBarEdit?

#c_sharp #wpf #devexpress


На сайте DevExpress в документации для v.17.2 (я использую DevExpress v15.2, но другой
нет и раньше отличий не проявлялось) указано, что якобы цвет некой левой части задаётся
свойством AdditionalForeground.


  ProgressBarEdit.AdditionalForeground Property.
  
  Specifies the color of the left side of the ProgressBarEdit. This is a dependency
property.


а цвет некой правой части задаёт Foreground.


  To specify the color of the right side of the ProgressBarEdit, use the Foreground
property.


Проблема в том, что оно не работает — только Foreground задаёт цвет текста, отображаемого
поверх контрола; задание св-ва Background тоже ничего не меняет.
Т__Т  

XAML:


    
        
    





Подскажите, как изменить цвет(прогресса/фона)? Либо что тогда за левая/правая части
имелись в виду?
    


Ответы

Ответ 1



Необходимо переопределить PanelBackgroundHorizontalTemplate и PanelLeftSideHorizontalTemplate, чтобы изменить цвет для индикатора выполнения и управления фоном в элементе управления ProgressBarEdit. Пример кода: Ссылка на источник: Can't change color of BAR in ProgessBarEdit А также: Change ProgressBarEdit Color doesn't work How to change ProgressBarEdit's progress color depending on the Value property ProgressBarEdit Color

воскресенье, 12 января 2020 г.

Как вызвать статический метод класса возврающающий экземляр для использования Unity?

#c_sharp #devexpress #unity_container


Есть базовый интерфейсIViewModel для всех моих ViewModel. Все ViewModel используют
DevExpress POCO механизм:

public static EfCaseCollectionViewModel Create(IUnitOfWorkFactory
unitOfWorkFactory = null)
{
    return ViewModelSource.Create(() => new EfCaseCollectionViewModel(unitOfWorkFactory));
}
protected EfCaseCollectionViewModel(IUnitOfWorkFactory
unitOfWorkFactory = null)
        : base(x => x.Cases, unitOfWorkFactory ?? UnitOfWorkSource.GetUnitOfWorkFactory(),projection:t=>t.Where(x=>x.Removed==false))
{
}


Во ViewModel не используется без параметрический конструктор.

Unity контейнер я создаю при старте в статическом классе:

UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType("CaseCollection");


Во View:

public EfCaseCollectionView(IUnityContainer container)
{
    this.DataContext = GetViewModel(container);
    InitializeComponent();
}

private IViewModel GetViewModel(IUnityContainer container)
{
    if (container.IsRegistered("CaseCollection"))
    {
        return container.Resolve("CaseCollection");
    }
    return container.Resolve();
}


Но без конструктора без параметров это не работает. (MissingMethodException:'System.MissingMethodException:
No parameterless constructor defined for this object.). Как правильно использовать
Unity в этой ситуации?
    


Ответы

Ответ 1



Спасибо за ссылку, помогла как пища для размышлений. Честно, часть вопроса связанная с Exception не связана с основной частью тем что я просто не уследил что в CodeBehind'е View UserControl нету конструктора без параметров. По поводу основной части. Сделал так: public static readonly UnityContainer UnityContainer = new UnityContainer(); public static void Resolve() { UnityContainer.RegisterType("CaseCollection", new InjectionFactory(c => EfCaseCollectionViewModel.Create())); } Этот метод вызывается в App.xaml.cs А в View Так: public EfCaseCollectionView() { InitializeComponent(); this.DataContext = GetViewModel(Bootstrapper.UnityContainer); } private IViewModel GetViewModel(IUnityContainer container) { if (container.IsRegistered("CaseCollection")) { return container.Resolve("CaseCollection"); } //if not found, get default factory return container.Resolve(); } Я не уверен что решение правильное, но мою текущую задачу оно выполнило.

суббота, 11 января 2020 г.

Как получить индекс строки в WinForms, DevExpress

#c_sharp #winforms #devexpress


Как получить индекс или значения выбранной строки в gridControl от DevExpress в WinForms?
В стандартном datagridview делаю так =  

gridOrder.SelectedRows[i].Cells["order_number"].Value


Хочу заменить стандартный datagridview на gridControl, но встречаю много отличий,
думал они будут идентичный просто gridControl более функционален, а получается они
почти разные? Может есть документация на русском у кого-то для него?
    


Ответы

Ответ 1



Используйте метод ColumnView.GetSelectedRows для получения индексов выделенных строк: int[] rowHandles = gridView1.GetSelectedRows(); Если множественное выделение отключено(по умолчанию или с помощью опции режима выделения ColumnView.OptionsSelection.MultiSelect) используйте свойство ColumnView.FocusedRowHandle: int rowHandle = gridView1.FocusedRowHandle; Получить же сами значения ячеек или объект строки целиком можно с помощью методов ColumnView.GetRowCellValue/ColumnView.GetRow: int number = (int)gridView1.GetRowCellValue(rowHandle, "order_number"); //... Order order = (Order)gridView1.GetRow(rowHandle); Для сфокусированной строки есть "сокращенные" методы GetFocusedRow/GetFocusedRowCellValue p.s. Полезные ссылки: Focus and Selection Handling Obtaining and Setting Cell Values p.p.s. Как верно заметили в комментариях, MVVM можно использовать и в WinForms, и как раз DevExpress является единственным вендором, предоставляющим поддержку MVVM на уровне WinForms контролов из коробки. Для случая с выделенной строкой код биндинга может выглядеть вот так: // привязка вьюмодели списка заказов к гриду var fluent = mvvmContext1.OfType(); // привязка индикатора загрузки(если во вьюмодели есть асинхронка) fluent.SetBinding(gridView1, gView => gView.LoadingPanelVisible, x => x.IsLoading); fluent.SetBinding(gridControl1, gControl => gControl.DataSource, x => x.Entities); // привязка выделенной строки fluent.WithEvent(gridView1, "FocusedRowObjectChanged") .SetBinding(x => x.SelectedEntity, args => args.Row as Order, (gView, entity) => gView.FocusedRowHandle = gView.FindRow(entity));

воскресенье, 8 декабря 2019 г.

DevExpress XtraReport.LoadLayout падает на Windows10

#c_sharp #net #администрирование #windows_10 #devexpress


Проблема возникает на некоторых машинах где установлена Windows 10. Что интересно,
то на большинстве машин всё работает, но на некоторых появляется ошибка. 



Текст ошибки говорит о том, что не найдена dll, которая по всей видимости создается
DevExpress динамически, в темповую папку. Но почему то в конкретном случае это не происходит.
Причем я пробовал создавать файлы в этой директории и файлы создаются. Я не системный
администратор и не знаю тонкостей, но права у пользователя максимальные, которыя я
смог задать.Видно что падает в методе 

XtraReport.LoadLayout(String) Method


Я задал этот вопрос на форуме DevExpress.ссылка на вопрос. Но предварительно просмотрев
гугл и  тот же форум, предполагаю, что они открестятся от него. На самом деле я даже
не уверен, что эта их проблема, поэтому пишу сюда! Если кто раньше встречался с подобной
проблемой, откликнитесь! Буду очень признателен за помощь! Пишите в комментариях если
нужна какая-либо дополнительная информация!
    


Ответы

Ответ 1



Проблема достаточно понятна - когда писали программу (скорее всего компонент XtraReport) путь для копирования dll в одном месте программы задали жестко (например что-то типа C:\Windows\Temp), а в другом - запрашивали путь у операционной системы. Даже если это ошибка DevExpress, как показывает мой опыт чинить они вряд ли будут (в лучшем случае это займет немало времени) и нужна будет актуальная лицензия и пересборка основной программы. У меня была похожая ошибка с этим компонентом, но не с dll, а с бланком отчета - после экспорта XtraReport использовал путь к шаблону не тот что указан в программе, а папку экспорта. Возможные решения: Попробуйте найти эту dll (например на машине с другой версией Windows) и положить в папку C:\Users\Admin\AppData\Local\Temp\ Если это поможет, вы можете сделать симлинк на эту dll из другой папки. Если dll не имеет постоянного имени (меняется при каждом отчете), вам придется схитрить - програмно отлавливать новые dll там где они появляются (скорее всего это C:\Windows\Temp\, но надо отследить) и копировать их в C:\Users\Admin\AppData\Local\Temp\ Это может сделать достаточно простая программа. Попробуйте запустить программу в режиме эмуляции Windows XP. Возможно это подставит старый формат пути в программу. В любом случае начинать надо с поисков что это за dll и куда она сохраняется. Если не находите - посмотрите ее не под Windows 10.

воскресенье, 1 декабря 2019 г.

DataGridView+ сторонний контрол

#c_sharp #winforms #datagridview #devexpress


Подскажите, пожалуйста, возможно ли увязать сторонний контролл с DatagridView? В
частности, RatingControl от DevExpress. Пример ниже:


    


Ответы

Ответ 1



На просторах большого 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); Возможно перевод не идеален, поэтому заранее прошу прощения. Главное, что суть ответа ясна и все довольно подробно изложено. Мелкие комментарии в коде переводить не стал.

вторник, 9 июля 2019 г.

Как добавить DevExpress в проект

Мне нужно использовать
using DevExpress.Data.Filtering; using DevExpress.Xpf.Grid;
как добавить это в проект? Через Nugget? Как понять что именно из списка в Nugget мне надо, или гуглить dll?


Ответ

Ну вообще вам нужно установить DevExpress, при установки dll пропишутся в GAC, а потом вы уже добавите нужные вам непосредсвенно в проекте. триальная версия.

среда, 13 марта 2019 г.

Есть ли способ стилизации системных диалогов через DevExpress под WinForms?

Доброго дня!
При использовании DevExpress для WinForms есть возможность менять внешний вид приложения посредством LookAndFeel, но при этом все системные диалоги, например, OpenFileDialog остаются неизменными (не стилизованными).
Очень интересно, имеется ли возможность средствами самой библиотеки DevExpress привести внешний вид диалогов в соответствие с внешним видом самого приложения? Если да, то как это сделать?
Грубо говоря, если у приложения используется тёмный скин, то и унифицированный вид диалога тоже должен быть тёмным.


Ответ

К сожалению, нет решения "из коробки". Ждем уже 11 лет: DevExpress Support Center

вторник, 12 марта 2019 г.

Как изменить цвет(прогресса/фона) ProgressBarEdit?

На сайте DevExpress в документации для v.17.2 (я использую DevExpress v15.2, но другой нет и раньше отличий не проявлялось) указано, что якобы цвет некой левой части задаётся свойством AdditionalForeground
ProgressBarEdit.AdditionalForeground Property. Specifies the color of the left side of the ProgressBarEdit. This is a dependency property.
а цвет некой правой части задаёт Foreground
To specify the color of the right side of the ProgressBarEdit, use the Foreground property.
Проблема в том, что оно не работает — только Foreground задаёт цвет текста, отображаемого поверх контрола; задание св-ва Background тоже ничего не меняет. Т__Т
XAML:


Подскажите, как изменить цвет(прогресса/фона)? Либо что тогда за левая/правая части имелись в виду?


Ответ

Необходимо переопределить PanelBackgroundHorizontalTemplate и PanelLeftSideHorizontalTemplate, чтобы изменить цвет для индикатора выполнения и управления фоном в элементе управления ProgressBarEdit.
Пример кода:








Ссылка на источник: Can't change color of BAR in ProgessBarEdit
А также:
Change ProgressBarEdit Color doesn't work How to change ProgressBarEdit's progress color depending on the Value property ProgressBarEdit Color

четверг, 7 марта 2019 г.

Как вызвать статический метод класса возврающающий экземляр для использования Unity?

Есть базовый интерфейсIViewModel для всех моих ViewModel. Все ViewModel используют DevExpress POCO механизм:
public static EfCaseCollectionViewModel Create(IUnitOfWorkFactory unitOfWorkFactory = null) { return ViewModelSource.Create(() => new EfCaseCollectionViewModel(unitOfWorkFactory)); } protected EfCaseCollectionViewModel(IUnitOfWorkFactory unitOfWorkFactory = null) : base(x => x.Cases, unitOfWorkFactory ?? UnitOfWorkSource.GetUnitOfWorkFactory(),projection:t=>t.Where(x=>x.Removed==false)) { }
Во ViewModel не используется без параметрический конструктор.
Unity контейнер я создаю при старте в статическом классе:
UnityContainer unityContainer = new UnityContainer(); unityContainer.RegisterType("CaseCollection");
Во View
public EfCaseCollectionView(IUnityContainer container) { this.DataContext = GetViewModel(container); InitializeComponent(); }
private IViewModel GetViewModel(IUnityContainer container) { if (container.IsRegistered("CaseCollection")) { return container.Resolve("CaseCollection"); } return container.Resolve(); }
Но без конструктора без параметров это не работает. (MissingMethodException:'System.MissingMethodException: No parameterless constructor defined for this object.). Как правильно использовать Unity в этой ситуации?


Ответ

Спасибо за ссылку, помогла как пища для размышлений.
Честно, часть вопроса связанная с Exception не связана с основной частью тем что я просто не уследил что в CodeBehind'е View UserControl нету конструктора без параметров.
По поводу основной части. Сделал так:
public static readonly UnityContainer UnityContainer = new UnityContainer();
public static void Resolve() { UnityContainer.RegisterType("CaseCollection", new InjectionFactory(c => EfCaseCollectionViewModel.Create())); }
Этот метод вызывается в App.xaml.cs
А в View Так:
public EfCaseCollectionView() { InitializeComponent(); this.DataContext = GetViewModel(Bootstrapper.UnityContainer); }
private IViewModel GetViewModel(IUnityContainer container) { if (container.IsRegistered("CaseCollection")) { return container.Resolve("CaseCollection"); } //if not found, get default factory return container.Resolve(); }
Я не уверен что решение правильное, но мою текущую задачу оно выполнило.

вторник, 9 октября 2018 г.

DataGridView+ сторонний контрол

Подскажите, пожалуйста, возможно ли увязать сторонний контролл с 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);
Возможно перевод не идеален, поэтому заранее прошу прощения. Главное, что суть ответа ясна и все довольно подробно изложено. Мелкие комментарии в коде переводить не стал.