I want to color all "Unselectable" Text from combo box. How can i do this? I tried it but i am unable to do this.
My Code is Given Below:
private class ComboBoxItem
{
public int Value { get; set; }
public string Text { get; set; }
public bool Selectable { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
this.comboBox1.ValueMember = "Value";
this.comboBox1.DisplayMember = "Text";
this.comboBox1.Items.AddRange(new[] {
new ComboBoxItem() { Selectable = true, Text="Selectable0", Value=0, },
new ComboBoxItem() { Selectable = true, Text="Selectable1", Value=1},
new ComboBoxItem() { Selectable = true, Text="Selectable2", Value=2},
new ComboBoxItem() { Selectable = false, Text="Unselectable", Value=3},
new ComboBoxItem() { Selectable = true, Text="Selectable3", Value=4},
new ComboBoxItem() { Selectable = false, Text="Unselectable", Value=5},
});
this.comboBox1.SelectedIndexChanged += (cbSender, cbe) =>
{
var cb = cbSender as ComboBox;
if (cb.SelectedItem != null && cb.SelectedItem is ComboBoxItem && ((ComboBoxItem)cb.SelectedItem).Selectable == false)
{
// deselect item
cb.SelectedIndex = -1;
}
};
}
I am working in C#.NET.
You need to set the foreground property on the ComboBoxItem to the colour you require.
new ComboBoxItem() { Selectable = false, Text="Unselectable", Value=3, Foreground = Brushes.Red},
MSDN page
You need to set the ComboBox.DrawMode to OwnerDrawxxx and script the DrawItem event e.g. like this:
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
// skip without valid index
if (e.Index >= 0)
{
ComboBoxItem cbi = (ComboBoxItem)comboBox1.Items[e.Index];
Graphics g = e.Graphics;
Brush brush = new SolidBrush(e.BackColor);
Brush tBrush = new SolidBrush(cbi.Text == "Unselectable" ? Color.Red : e.ForeColor);
g.FillRectangle(brush, e.Bounds);
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), e.Font,
tBrush, e.Bounds, StringFormat.GenericDefault);
brush.Dispose();
tBrush.Dispose();
}
e.DrawFocusRectangle();
}
This part cbi.Text == "Unselectable"is not good, obviously. Since you already have a Property Selectable it should really read !cbi.Selectable". Off course you must make sure that the property is in synch with the text.
Related
I'd like to create a comboBox in visual studio 2019 as presented as shown,
How can I extract the images from the ChartType ComboBox and show list of ChartType in my ComboBox with the images?
The following is my code without the use of additional classes. This is done with the help of CobyC Answer.
private List<string> dataSourceNames = new List<string>();
private List<Bitmap> dataSourceImage = new List<Bitmap>();
private void loadCombobox1()
{
// Get ChartTypes and Images
var resourceStream = typeof(Chart).Assembly
.GetManifestResourceStream("System.Windows.Forms.DataVisualization.Charting.Design.resources");
using (System.Resources.ResourceReader resReader = new ResourceReader(resourceStream))
{
var dictEnumerator = resReader.GetEnumerator();
while (dictEnumerator.MoveNext())
{
var ent = dictEnumerator.Entry;
dataSourceNames.Add(ent.Key.ToString());
dataSourceImage.Add(ent.Value as Bitmap);
}
}
//Load ChartType Into combobox
comboBox1.DataSource = dataSourceNames;
comboBox1.MaxDropDownItems = 10;
comboBox1.IntegralHeight = false;
comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox1.DrawItem += comboBox1_DrawItem;
}
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index >= 0)
{
// Get text string
var txt = comboBox1.GetItemText(comboBox1.Items[e.Index]);
// Specify points for drawing
var r1 = new Rectangle(e.Bounds.Left + 1, e.Bounds.Top + 1,
2 * (e.Bounds.Height - 2), e.Bounds.Height - 2);
var r2 = Rectangle.FromLTRB(r1.Right + 2, e.Bounds.Top,
e.Bounds.Right, e.Bounds.Bottom);
//Draw Image from list
e.Graphics.DrawImage(dataSourceImage[e.Index], r1);
e.Graphics.DrawRectangle(Pens.Black, r1);
TextRenderer.DrawText(e.Graphics, txt, comboBox1.Font, r2,
comboBox1.ForeColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
}
}
To get the images you need to extract them from the embedded resources within the compiled .Net assembly. You will need to add using System.Resources; to your using statements.
Get the manifest stream System.Windows.Forms.DataVisualization.Charting.Design.resources from the assembly containing the Chart System.Windows.Forms.DataVisualization.Charting.Chart
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
var resourceStream = typeof(System.Windows.Forms.DataVisualization.Charting.Chart)
.Assembly.GetManifestResourceStream("System.Windows.Forms.DataVisualization.Charting.Design.resources");
using (System.Resources.ResourceReader resReader = new ResourceReader(resourceStream))
{
var dictEnumerator = resReader.GetEnumerator();
while (dictEnumerator.MoveNext())
{
var ent = dictEnumerator.Entry;
imageList1.Images.Add($"{ent.Key}", ent.Value as Bitmap);
listView1.Items.Add(new ListViewItem($"{ent.Key}", $"{ent.Key}"));
}
}
}
}
For simplicity I just added it to a ImageList linked to a ListView.
listView1.View = View.LargeIcon;
listView1.LargeImageList = imageList1;
listView1.SmallImageList = imageList1
This is the outcome.
To create a combo box with a drop-down
For the combo I looked at this questions
The code:
In the while block
...
...
while (dictEnumerator.MoveNext())
{
var ent = dictEnumerator.Entry;
chartSelector1.Items.Add(new ChartDropDownItem($"{ent.Key}",ent.Value as Bitmap));
}
...
...
and the additional classes: (This will create a ChartSelector control in your toolbox after building the project)
public class ChartDropDownItem
{
public string Value { get; set; }
public Image Image { get; set; }
public ChartDropDownItem(string val, Bitmap img)
{
Value = val;
Image = img;
}
}
public class ChartSelector : ComboBox
{
public ChartSelector()
{
DrawMode = DrawMode.OwnerDrawFixed;
DropDownStyle = ComboBoxStyle.DropDownList;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
if (e.Index >= 0 && e.Index < Items.Count)
{
ChartDropDownItem item = (ChartDropDownItem)Items[e.Index];
e.Graphics.DrawImage(item.Image, e.Bounds.Left, e.Bounds.Top);
e.Graphics.DrawString(item.Value, e.Font, new SolidBrush(e.ForeColor), e.Bounds.Left + item.Image.Width, e.Bounds.Top + 2);
}
base.OnDrawItem(e);
}
}
and that looks like this:
I want to create GUI application using .NET and I need to implement such kind of scrollable list with custom items. All items should grouped to 3 different groups and to include images, text and favourite star button as shown here:
I would also like to implement filtering using search box, but once I would be able to create such list with customizable items then I hope it would be much easier.
I tried using .NET list view, but I want each cell to look like 1 cell - with out any borders between the image, text, image.
I would extremely appreciate any thoughts about this manner!
The best way would be making control template for either WPF ListView or ListBox control.
For example, ListBox.ItemTemplate allows for custom item look.
If you would like to use 3rd party components, Better ListView allows this using owner drawing (requires subclassing BetterListView):
Here is a setup code for the control:
this.customListView = new CustomListView
{
Dock = DockStyle.Fill,
Parent = this
};
this.customListView.BeginUpdate();
for (int i = 0; i < 6; i++)
{
var item = new BetterListViewItem
{
Image = imageGraph,
Text = String.Format("Item no. {0}", i)
};
this.customListView.Items.Add(item);
}
var group1 = new BetterListViewGroup("First group");
var group2 = new BetterListViewGroup("Second group");
var group3 = new BetterListViewGroup("Third group");
this.customListView.Groups.AddRange(new[] { group1, group2, group3 });
this.customListView.Items[0].Group = group1;
this.customListView.Items[1].Group = group1;
this.customListView.Items[2].Group = group2;
this.customListView.Items[3].Group = group2;
this.customListView.Items[4].Group = group2;
this.customListView.Items[5].Group = group3;
this.customListView.AutoSizeItemsInDetailsView = true;
this.customListView.GroupHeaderBehavior = BetterListViewGroupHeaderBehavior.None;
this.customListView.ShowGroups = true;
this.customListView.LayoutItemsCurrent.ElementOuterPadding = new Size(0, 8);
this.customListView.EndUpdate();
and CustomListView class (implements custom drawing and interaction with star icons):
using BetterListView;
internal sealed class CustomListView : BetterListView
{
private const int IndexUndefined = -1;
private Image imageStarNormal;
private Image imageStarHighlight;
private int lastStarIndex = IndexUndefined;
private int currentStarIndex = IndexUndefined;
public CustomListView()
{
this.imageStarNormal = Image.FromFile("icon-star-normal.png");
this.imageStarHighlight = Image.FromFile("icon-star-highlight.png");
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var item = HitTest(e.Location).ItemDisplay;
if (item == null)
{
return;
}
var bounds = GetItemBounds(item);
if (bounds == null)
{
return;
}
Rectangle boundsStar = GetStarBounds(bounds);
UpdateStarIndex(boundsStar.Contains(e.Location)
? item.Index
: IndexUndefined);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
UpdateStarIndex(IndexUndefined);
}
protected override void OnDrawGroup(BetterListViewDrawGroupEventArgs eventArgs)
{
eventArgs.DrawSeparator = false;
base.OnDrawGroup(eventArgs);
}
protected override void OnDrawItem(BetterListViewDrawItemEventArgs eventArgs)
{
base.OnDrawItem(eventArgs);
Graphics g = eventArgs.Graphics;
BetterListViewItemBounds bounds = eventArgs.ItemBounds;
int imageWidth = this.imageStarNormal.Width;
int imageHeight = this.imageStarNormal.Height;
g.DrawImage(
(this.currentStarIndex == eventArgs.Item.Index) ? this.imageStarHighlight : this.imageStarNormal,
GetStarBounds(bounds),
0, 0, imageWidth, imageHeight,
GraphicsUnit.Pixel);
Rectangle boundsSelection = bounds.BoundsSelection;
g.DrawRectangle(
Pens.Gray,
new Rectangle(boundsSelection.Left, boundsSelection.Top, boundsSelection.Width - 1, boundsSelection.Height - 1));
}
private void UpdateStarIndex(int starIndex)
{
if (starIndex == this.lastStarIndex)
{
return;
}
bool isUpdated = false;
if (this.lastStarIndex != IndexUndefined)
{
Items[this.lastStarIndex].Invalidate();
isUpdated = true;
}
if (starIndex != IndexUndefined)
{
Items[starIndex].Invalidate();
isUpdated = true;
}
this.lastStarIndex = this.currentStarIndex;
this.currentStarIndex = starIndex;
if (isUpdated)
{
RedrawItems();
}
}
private Rectangle GetStarBounds(BetterListViewItemBounds bounds)
{
Rectangle rectInner = bounds.BoundsInner;
int widthImage = this.imageStarNormal.Width;
int heightImage = this.imageStarNormal.Height;
return (new Rectangle(
rectInner.Width - widthImage,
rectInner.Top + ((rectInner.Height - heightImage) >> 1),
widthImage,
heightImage));
}
}
I am trying to draw items in a ComboBoxCell in a DataGridView using the DrawItem Event. Following is my code.
Updated Code:
private void dgv_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
int index = dgv.CurrentCell.ColumnIndex;
if (index == FormatColumnIndex)
{
var combobox = e.Control as ComboBox;
if (combobox == null)
return;
combobox.DrawMode = DrawMode.OwnerDrawFixed;
combobox.DrawItem -= combobox_DrawItem;
combobox.DrawItem += new DrawItemEventHandler(combobox_DrawItem);
}
}
void combobox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0)
{
return;
}
int index = dgv.CurrentCell.RowIndex;
if (index == e.Index)
{
DataGridViewComboBoxCell cmbcell = (DataGridViewComboBoxCell)dgv.CurrentRow.Cells["ProductFormat"];
string productID = dgv.Rows[cmbcell.RowIndex].Cells["ProductID"].Value.ToString();
string item = cmbcell.Items[e.Index].ToString();
if (item != null)
{
Font font = new System.Drawing.Font(FontFamily.GenericSansSerif, 8);
Brush backgroundColor;
Brush textColor;
if (e.State == DrawItemState.Selected)
{
backgroundColor = SystemBrushes.Highlight;
textColor = SystemBrushes.HighlightText;
}
else
{
backgroundColor = SystemBrushes.Window;
textColor = SystemBrushes.WindowText;
}
if (item == "Preferred" || item == "Other")
{
font = new Font(font, FontStyle.Bold);
backgroundColor = SystemBrushes.Window;
textColor = SystemBrushes.WindowText;
}
if (item != "Select" && item != "Preferred" && item != "Other")
e.Graphics.DrawString(item, font, textColor, new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));
else
e.Graphics.DrawString(item, font, textColor, e.Bounds);
}
}
}
}
The items are displayed properly, but the dropdown seems out of place and looks awkward.
Also when I hover over the dropdown items, they seem to be painted over again which makes them look darker and blurred. How can I fix this? Thanks.
Your drawing routine looks like it is confusing the RowIndex of the grid with the e.Index of the ComboBox items collection. Different things.
Try removing this:
// int index = dgv.CurrentCell.RowIndex;
// if (index == e.Index) {
As far as the blurriness is concerned, add the following line to fix that:
void combobox_DrawItem(object sender, DrawItemEventArgs e) {
e.DrawBackground();
I had the same problem and fixed it by setting the TextRenderingHint property of e.Graphics to SingleBitPerPixelGridFit before calling DrawString:
e.DrawBackground()
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit
e.Graphics.DrawString(lstr_h, e.Font, lbrush_fore, e.Bounds)
e.DrawFocusRectangle()
public static Boolean TextBoxValidation(TextBox txt, String AdditionalMsg)
{
if (txt.Text.Trim() == "")
{
MessageBox.Show("Please Enter " + AdditionalMsg);
return false;
}
return true;
}
This is my code; when the user does not fill some entry then a message is shown. I want something more creative: when the user does not fill some entry into the textbox, a red border blinks around my textbox and a message is shown to user just like a tooltip.
Refer to the picture I have uploaded:
I kind of wanted something like that for a while and this is what I come up with:
Since you can't set border color for TextBox, I made a UserControl with textbox inside:
public partial class UCTextBoxCustomcs : UserControl
{
private ToolTip _errorToolTip;
// keep original background color so you can change it when txet value is OK
private Color _orgBgColor;
public new Color BackColor
{
get { return _orgBgColor; }
set
{
base.BackColor = value;
_orgBgColor = value;
}
}
public new string Text
{
get { return this.txbContent.Text; }
set { this.txbContent.Text = value; }
}
public Color InvalidBgColor { get; set; }
private bool _IsValid;
public bool IsValid
{
get { return _IsValid; }
set
{
_IsValid = value;
if (value)
{
base.BackColor = _orgBgColor;
_errorToolTip.SetToolTip(this.txbContent, "");
_errorToolTip.ShowAlways = false;
_errorToolTip.Hide(this.txbContent);
}
else
{
base.BackColor = InvalidBgColor;
_errorToolTip.ShowAlways = true;
this._errorToolTip.BackColor = InvalidBgColor;
_errorToolTip.Show(this.ErrorText, this.txbContent,this.txbContent.Width +3 ,0);
}
}
}
private string _ErrorText;
public string ErrorText
{
get
{
return _ErrorText;
}
set
{
_ErrorText = value;
if (value == null || value.Length == 0) IsValid = true;
else IsValid = false;
}
}
public UCTextBoxCustomcs()
{
this._errorToolTip = new ToolTip();
// BackColor in ToolTip is ignored, so if you want to change it,
// you have to draw it yourself
this._errorToolTip.OwnerDraw = true;
_errorToolTip.Draw += new DrawToolTipEventHandler(_errorToolTip_Draw);
_errorToolTip.Popup += new PopupEventHandler(_errorToolTip_Popup);
// white background so it looks like TextBox
this.BackColor = Color.White;
InitializeComponent();
this.txbContent.BorderStyle = BorderStyle.None;
// Intelisense tells you this property isn't there, but it is
// you have to set it to false so TextBox height can be changed
// when MultiLine is set to false
this.txbContent.AutoSize = false;
this.txbContent.Multiline = false;
// Leave 1 pixel around TextBox for pseudo-border
this.Padding = new Padding(1);
this.txbContent.Dock = DockStyle.Fill;
this.InvalidBgColor = Color.Red;
this.IsValid = true;
}
void _errorToolTip_Popup(object sender, PopupEventArgs e)
{
using (Font f = new Font("Calibri", 9))
{
Size ttSize = TextRenderer.MeasureText(
_errorToolTip.GetToolTip(e.AssociatedControl), f);
e.ToolTipSize = new Size(ttSize.Width + 6, ttSize.Height + 6);
}
}
void _errorToolTip_Draw(object sender, DrawToolTipEventArgs e)
{
// In this case a simple rectangle is drawn, but you can draw whatever you want
// Draw the custom background.
e.Graphics.FillRectangle(new SolidBrush(this.InvalidBgColor), e.Bounds);
// Draw the standard border.
e.DrawBorder();
// Draw the custom text.
// The using block will dispose the StringFormat automatically.
using (StringFormat sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
sf.FormatFlags = StringFormatFlags.NoWrap;
using (Font f = new Font("Calibri", 9))
{
Rectangle textBounds = new Rectangle(
e.Bounds.Left+3,
e.Bounds.Top+3,
e.Bounds.Width-6,
e.Bounds.Height-6);
e.Graphics.DrawString(e.ToolTipText, f,
SystemBrushes.ActiveCaptionText, e.Bounds, sf);
}
}
}
protected override void OnValidating(CancelEventArgs e)
{
this.ValidateChildren();
base.OnValidating(e);
}
}
How to use:
private void ucTextBoxCustomcs1_Validating(object sender, CancelEventArgs e)
{
if (ucTextBoxCustomcs1.Text.Length == 0)
{
ucTextBoxCustomcs1.ErrorText = "Cant be empty";
}
else ucTextBoxCustomcs1.ErrorText = null;
}
It looks like this:
I am trying to work around changing the color of highlighting in a ComboBox dropdown on a C# Windows Forms application.
I have searched the whole web for an answer, and the best option i found so far was to draw a rectangle of the desired color when the item that is selected is being drawn.
Class Search
{
Public Search()
{
}
private void addFilter()
{
ComboBox field = new ComboBox();
field.Items.AddRange(new string[] { "Item1", "item2" });
field.Text = "Item1";
field.DropDownStyle = ComboBoxStyle.DropDownList;
field.FlatStyle = FlatStyle.Flat;
field.BackColor = Color.FromArgb(235, 235, 235);
field.DrawMode = DrawMode.OwnerDrawFixed;
field.DrawItem += field_DrawItem;
}
private void field_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index >= 0)
{
ComboBox combo = sender as ComboBox;
if (e.Index == combo.SelectedIndex)
e.Graphics.FillRectangle(new SolidBrush(Color.Gray),
e.Bounds
);
else
e.Graphics.FillRectangle(new SolidBrush(combo.BackColor),
e.Bounds
);
e.Graphics.DrawString(combo.Items[e.Index].ToString(), e.Font,
new SolidBrush(combo.ForeColor),
new Point(e.Bounds.X, e.Bounds.Y)
);
}
}
}
The problem with this code, is that once another Item in the dropdown is selected, the other Item I draw a rectangle is still with the color i want to highlight.
Then i tried to save the last Item drawn and redraw it:
Class Search
{
private DrawItemEventArgs lastDrawn;
Public Search()
{
lastDrawn = null;
}
private void addFilter()
{
ComboBox field = new ComboBox();
field.Items.AddRange(new string[] { "Item1", "item2" });
field.Text = "Item1";
field.DropDownStyle = ComboBoxStyle.DropDownList;
field.FlatStyle = FlatStyle.Flat;
field.BackColor = Color.FromArgb(235, 235, 235);
field.DrawMode = DrawMode.OwnerDrawFixed;
field.DrawItem += field_DrawItem;
}
private void field_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index >= 0)
{
ComboBox combo = sender as ComboBox;
if (e.Index == combo.SelectedIndex)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Gray), e.Bounds);
if(lastDrawn != null)
lastDrawn.Graphics.FillRectangle(new SolidBrush(combo.BackColor),
lastDrawn.Bounds
);
lastDrawn = e;
}
else
e.Graphics.FillRectangle(new SolidBrush(combo.BackColor),
e.Bounds
);
e.Graphics.DrawString(combo.Items[e.Index].ToString(), e.Font,
new SolidBrush(combo.ForeColor),
new Point(e.Bounds.X, e.Bounds.Y)
);
}
}
}
This line returns an error because of lastDrawn.Bounds (incompatible type)
lastDrawn.Graphics.FillRectangle(new SolidBrush(combo.BackColor),
lastDrawn.Bounds
);
I am feeling that changing the highlight color of the dropdown is impossible.
Thanks in advance!
In case you are using the ComboBox in more than one place in your project, it will not make sense to repeat the same code for DrawItem event over and over again. Just add this class to your project and you will have a new ComboBox control that has the HightlightColor property which will makes it easier to use the control all over the project:
class AdvancedComboBox : ComboBox
{
new public System.Windows.Forms.DrawMode DrawMode { get; set; }
public Color HighlightColor { get; set; }
public AdvancedComboBox()
{
base.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
this.HighlightColor = Color.Gray;
this.DrawItem += new DrawItemEventHandler(AdvancedComboBox_DrawItem);
}
void AdvancedComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0)
return;
ComboBox combo = sender as ComboBox;
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
e.Graphics.FillRectangle(new SolidBrush(HighlightColor),
e.Bounds);
else
e.Graphics.FillRectangle(new SolidBrush(combo.BackColor),
e.Bounds);
e.Graphics.DrawString(combo.Items[e.Index].ToString(), e.Font,
new SolidBrush(combo.ForeColor),
new Point(e.Bounds.X, e.Bounds.Y));
e.DrawFocusRectangle();
}
}