Ok so im starting to get stuck into my design and get the style right.
My Theme is using a kryptonForm style GUI but kyryptonForms do not not have a pre designed ListView, so im having to build this myself
My Application is a messenger system based on XMPP/Jabber so you can guess how i would like my contact list to be designed.
i have done most of the positioning but im struggling on styling each contact row.
Im aiming for some transparent overlay simmerler to the MSN Live messenger Contact List
Heres my OnDraw Event code atm and im struggling to figure out the best way to do the gradient
private void ContactItem_OnPaintDraw(object sender, DrawListViewItemEventArgs e)
{
Rectangle ImageRect = e.Bounds;
ImageRect.Inflate(-2, -2);
ImageRect.Width = 32;
Rectangle TextRect = e.Bounds;
TextRect.X = ImageRect.Right + 2;
TextRect.Width = e.Bounds.Width - TextRect.X;
Rectangle IconRect = TextRect;
IconRect.Inflate(-1, 0);
IconRect.Y = ImageRect.Bottom - 16;
IconRect.Width = 16;
IconRect.Height = 16;
if ((e.State & ListViewItemStates.Selected) != 0)
{
// Draw the background and focus rectangle for a selected item.
e.Graphics.FillRectangle(ContactListBackgroundBrush, e.Bounds);
e.DrawFocusRectangle();
}
else
{
// Draw the background for an unselected item.
e.Graphics.FillRectangle(Brushes.White, e.Bounds);
}
if (ListViewContacts.View != View.Details)
{
e.Graphics.DrawImage((Image)Resources.UserIconDefault, ImageRect);
TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, TextRect, e.Item.ForeColor, TextFormatFlags.GlyphOverhangPadding);
}
}
And the ContactListBackgroundBrush var is like so
private Brush ContactListBackgroundBrush = new SolidBrush(Color.FromArgb(33, 162, 191));
its this that i need to convert to the styled element
alt text http://screensnapr.com/u/yeq8o0.png
Im Looking to get this Highlighted style without importing any specific windows 7 DLL files as the App is used for windows XP as well.
Hope you guys can help me :)
You can define a brush as a LinearGradientBrush, look for the msnd documentation. This is IMHO the best way, to draw gradiants..
Related
I have a custom ComboBox in a C# WinForms application, and I have been successful in overriding the background color when its both enabled and disabled, and overriding the border when its enabled, but when the control is disabled I cannot figure out how to change the color of its border.
Currently, I am catching the WndProc messages being sent to the custom control, something like this:
protected override void WndProc (ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_PAINT)
{
// set the enabled border color here, and it works
using (var g = Graphics.FromHwnd(Handle)
{
using (var p = new Pen(Color.Black))
{
g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
}
}
}
if (m.Msg == WM_CTLCOLORSTATIC)
{
// set the disabled background color here
]
if (m.Msg == WM_NCPAINT)
{
// try to set the disabled border color here, but its not working
using (var g = Graphics.FromHwnd(Handle)
{
using (var p = new Pen(Color.Black))
{
g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
}
}
}
}
Here are some images to help understand the problem, I have chosen colors that specifically highlight the problem area, these are not the actual colors I would use:
Enabled comboBox:
Disabled comboBox:
Notice the thick SystemGrey border being applied when the comboBox is disabled. This is what I want to remove, it looks worse on older windows systems, these screenshots were taken on Windows 10 but I'm targeting Windows Server 2012 where it produces a strange "halo" type effect that extends outside the control.
Looking at the MSDN, it seems like WM_NCPAINT is the message I want, but stepping through the code the border appears to have already been drawn at this point. I also tried looking at the MSDN for WM_CTLCOLORSTATIC, since the name seems promising, but it doesnt seem like it triggers anything but background colors to be set.
Is there another message I should be looking at, or am I approaching this the wrong way? I tried stepping through and looking at each message, but I just cant tell which one is triggering the call for the border to be set.
Edit: See below for the solution, this is a quick and dirty example of what I wanted to achieve, and what the solution code will do:
This answer relies on Change ComboBox Border Color in Windows Forms.
This post added a few criteria to remove the white inner border from the control when the control have DropDown or DropDownList as DropDownStyle and in all FlatStyle values and both for enabled and disables. This is done by handling WM_PAINT message and drawing the outer and inner border for combo box, like following image.
For demo purpose, all the controls in the image have BackColor = Color.Black and BorderColor = Color.Red and you can see their DropDownStyle, FlatStyle and Enabled as their selected item:
using System;
using System.Drawing;
using System.Windows.Forms;
public class FlatCombo : ComboBox
{
private const int WM_PAINT = 0xF;
private int buttonWidth = SystemInformation.HorizontalScrollBarArrowWidth;
Color borderColor = Color.Blue;
public Color BorderColor
{
get { return borderColor; }
set { borderColor = value; Invalidate(); }
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_PAINT && DropDownStyle != ComboBoxStyle.Simple)
{
using (var g = Graphics.FromHwnd(Handle))
{
var adjustMent = 0;
if (FlatStyle == FlatStyle.Popup ||
(FlatStyle == FlatStyle.Flat &&
DropDownStyle == ComboBoxStyle.DropDownList))
adjustMent = 1;
var innerBorderWisth = 3;
var innerBorderColor = BackColor;
if (DropDownStyle == ComboBoxStyle.DropDownList &&
(FlatStyle == FlatStyle.System || FlatStyle == FlatStyle.Standard))
innerBorderColor = Color.FromArgb(0xCCCCCC);
if (DropDownStyle == ComboBoxStyle.DropDown && !Enabled)
innerBorderColor = SystemColors.Control;
if (DropDownStyle == ComboBoxStyle.DropDown || Enabled == false)
{
using (var p = new Pen(innerBorderColor, innerBorderWisth))
{
p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
g.DrawRectangle(p, 1, 1,
Width - buttonWidth - adjustMent - 1, Height - 1);
}
}
using (var p = new Pen(BorderColor))
{
g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
g.DrawLine(p, Width - buttonWidth - adjustMent,
0, Width - buttonWidth - adjustMent, Height);
}
}
}
}
}
Note:
To learn more about how to render a flat combo box, you can take a look at source code of internal ComboBox.FlatComboAdapter class of .Net Framework.
If you see flickers, for a flicker-free solution you can use BeginPaint and EndPaint. Or as a workaround, in your form add the following code:
private const int WS_EX_COMPOSITED = 0x02000000;
protected override CreateParams CreateParams
{
get
{
var c = base.CreateParams;
c.ExStyle |= WS_EX_COMPOSITED;
return c;
}
}
Edit: since there seems to be some confusion about why the various other answers out there are not appropriate, I will explain the issue. When a combobox is disabled, the framework generates a 3 pixel wide border around it, using the default windows background color of "Grey". If you are using the default windows form background color, you will never see this border, and simply drawing over the 1 pixel wide black "border" (its not actually the border, its part of the control area) will solve the problem. However, if your form background is, for example, Black, you will see this default form color border. This is what the below solution solves, and I hope this helps someone.
Turns out sometimes you just need to step away for 10 minutes, I figured out how to do this. Its probably not ideal, and its definitely not graceful, but it works.
You can in fact draw a new border while handling the WM_CTLCOLORSTATIC message, i thought you couldnt because the rectangle is so small, but it does in fact get drawn, and while this is definitely not the message thats handling the original border drawing, this comes after it so you can draw right over top.
This code will draw over top of the existing light grey border with a dark grey border:
if (m.Msg == WM_CTLCOLORSTATIC)
{
// set your other colors for disabled controls
using (var g = Graphics.FromHwnd(Handle)
{
using (var p = new Pen(Color.FromArgb(93, 100, 103))
{
// its a fat border so we need to draw 3 rectangles to cover it
g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
g.DrawRectangle(p, 1, 1, Width - 3, Height - 3);
g.DrawRectangle(p, 2, 2, Width - 5, Height - 5);
}
}
]
I know this is disgusting, but I am on hour 4 of trying to draw a border around a control... so if anyone has a cleaner solution, I am more than open to accepting it.
I am working on a regular WinForm Outlook Addin and I have created a Treeview using DrawNode event, The tree is working as expected but there is a glitch in node clicking, only the green region is clickable and the half node gets non responsive.
When I use the MouseDown event the whole area gets clickable including the blank space right next to the node. But to restrict this blank clicking I am using a logic with the help of TreeViewHitTestLocations I am checking if the clicked location is the RightOfLabel then don't do anything but unfortunately this doesn't give me a precise result, it somehow gets confuse and takes right half of the label(Node) as the blank space and doesn't get clicked.
Note: I think this all happened because I played with DrawNode method and while keeping the distance between label and workspace icon the application underneath assumes that the label gets finish within the green portion so the red portion gets left as a blank space. This is just my assumption based on all the naïve things I have done with the method.
Need help to resolve this issue if someone can guide me to a fix. Thanks
void treeview_mousedown(object sender, MouseEventArgs e)
{
TreeNode nodeClicked;
// if arrow up/down will be excluded from the mousedown event
var hitTest = this.HitTest(e.Location);
if (hitTest.Location == TreeViewHitTestLocations.PlusMinus)
return;
if (hitTest.Location == TreeViewHitTestLocations.RightOfLabel)
return;
// Get the node clicked on
nodeClicked = this.GetNodeAt(e.X, e.Y);
// Was the node clicked on?
if (!(nodeClicked == null))
this.SelectedNode = nodeClicked;
}
Below is the treeview drawnode method I am using:
void treeview_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
Rectangle nodeRect = e.Node.Bounds;
// below location is the expand and collapse icon location
Point ptExpand = new Point(nodeRect.Location.X - 7, nodeRect.Location.Y + 5);
Image expandImg = null;
// check the below condition for nodes with child nodes and nodes without child nodes
if ( e.Node.Nodes.Count < 1)
expandImg = global::myresource.OfficeAddin.Controls.Resource.search;
else if (e.Node.IsExpanded && e.Node.Nodes.Count > 1)
expandImg = global::myresource.OfficeAddin.Controls.Resource.down_arrow_icon;
else
expandImg = global::myresource.OfficeAddin.Controls.Resource.right_arrow_icon;
Graphics g = Graphics.FromImage(expandImg);
IntPtr imgPtr = g.GetHdc();
g.ReleaseHdc();
e.Graphics.DrawImage(expandImg, ptExpand);
// draw node icon
Point ptNodeIcon = new Point(nodeRect.Location.X - 4, nodeRect.Location.Y + 2);
Image nodeImg = global::myresource.OfficeAddin.Controls.Resource.folder_icon_16px;
g = Graphics.FromImage(nodeImg);
imgPtr = g.GetHdc();
g.ReleaseHdc();
e.Graphics.DrawImage(nodeImg, ptNodeIcon);
// draw node text
Font nodeFont = e.Node.NodeFont;
if (e.Node.NodeFont != null)
{
nodeFont = e.Node.NodeFont;
} else {
nodeFont = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
}
// set the forecolor
Color forecolor = e.Node.ForeColor;
// color same as the font color
string strSelectedColor = #"#505050";
Color selectedColor = System.Drawing.ColorTranslator.FromHtml(strSelectedColor);
SolidBrush selectedTreeBrush = new SolidBrush(selectedColor);
//Inflate to not be cut
Rectangle textRect = nodeRect;
//below value controls the width of the text if given less then, long texts will come in multiple lines
textRect.Width += 150;
// below value controls the over all width of the node, if given less all the things will get sqeeze
e.Graphics.DrawString(e.Node.Text, nodeFont, selectedTreeBrush , Rectangle.Inflate(textRect, -20, 0));
}
Your assumption is right, you can check it by marking the bounds at drawing, for example:
TreeNodeStates state = e.State;
bool isFocused = (state & TreeNodeStates.Focused) == TreeNodeStates.Focused;
if (isFocused)
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, foreColor, backColor);
Your code has more problems:
Issue 1: Images
If you use icons for nodes use the TreeView.ImageList and TreeNode.ImageKey properties. Otherwise, no space will be allocated for the image at drawing. In this case you can use TreeViewDrawMode.OwnerDrawText DrawMode.
Issue 2: Fonts
Do not use other font than the font of the node or the tree because it can have unexpected size. If this SegoeUI font is a default one use it at your TreeView instance instead. Then you can obtain the required font like this:
Font font = e.Node.NodeFont ?? e.Node.TreeView.Font;
Issue 3: Texts
You use Graphics.DrawString for drawing texts, which uses GDI+. However, starting with .NET 2.0 the default text rendering method is by GDI, unless CompatibleTextRendering property of a control is true. They produce slightly different text sizes.
To use GDI, for which the size of a label is calculated use the TextRenderer class instead:
TextRenderer.DrawText(e.Graphics, e.Node.Text, font, e.Bounds, foreColor, TextFormatFlags.GlyphOverhangPadding | TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis | TextFormatFlags.NoPrefix);
I have a tab control which I want to customize. To be more specific, I want to change the color of the tab page header, as well as the color of that white line around the tab page (check first picture).
I thought of using a custom renderer to do this (similar to recoloring a menu strip, for example), but I'm not sure how to do this. I've also read that setting the DrawMode to OwnerDrawFixed may do this, but using this option makes the the tab control look as if my program was made in the '90s (check second picture).
What I really want to do is to keep the tabs simple and flat and change their color. Check the way tabs are in Visual Studio as an example (check third picture).
Any ideas?
Edit: Another picture of the tab page so that it's more clear what this "white line" is.
When you use OwnerDrawFixed it means you will supply the drawing code. If you did not hook up and use the DrawItem event, nothing gets drawn. This will look much the same as yours at design time, because the event is not firing. For design time painting, you'd have to subclass the control and use OnDrawItem.
// colors to use
private Color[] TColors = {Color.Salmon, Color.White, Color.LightBlue};
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
// get ref to this page
TabPage tp = ((TabControl)sender).TabPages[e.Index];
using (Brush br = new SolidBrush(TColors[e.Index]))
{
Rectangle rect = e.Bounds;
e.Graphics.FillRectangle(br, e.Bounds);
rect.Offset(1, 1);
TextRenderer.DrawText(e.Graphics, tp.Text,
tp.Font, rect, tp.ForeColor);
// draw the border
rect = e.Bounds;
rect.Offset(0, 1);
rect.Inflate(0, -1);
// ControlDark looks right for the border
using (Pen p = new Pen(SystemColors.ControlDark))
{
e.Graphics.DrawRectangle(p, rect);
}
if (e.State == DrawItemState.Selected) e.DrawFocusRectangle();
}
}
basic result:
The tab thumb looks a bit cramped to me and not as tall as the default. So, I added a TFontSize to draw the text at a different size than the Font.
Set the TabControl.Font to 10 (which seems to be plenty), so that Windows draws a slightly larger thumb/header. If you still draw the text at the default 8.25, there is more room:
private float TFontSize = 8.25F; // font drawing size
...
using (Font f = new Font(tp.Font.FontFamily,TFontSize))
{
// shift for a gutter/padding
rect.Offset(1, 1);
TextRenderer.DrawText(e.Graphics, tp.Text,
f, rect, tp.ForeColor);
}
One thing you will loose this way is the VisualStyles effect, but they would seem to clash with colored tabs anyway.
I have a listview that uses custom cell colors, but when I set a background image in the listview, the custom cell colors will not appear anymore. I tried to remove the background image temporarily (when assembling the list) and restore it after applying cell colors. This results in no custom colors but shows the background. I would like to combine these 2 listview properties if possible.
My code for setting/removing background image:
list.BackgroundImage = Properties.Resources.bgalpha;
list.BackgroundImage = null;
A part of my code for setting custom cell colors:
for (int i = 0; i < kavels.Count(); i++ )
{
if (list.Items[i].SubItems[1].Text != "0")
{
list.Items[i].UseItemStyleForSubItems = false;
list.Items[i].SubItems[1].BackColor = Color.LightGreen;
}
}
Here are two screenshots:
List view with background: http://i.imgur.com/aHUXAVh.png
List view without background: http://i.imgur.com/sO83wTP.png
I also tried making a PictureBox with a transparent background along with a png image with transparency on top of the ListView, but that also didn't work obviously.
You have two options:
You could overlay a Panel or a PictureBox with a semi-transprent Image. For this to work you would have to make it sit inside the ListView, so that it is the Parent of the overlay.
But that will make the Listview non-clickable. - Another problem with this is that it will slightly color the text, so it won't look quite right.
Or you can set the ListView to OwnerDraw = true and add code to do the drawing yourself.
Here is an example, non-scrolled and scrolled:
Note that the original BackgroundImage shines through the emtpy space to the right.
If you owner-draw a ListView in Details mode you need to code events to draw subitems and headers; note the class level variable to hold the itemHeight; this assumes they all have the same Height .. The other one is need for horizontal scrolling.
int itemHeight = 0; // we need this number!
int itemLeft = 0; // we need this number, too
private void listView1_DrawColumnHeader(object sender,
DrawListViewColumnHeaderEventArgs e)
{
Rectangle R0 = listView1.GetItemRect(0);
itemHeight = R0.Height; // we need this number!
itemLeft = R0.Left; // we need this number too
e.DrawBackground();
e.DrawText();
}
private void listView1_DrawSubItem(object sender,
DrawListViewSubItemEventArgs e)
{
Rectangle rrr = listView1.GetItemRect(e.ItemIndex);
Rectangle rect = e.Bounds;
Rectangle rect0 = new Rectangle(rect.X - itemLeft , itemHeight * e.ItemIndex,
rect.Width, rect.Height);
Image img = listView1.BackgroundImage;
e.Graphics.DrawImage(img, rect, rect0, GraphicsUnit.Pixel);
using (SolidBrush brush = new SolidBrush(e.SubItem.BackColor))
e.Graphics.FillRectangle(brush, rect);
e.DrawText();
}
Here is the code to set the colors in the ListViewItem lvi for the example:
lvi.UseItemStyleForSubItems = false;
lvi.BackColor = Color.FromArgb(66, Color.LightBlue);
lvi.SubItems[1].BackColor = Color.FromArgb(77, Color.LightGreen);
lvi.SubItems[2].BackColor = Color.FromArgb(88, Color.LightPink);
Note that the code assumes your background is one large image and no tiling is involved! Also the code works only if you don't have groups!
ObjectListView -- an open source wrapper around a standard .NET ListView -- provides ImageOverlays and true background images too. They both work with colour cells.
I have the following code in my Universal App (Windows Phone):
void colourPicker_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
PicturePicker picturePicker = sender as PicturePicker;
GeneralTransform transform = picturePicker.TransformToVisual(ImageHolder);
Point controlPosition = transform.TransformPoint(new Point(0, 0));
int pointX = (int)controlPosition.X;
int pointY = (int)controlPosition.Y;
Color c = writeableBmp.GetPixel(pointX, pointY);
// WriteableBitmap newWB = writeableBmp.Crop(pointX - 21, pointY - 21, 42, 42);
// picturePicker.SetImageBrush(newWB);
SolidColorBrush brush = new SolidColorBrush(c);
picturePicker.SetColor(brush);
Canvas.SetLeft(picturePicker, Canvas.GetLeft(picturePicker) + e.Delta.Translation.X);
Canvas.SetTop(picturePicker, Canvas.GetTop(picturePicker) + e.Delta.Translation.Y);
}
I have a image, and on top of that a UserControl (very simple XAML) that the user can drag around to find the color he/she wants. Only dragging works great on ManipulationDelta, but when I try to GetPixel() everytime the ManipulationDelta event triggers the translate becomes very laggy. If I comment out the setting of the color (picturePicker.SetColor) it's still slow, so that's not the problem.
Weird enough, very similiar code worked fluid in the old version of this app in Silverlight (on the same phone).
Any thoughts on how to improve this?