This question already has answers here:
Colour Individual Items in a winforms ComboBox?
(4 answers)
Closed 8 years ago.
i was wondering if it is possible to create a combo box (drop down list) and to paint specific cells in the drop down list.
so, if i have five items in the drop down list, the second item and the last item will be painted in blue for example and the others in gray..
is it possible?
System.Windows.Forms.ComboBox comboBox;
comboBox = new System.Windows.Forms.ComboBox();
comboBox.AllowDrop = true;
comboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
comboBox.FormattingEnabled = true;
comboBox.Items.AddRange(excel.getPlanNames());
comboBox.Location = new System.Drawing.Point(x, y);
comboBox.Name = "comboBox1";
comboBox.Size = new System.Drawing.Size(sizeX, sizeY);
comboBox.TabIndex = 0;
group.Controls.Add(comboBox);
thank's for any help..
You can use the DrawItem Event.
First you have to set the DrawMode of the ComboBox to OwnerDrawFixed
Then you set the DrawItem to something like the following:
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
Font font = (sender as ComboBox).Font;
Brush backgroundColor;
Brush textColor;
if (e.Index == 1 || e.Index == 3)
{
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
backgroundColor = Brushes.Red;
textColor = Brushes.Black;
}
else
{
backgroundColor = Brushes.Green;
textColor = Brushes.Black;
}
}
else
{
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
backgroundColor = SystemBrushes.Highlight;
textColor = SystemBrushes.HighlightText;
}
else
{
backgroundColor = SystemBrushes.Window;
textColor = SystemBrushes.WindowText;
}
}
e.Graphics.FillRectangle(backgroundColor, e.Bounds);
e.Graphics.DrawString((sender as ComboBox).Items[e.Index].ToString(), font, textColor, e.Bounds);
}
This example will make the default background colour Green with black text, and the highlighted item will have a red background and black text, of the items at indexes 1 and 3.
You could also set the font of individual items using the font variable.
Related
I'm trying to change the default (blue) color of selection bar in ListView.
I'm refusing to use ObjectListView since I'll have to change all the code.
I've searched on this subject and found some answers here:
Change background selection color of ListView?
but that points to ObjectListView.
When I was using ListBox before, this worked to set the selection bar color to my likings:
Set DrawMode to OwnerDrawFixed under Properties
Set DrawItem to ListBox1_DrawItem under Events
private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
//if the item state is selected them change the back color
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
e = new DrawItemEventArgs(e.Graphics,
e.Font,
e.Bounds,
e.Index,
e.State ^ DrawItemState.Selected,
e.ForeColor,
Color.FromArgb(43, 144, 188));//Choose the color
// Draw the background of the ListBox control for each item.
e.DrawBackground();
// Draw the current item text
e.Graphics.DrawString(lb_result.Items[e.Index].ToString(), e.Font, Brushes.Black, e.Bounds, StringFormat.GenericDefault);
// If the ListBox has focus, draw a focus rectangle around the selected item.
e.DrawFocusRectangle();
}
But I'm now using ListView.
I set OwnerDraw to True
I set DrawItem to ListView1_DrawItem
...and use the code from above.
I expected it to show me a different selection color as stated, but instead I get a few errors:
How would I use this code correctly for a ListView?
Owner-drawing a ListView control is more complicated than a ListBox control: many more details to take care of.
This is an example that considers four View settings of a ListView:
View.Details, View.List, View.Tile and View.SmallIcon.
Only the Text is drawn here (that's why View.LargeIcon is not included), to contain the code to a decent limit.
An example of drawing the Bitmaps included in a ImageList linked to the ListView is here.
Set up the ListView:
Enable your ListView OwnerDraw mode, then subscribe to its DrawItem, DrawSubItem and DrawColumnHeader events as shown in the sample code (mandatory, if you want the ListView to show anything).
The Headers are painted using the default rendering (setting e.DrawDefault = true).
Description of common operations:
The Item Text is drawn using TextRenderer.DrawText: this is the original method used by the ListView to draw its items. It allows to match exactly the default rendering, so we won't notice some mis-alignment of the text.
The DrawItem event is used to draw a custom background in all View modes and will draw the Items' text in all mode except View.Details, where the DrawSubItems event comes into play: we would draw the first Item's text twice, should the DrawItem event perform the same task.
The DrawSubItems event is not called when the View is set to Tile or List.
Details on the code presented here:
A helper method, GetTextAlignment, takes care of setting the Items' alignment, since each Column can have a specific text alignment.
A field, Color listViewSelectionColor, is used to set/change the Color of the select Items. To modify the selection color, set this filed to any value and Invalidate() the ListView: the new Color will be applied immediately.
Sample of the results:
bool lvEditMode = false;
Color listViewSelectionColor = Color.Orange;
protected void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
var lView = sender as ListView;
if (lvEditMode || lView.View == View.Details) return;
TextFormatFlags flags = GetTextAlignment(lView, 0);
Color itemColor = e.Item.ForeColor;
if (e.Item.Selected) {
using (var bkBrush = new SolidBrush(listViewSelectionColor)) {
e.Graphics.FillRectangle(bkBrush, e.Bounds);
}
itemColor = e.Item.BackColor;
}
else {
e.DrawBackground();
}
TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, itemColor, flags);
if (lView.View == View.Tile && e.Item.SubItems.Count > 1) {
var subItem = e.Item.SubItems[1];
flags = GetTextAlignment(lView, 1);
TextRenderer.DrawText(e.Graphics, subItem.Text, subItem.Font, e.Bounds, SystemColors.GrayText, flags);
}
}
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
var lView = sender as ListView;
TextFormatFlags flags = GetTextAlignment(lView, e.ColumnIndex);
Color itemColor = e.Item.ForeColor;
if (e.Item.Selected && !lvEditMode) {
if (e.ColumnIndex == 0 || lView.FullRowSelect) {
using (var bkgrBrush = new SolidBrush(listViewSelectionColor)) {
e.Graphics.FillRectangle(bkgrBrush, e.Bounds);
}
itemColor = e.Item.BackColor;
}
}
else {
e.DrawBackground();
}
TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, itemColor, flags);
}
protected void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
=> e.DrawDefault = true;
private TextFormatFlags GetTextAlignment(ListView lstView, int colIndex)
{
TextFormatFlags flags = (lstView.View == View.Tile)
? (colIndex == 0) ? TextFormatFlags.Default : TextFormatFlags.Bottom
: TextFormatFlags.VerticalCenter;
if (lstView.View == View.Details) flags |= TextFormatFlags.LeftAndRightPadding;
if (lstView.Columns[colIndex].TextAlign != HorizontalAlignment.Left) {
flags |= (TextFormatFlags)((int)lstView.Columns[colIndex].TextAlign ^ 3);
}
return flags;
}
private void listView1_BeforeLabelEdit(object sender, LabelEditEventArgs e) => lvEditMode = true;
private void listView1_AfterLabelEdit(object sender, LabelEditEventArgs e) => lvEditMode = false;
I'm making an interface to edit scenarios for a game. Basically it consists of events, which have nested conditions and actions. So, I planned on using two treeviews - one for selecting the event, and other for selecting the condition/action inside the event to edit.
Now, you see, if I select an event (in left treeview) and then try to select something in the right treeview, the left treeview will stop showing the blue selection rectangle. This is obviously bad because now the user doesn't know which event is he editing!
The only way I found to retain some sort of information about what is the current selection is by using SelectedImageIndex, but that's only one little image that will be different.
Is there any other way to highlight the treenode while there is no focus on the treeview? I know I can just use Graphics.DrawRectangle or something, but I heard that drawing should be done in Paint event and treeview has no paint event... So I guess if I draw it on the event of losing focus, and then drag the form out of the screen or something, it will be "erased"?
Anyway, please tell me if you got an idea (other than using a separate icon for selected and not selected treenode).
What you are looking for is the HideSelection property on the TreeView.
From MSDN:
Gets or sets a value indicating whether the selected tree node remains highlighted even when the tree view has lost the focus.
Link: http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.hideselection.aspx
Code:
TreeView.HideSelection = false;
It is still shown but only in light grey which depending on your screen and current setup can be near in visible!
Override the OnDrawNode event. So you create and new class (call it "SpecialTreeView") an inherit from the Microsoft TreeView like class SpecialTreeView : TreeView. Then you add the following event override:
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
TreeNodeStates treeState = e.State;
Font treeFont = e.Node.NodeFont ?? e.Node.TreeView.Font;
// Colors.
Color foreColor = e.Node.ForeColor;
string strDeselectedColor = #"#6B6E77", strSelectedColor = #"#94C7FC";
Color selectedColor = System.Drawing.ColorTranslator.FromHtml(strSelectedColor);
Color deselectedColor = System.Drawing.ColorTranslator.FromHtml(strDeselectedColor);
// New brush.
SolidBrush selectedTreeBrush = new SolidBrush(selectedColor);
SolidBrush deselectedTreeBrush = new SolidBrush(deselectedColor);
// Set default font color.
if (foreColor == Color.Empty)
foreColor = e.Node.TreeView.ForeColor;
// Draw bounding box and fill.
if (e.Node == e.Node.TreeView.SelectedNode)
{
// Use appropriate brush depending on if the tree has focus.
if (this.Focused)
{
foreColor = SystemColors.HighlightText;
e.Graphics.FillRectangle(selectedTreeBrush, e.Bounds);
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, foreColor, SystemColors.Highlight);
TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
foreColor, TextFormatFlags.GlyphOverhangPadding);
}
else
{
foreColor = SystemColors.HighlightText;
e.Graphics.FillRectangle(deselectedTreeBrush, e.Bounds);
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, foreColor, SystemColors.Highlight);
TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
foreColor, TextFormatFlags.GlyphOverhangPadding);
}
}
else
{
if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, hotFont, e.Bounds,
System.Drawing.Color.Black, TextFormatFlags.GlyphOverhangPadding);
}
else
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
foreColor, TextFormatFlags.GlyphOverhangPadding);
}
}
}
Compile the code and you should see "SpecialTreeView" in your tool box in the designer. Replace your TreeView with this new one using the same name and the only thing that will be different is the selection colours. When selected it will be selectedColor, when not selected the deselectedColor.
I hope this helps.
Fast solution:
Set the properties:
HideSelection = false;
DrawMode = TreeViewDrawMode.OwnerDrawText;
Then in the DrawNode event handler simply do:
private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e) {
e.DrawDefault = true;
}
On Windwos 7 this restores the old rendering, including the dashed box around the selection (which actually looks a bit outdated). The text will be white with focus, and black without focus. The background stays blue and visible.
This answer is not new, the others also contain these steps, but this is the minimal needed (at least in Windows 7, didn't test other OS's).
Not absolutely perfect solution, but quite near:
treeView.HideSelection = false;
treeView.DrawMode = TreeViewDrawMode.OwnerDrawText;
treeView.DrawNode += (o, e) =>
{
if (!e.Node.TreeView.Focused && e.Node == e.Node.TreeView.SelectedNode)
{
Font treeFont = e.Node.NodeFont ?? e.Node.TreeView.Font;
e.Graphics.FillRectangle(Brushes.Gray, e.Bounds);
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, SystemColors.HighlightText, SystemColors.Highlight);
TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
}
else
e.DrawDefault = true;
};
treeView.MouseDown += (o, e) =>
{
TreeNode node = treeView.GetNodeAt(e.X, e.Y);
if (node != null && node.Bounds.Contains(e.X, e.Y))
treeView.SelectedNode = node;
};
Found an easier way:
Set TreeView.HideSelection = True
Add the following to TreeView.AfterSelect-Callback:
private void treeViewBenutzerverwaltung_AfterSelect(object sender, TreeViewEventArgs e)
{
// Select new node
e.Node.BackColor = SystemColors.Highlight;
e.Node.ForeColor = SystemColors.HighlightText;
if (_lastSelectedNode != null)
{
// Deselect old node
_lastSelectedNode.BackColor = SystemColors.Window;
_lastSelectedNode.ForeColor = SystemColors.WindowText;
}
_lastSelectedNode = e.Node;
}
Similar to the previous one but with the appearance more similar to the Win10 standard:
treeView.HideSelection = false;
treeView.DrawMode = TreeViewDrawMode.OwnerDrawText;
treeView.DrawNode += (o, e) =>
{
if (e.Node == e.Node.TreeView.SelectedNode)
{
Font font = e.Node.NodeFont ?? e.Node.TreeView.Font;
Rectangle r = e.Bounds;
r.Offset(0, 1);
Brush brush = e.Node.TreeView.Focused ? SystemBrushes.Highlight : Brushes.Gray;
e.Graphics.FillRectangle(brush, e.Bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, font, r, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
}
else
e.DrawDefault = true;
};
treeView.MouseDown += (o, e) =>
{
TreeNode node = treeView.GetNodeAt(e.Location);
if (node != null && node.Bounds.Contains(e.Location)) treeView.SelectedNode = node;
};
I made custom combobox that have an integrated button to add new item and it works good in case when DropDownStyle = ComboBoxStyle.DropDownList but there is a problem when the combobox DropDownStyle = ComboBoxStyle.DropDown the text of the combobox is cover the button that I made.
Is there away to make space before text in case when the DropDownStyle for the combobox is set to DropDown? You can see the problem in image.
[Image showing the spacing issue on the combobox]1
public class ComboBoxButton3 : ComboBox
{
public ComboBoxButton3()
{
myButton = new Button();
this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.DropDownStyle = ComboBoxStyle.DropDownList;
}
protected override void OnCreateControl()
{
this.myButton.Size = new Size(23, this.ClientSize.Height);
this.myButton.Location = new Point(0, 0);
this.myButton.Cursor = Cursors.Default;
this.Button.BackgroundImage = global::RibbonMenuControlTest.Properties.Resources.add1;
this.Button.BackgroundImageLayout = ImageLayout.Stretch;
this.Button.FlatStyle = FlatStyle.Flat;
this.Controls.Add(this.myButton);
base.OnCreateControl();
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (this != null)
{
e.DrawBackground();
if (e.Index >= 0)
{
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
Brush brush = new SolidBrush(this.ForeColor);
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
brush = SystemBrushes.HighlightText;
e.Graphics.DrawString(this.Items[e.Index].ToString(), this.Font, brush, e.Bounds, sf);
}
}
base.OnDrawItem(e);
}
public Button myButton;
public Button Button
{
get
{
return myButton;
}
set
{
myButton = value;
}
}
}
Generally, if a control is not designed to do a certain function with ease, there is normally a reason for it.
I would suggest re-considering your design before going forward.
Regardless, if you insist and continue - then these threads should give you the desired result / point you in the right direction (at least with moving the text around).
Align Text in Combobox
http://blog.michaelgillson.org/2010/05/18/left-right-center-where-do-you-align/
Snippet:
How to set combobox item height? My combobox.size=new size(320,40) and I had set combobox.itemheight=18 but it didn't work. I want my itemheight or text height to be 18, and fixed size for the combobox which is 320x40. I used also drawmode property but nothing is happening.
Try changing Font Size of your combo box
Well, in order to prevent combobox resizing to its default height, you can declare it being manually drawing:
myComboBox.DrawMode = DrawMode.OwnerDrawFixed; // or DrawMode.OwnerDrawVariable;
myComboBox.Height = 18; // <- what ever you want
Then you have to implement DrawItem event:
private void myComboBox_DrawItem(object sender, DrawItemEventArgs e) {
ComboBox box = sender as ComboBox;
if (Object.ReferenceEquals(null, box))
return;
e.DrawBackground();
if (e.Index >= 0) {
Graphics g = e.Graphics;
using (Brush brush = ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
? new SolidBrush(SystemColors.Highlight)
: new SolidBrush(e.BackColor)) {
using (Brush textBrush = new SolidBrush(e.ForeColor)) {
g.FillRectangle(brush, e.Bounds);
g.DrawString(box.Items[e.Index].ToString(),
e.Font,
textBrush,
e.Bounds,
StringFormat.GenericDefault);
}
}
}
e.DrawFocusRectangle();
}
Edit: to have the combobox stretched, but not its dropdown list
myComboBox.DrawMode = DrawMode.OwnerDrawVariable;
myComboBox.Height = 18; // Combobox itself is 18 pixels in height
...
private void myComboBox_MeasureItem(object sender, MeasureItemEventArgs e) {
e.ItemHeight = 17; // while item is 17 pixels high only
}
I need to change back colours of selected nodes, when node selected and has focus - back color make green, when selected but doesn't have focus - red. I can't make the difference between selected node with focus on tree view and without. Tree view located in TabPage object.
//...
this.myTreeView.HideSelection = false;
//...
private void myTreeView_drawNode(object sender, DrawTreeNodeEventArgs e)
{
Color backColorSelected = System.Drawing.Color.Green;
Color backColor = System.Drawing.Color.Red;
// node selected and has focus
if (((e.State & TreeNodeStates.Selected) != 0)
&& (this.myTabControl.Focused)) // this doesn't work, node is always red
{
using (SolidBrush brush = new SolidBrush(backColorSelected))
{
e.Graphics.FillRectangle(brush, e.Bounds);
}
}
// node selected but doesn't have focus
else if ((e.State & TreeNodeStates.Selected) != 0)
{
using (SolidBrush brush = new SolidBrush(backColor))
{
e.Graphics.FillRectangle(brush, e.Bounds);
}
}
// not selected at all
else
{
e.Graphics.FillRectangle(Brushes.White, e.Bounds);
}
e.Graphics.DrawRectangle(SystemPens.Control, e.Bounds);
TextRenderer.DrawText(e.Graphics,
e.Node.Text,
e.Node.TreeView.Font,
e.Node.Bounds,
e.Node.ForeColor);
}
Just check the node's property, it works (tested). Also I suggest caching any custom brushes you make like the following.. (Of course you can also use Brushes.Red and Brushes.Green)
SolidBrush greenBrush = new SolidBrush(Color.Green);
SolidBrush redBrush = new SolidBrush(Color.Red);
private void myTreeView_drawNode(object sender, DrawTreeNodeEventArgs e)
{
if (e.Node.IsSelected)
{
if (treeView1.Focused)
e.Graphics.FillRectangle(greenBrush, e.Bounds);
else
e.Graphics.FillRectangle(redBrush, e.Bounds);
}
else
e.Graphics.FillRectangle(Brushes.White, e.Bounds);
e.Graphics.DrawRectangle(SystemPens.Control, e.Bounds);
TextRenderer.DrawText(e.Graphics,
e.Node.Text,
e.Node.TreeView.Font,
e.Node.Bounds,
e.Node.ForeColor);
}
P.S. You'll probably need to render something that you click to expand nodes etc.