C# ComboBox Item Highlight not working with DrawItem - c#

Using the example from the marked answer in this post i came up with this.
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
using (DBDataContext data = new DBDataContext())
{
var query = (from a in data.Programs
where a.IsCurrentApplication == 1
select a.Name).Distinct();
e.DrawBackground();
string text = ((ComboBox)sender).Items[e.Index].ToString();
Brush brush;
if (query.Contains(text))
{
brush = Brushes.Green;
}
else
{
brush = Brushes.Black;
}
e.Graphics.DrawString(text,
((Control)sender).Font,
brush,
e.Bounds.X, e.Bounds.Y);
}
}
What I'm doing is querying a database for applications with a flag. If the flag is true (1), then I change the text of the combobox item to Green. My issue here is that once all the items are drawn. When I hover my cursor of an item, it doesn't highlight. It does however change the darkness of the text slightly. Is there a way i can get the highlighting to work?

As I said in the comment, when drawing we should avoid as much non-drawing code as possible. In some cases doing so can cause some flicker and in some cases the result is unpredicted. So we should always place such a code outside the drawing route. The query in your code should be placed outside your DrawItem event handler something like this:
public Form1(){
InitializeComponent();
using (DBDataContext data = new DBDataContext()) {
query = (from a in data.Programs
where a.IsCurrentApplication == 1
select a.Name).Distinct().ToList();//execute the query right
}
}
IEnumerable<string> query;
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e) {
var combo = sender as ComboBox;
e.DrawBackground();
string text = combo.Items[e.Index].ToString();
Brush brush = query.Contains(text) ? Brushes.Green : Brushes.Black;
e.Graphics.DrawString(text, e.Font, brush, e.Bounds);
}

Related

How to change default selection color of a ListView?

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;

How to highlight or mark a node in a treeView in C #? [duplicate]

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;
};

Space before text in ComboBox

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 change the background color of a ListBox Item when hovering?

How do I change the Background Color of a ListBox Item when I hover over it?
I have overridden the DrawItem Event with this code:
private void DrawListBox(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
Graphics g = e.Graphics;
Brush brush = ((e.State & DrawItemState.Selected) == DrawItemState.Selected) ?
new SolidBrush(Color.FromArgb(52, 146, 204)) : new SolidBrush(e.BackColor);
g.FillRectangle(brush, e.Bounds);
e.Graphics.DrawString(listBox1.Items[e.Index].ToString(), e.Font,
new SolidBrush(e.ForeColor), e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
}
However, a ListBox does not have a Property which contains the hovered Item. There is also no Event like MouseEnterItem, MouseHoverItem Event, or anything similar on a ListBox to subscribe to.
I did quite a lot of research, but all I found was a similar question on SO, which confused ListBox and ListView: Hightlight Listbox item on mouse over event
As the ListBox does not provide the MouseEnterItem and MouseHoverItem Events, it is necessary to code this functionality yourself, tracking the coordinates of the mouse to determine which item the mouse is over.
The following question is very similar, aimed at showing a tooltip for each item when hovered over. Michael Lang's answer is a very good workaround and should be adaptable for your purposes:
How can I set different Tooltip text for each item in a listbox?
Do you really want a hover? => user moves mouse and then stops over a point for a delay without moving again
If you want immediate feedback then use the MouseMove() event and get the Index of the item you are over with IndexFromPoint(). Store that value at form level so you can determine if it has changed since the last move and then set the background fill color accordingly in your DrawListBox() handler:
private int prevIndex = -1;
private void listBox1_MouseMove(object sender, MouseEventArgs e)
{
int index = listBox1.IndexFromPoint(listBox1.PointToClient(Cursor.Position));
if (index != prevIndex)
{
prevIndex = index;
listBox1.Invalidate();
}
}
private void listBox1_MouseLeave(object sender, EventArgs e)
{
prevIndex = -1;
listBox1.Invalidate();
}
private void DrawListBox(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
Graphics g = e.Graphics;
Color c;
if (e.Index == prevIndex )
{
c = Color.Yellow; // whatever the "highlight" color should be
}
else if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
c = Color.FromArgb(52, 146, 204);
}
else
{
c = e.BackColor;
}
using (SolidBrush brsh = new SolidBrush(c))
{
g.FillRectangle(brsh, e.Bounds);
}
using (SolidBrush brsh = new SolidBrush(e.ForeColor))
{
g.DrawString(listBox1.Items[e.Index].ToString(), e.Font,
brsh, e.Bounds, StringFormat.GenericDefault);
}
e.DrawFocusRectangle();
}

How to change color of selected item in ListBox?

I have a ListBox control with some items and I want to change color of selected item... How can I do it in C# (WinForms)?
Help me please :)
This is how you can set, say Red color, to the selected ASP.NET ListBox items:
<asp:ListBox runat="server" ID="ListBox1">
<asp:ListItem Text="Text1"></asp:ListItem>
<asp:ListItem Text="Text2"></asp:ListItem>
</asp:ListBox>
if(ListBox1.SelectedItem != null)
ListBox1.SelectedItem.Attributes["style"] = "color:red";
A good example of this is available here
I am copying over code from the above example here:
"You can set the color of individual items in a ListBox using C# in your .NET WinForm by writting your own handler for the listbox's DrawItem event.
Set the ListBox's DrawMode property:
Add a standard ListBox to your .NET WinForm then set it's DrawMode property to OwnerDrawFixed which forces the ListBox's DrawItem event to be fired.
Write the handler for the DrawItem event:
private void lstBox_DrawItem(object sender, _
System.Windows.Forms.DrawItemEventArgs e)
{
//
// Draw the background of the ListBox control for each item.
// Create a new Brush and initialize to a Black colored brush
// by default.
//
e.DrawBackground();
Brush myBrush = Brushes.Black;
//
// Determine the color of the brush to draw each item based on
// the index of the item to draw.
//
switch (e.Index)
{
case 0:
myBrush = Brushes.Red;
break;
case 1:
myBrush = Brushes.Orange;
break;
case 2:
myBrush = Brushes.Purple;
break;
}
//
// Draw the current item text based on the current
// Font and the custom brush settings.
//
e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(),
e.Font, myBrush,e.Bounds,StringFormat.GenericDefault);
//
// If the ListBox has focus, draw a focus rectangle
// around the selected item.
//
e.DrawFocusRectangle();
}
In the InitializeComponent section associate your handler to the DrawItem event:
this.lstBox.DrawItem +=
new System.Windows.Forms.DrawItemEventHandler(this.lstBox_DrawItem);
You can try combinations of different brushes and Colors.
using System.Drawing;
using System.Drawing.Drawing2D;
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Rectangle rc = listBox1.GetItemRectangle(listBox1.SelectedIndex);
LinearGradientBrush brush = new LinearGradientBrush(
rc, Color.Transparent, Color.Red, LinearGradientMode.ForwardDiagonal);
Graphics g = Graphics.FromHwnd(listBox1.Handle);
g.FillRectangle(brush, rc);
}
Collating the changes, none of the above answers worked completely for me. Collating what worked for me:
public CustomListBox()
{
this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.DrawItem += new System.Windows.Forms.DrawItemEventHandler(custom_DrawItem);
this.MeasureItem += lstMeasureItem;
}
protected override void OnSelectedIndexChanged(EventArgs e)
{
this.Invalidate();
}
private void custom_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
//if the item state is selected then 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.Gray);//Choose the color
// Draw the background of the ListBox control for each item.
e.DrawBackground();
System.Drawing.SolidBrush myBrush = new System.Drawing.SolidBrush(this.tbForeColor);
// Draw the current item text
e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault);
// If the ListBox has focus, draw a focus rectangle around the selected item.
e.DrawFocusRectangle();
}
private void lstMeasureItem(object sender, MeasureItemEventArgs e)
{
// Cast the sender object back to ListBox type.
CustomListBox listBox = (CustomListBox)sender;
e.ItemHeight = listBox.Font.Height;
}

Categories