I'm wondering if there is any way to make a control like that one in windows explorer's auto start when you plugin a device.
I had thought that this could be a listview-control in a more or less modified way, but I was not able to find anything with Google. I also checked many CodeProject-pages.
Does anyone have an idea where I would be able to get the control or how I could make one myself? (I am not that good with OwnerDraw :P)
Thanks.
Actually tweaking a ListView is not any easier than ownerDrawing it. Here is an example that shows, how simple it really is.
You just script one event (DrawItem) and you are done.
This piece of code assumes:
The LV's View is set to List
You have a suitable ImageList added to your form
You have the LV's ownerDraw set to true
You have added two columns to hold the text shown in the two labels
You have made the 1st column wide enough to hold the whole stuff that gets drawn
You have made the LV's FontSize as large as the Images' Height (say 32)
Assign the appropriate ImageIndex values to the LV's Items
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
Point point0 = new Point(e.Bounds.Left, e.Bounds.Top);
Point point1 = new Point(imageList1.ImageSize.Width + 10, e.Bounds.Top + 5);
Point point2 = new Point(imageList1.ImageSize.Width + 10, e.Bounds.Top + 25);
Size size = new Size(listView1.ClientRectangle.Width, e.Bounds.Height);
Rectangle R = new Rectangle(point0, size);
Font F1 = new Font(listView1.Font.FontFamily, 11f, FontStyle.Bold);
Font F2 = new Font(listView1.Font.FontFamily, 10f);
if (e.Item.Focused) e.Graphics.FillRectangle(Brushes.LightBlue, R);
else if (e.ItemIndex % 2 == 1) e.Graphics.FillRectangle(Brushes.GhostWhite, R);
e.Graphics.DrawImage(imageList1.Images[e.Item.ImageIndex], point0 );
e.Graphics.DrawString(e.Item.Text, F1, Brushes.Black, point1);
e.Graphics.DrawString(e.Item.SubItems[1].Text, F2, Brushes.Black, point2);
F1.Dispose(); F2.Dispose();
}
Note that I have hard-coded a few Colors to paint every other line and also the focused item. These colors really should use the respective System colors. These come to mind:
SolidBrush brush0 = new SolidBrush(SystemColors.ControlLight);
SolidBrush brush1 = new SolidBrush(SystemColors.Highlight);
I am using the Font that is assigned to the LV but with moderate sizes. Obviously more or less anything, especially the various offsets, can be configured to your liking. But using colors from the System.Colors collection is good way to stay in keeping with your users' Windows themes.
Related
I redraw the graphics of the Tab for the TabControl but I can't set the Width of it.
What I want is that the text of the Tab and the icon is contained in its size.
Now is something like this:
My Code
private void tabControlForm_DrawItem(object sender, DrawItemEventArgs e)
{
try
{
using (Brush br = new SolidBrush(TabColors[tabControlForm.TabPages[e.Index]]))
{
Rectangle rect = e.Bounds;
rect.Width += 10;
e.Graphics.FillRectangle(br, rect);
SizeF sz = e.Graphics.MeasureString(tabControlForm.TabPages[e.Index].Text, e.Font);
e.Graphics.DrawString(tabControlForm.TabPages[e.Index].Text, e.Font, Brushes.Black, rect.Left + (rect.Width - sz.Width) / 2, rect.Top + (rect.Height - sz.Height) / 2 + 1);
using (var src = new Bitmap(System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("Castor.Gestionale.images.close-button.png")))
{
e.Graphics.DrawImage(src, rect.Right - 18, rect.Top + 10);
}
e.Graphics.DrawRectangle(Pens.DarkGray, rect);
e.DrawFocusRectangle();
}
}
catch {}
}
Thanks
Actually you can set the size of the tabs, but not individually.
The combination of SizeMode = Fixed and some suitable value for the TabControl.Itemsize will create any size, but always the same..:
So for individually enlarging each tab to make the icon fit you indeed need to use Ian's 'spacey' method..
Unfortunately, there isn't built-in property to control the width of the TabPages' tab header of the TabControl individually (Edit: apparently, there is TabControl.ItemSize to control it collectively. See TaW's answer to fix the width of all tab pages under a tab control).
But a little trick you could do is to give additional spaces in the left or in the right of the TabPage.Text to give you enough space for your icon.
Without space:
With 7 spaces:
It should be enough to put your icon
Try increasing "myTabControl.Padding.X". It works for me!
Use it...
private void FrmSqlMain_Load(object sender, EventArgs e)
{
myTabControl.SizeMode = TabSizeMode.Normal;
myTabControl.DrawMode = TabDrawMode.OwnerDrawFixed;
}
Also i would like to add one more thing. At first, I tried adding spaces but it didn't seem like working for me. Later, I realized I had to add more spaces than usual to see the difference.
Issue: I want to make Tooltip text Partly bold.
Example text that i want to show on tooltip:
label1: label1Value
label2: label2Value
Progress so far: I have made a custom tooptip class with my own implementation of Draw event. I have used:
DrawToopTipEventArgs newAgrs = new DrawToopTipEventArgs (
e.Graphics, e.AssociatedWindow, e.AssociatedControl,
e.Bounds, e.ToolTipText, this.BackColor, this.ForeColor,
new Font("Arial Unicode MS", 8.25F, FontStyle.Bold));
newAgrs.DrawText(TextFormatFlags.TextBoxControl);
Main Issue: This way am able to make complete tooltiptext bold, but still stuck how i can make only the value part bolds and keep rest of the labels as it is. (as shown above).
You'll have to measure your text and draw everything yourself.
private void ToolTip_Draw(object sender, DrawToolTipEventArgs e)
{
using (var boldFont = new Font(e.Font, FontStyle.Bold))
{
var headerText = "Header: ";
var valueText = "Value";
var headerTextSize = TextRenderer.MeasureText(headerText, e.Font);
TextRenderer.DrawText(e.Graphics, headerText, e.Font, e.Bounds.Location, Color.Black);
var valueTextPosition = new Point(e.Bounds.X + headerTextSize.Width, e.Bounds.Y);
TextRenderer.DrawText(e.Graphics, valueText, boldFont, valueTextPosition, Color.Black);
}
}
I hard-coded the header and value strings for simplicity. It should be trivial to extend this to work for multiple lines. The measured text size has a height, you can also get the height from the Font object itself.
The reason you're example is making everything bold is that you're just delegating all the drawing to a instance of DrawToolTipEventArgs with a new font and telling it to draw the tool tip text (all of it) with the new arguments. It happily took the new font and draw all the text using it.
One additional thing to keep in mind is that the Popup event should also be handled. That even is used to measure the size of the tooltip so you have enough room to draw your text. If you don't handle it, it may not be wide enough to handle the bold font. Specifically, you'll want to set the PopupEventArgs.ToolTipSize property.
Graphics gr;
gr = CreateGraphics();
Pen p = new Pen(System.Drawing.Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)), 1.1f);
Point p1 = new Point(array1[currentadd], dx);
Point p2 = new Point(array1[currentadd], dx = dx + 7);
gr.DrawLine(p, p1, p2);
now i have drawn a line and i want to write the info of points where the line is connecting them ...so i want so set label position to write the point position ... but how?
You should take a look at this document here at MSDN.
In particular something you'll notice in the example is this line:
formGraphics.DrawLine(myPen, 0, 0, 200, 200);
This is doing five different things:
First it will call our Drawing Object.
Second and third are our drawing points.
Fourth and fifth are the floating location.
This will obviously allow you to create some bounded locations. Obviously, if you've generated a class that will handle this initially to build it. Then when you call this object in another class you can position, dock, location, and anchor to that form you've called it in.
Please bear in mind that this assumes a basic line, but you can make more complex things as found here on the MSDN.
Potential Problem:
If you utilize the methodology in the example directly on your Form please bare in mind that the this will bind to the current scope of the object that the this is represented for. An example, if this code was on a button:
Pen myPen = Pen(Color.Red);
Graphics formGraphics = this.CreateGraphics();
formGraphics.DrawLine(myPen, 0, 100, 200, 200);
Then it will be bind the points based on the location of the button object in this case.
Hopefully that helps point you in the right direction.
Update:
Based on your question, it sounds like your trying to place the line either above or below a standard label. Which can be done quite simple, such as:
// Define Label
Label i = new Label();
// Create our Line.
Pen iPen = Pen(Color.Red);
Graphics iGraphics = i.CreateGraphics();
iGraphics.DrawLine(iPen, 0, 250, 0, 0);
// Dock to Bottom of Form
i.Dock = DockStyle.Bottom;
Then if you change the location at any point with a button, or you modify the text within a loop your line will remain bound to your label object. Is that not what you wanted?
Im drawing a linear gradient manually by drawing lines with changing colors. However, this is very slow, and i seems to update, when i resize the window. How do i make it faster? The color scale is linear in this example, but later i wan't to make non-linear gradients.
protected override void OnPaintBackground(PaintEventArgs paintEvnt)
{
SuspendLayout();
// Get the graphics object
Graphics gfx = paintEvnt.Graphics;
// Create a new pen that we shall use for drawing the line
// Loop and create a horizontal line 10 pixels below the last one
for (int i = 0; i <= 500; i++)
{
Pen myPen = new Pen(Color.FromArgb(i/2,0,0));
gfx.DrawLine(myPen, 0, i, 132, i);
}
ResumeLayout();
}
The problem is that GDI+ is incredibly slow.
You should use high level constructs with GDI+ which are relatively fast (relative to drawing lines like you do now). See http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.lineargradientbrush.aspx for more information about e.g. the LinearGradientBrush. There are much more of these brushes and pens which should help you increase your performance.
One more thing: the Suspend/ResumeLayout doesn't do anything in your example. These methods only apply when you are doing layout by e.g. adding Controls to the current form or changing properties on existing Controls like the Dock property or the Height and Width.
If you want to paint it once and only once, without resizing, I suggest you write this to a Bitmap object once, and then draw this bitmap to the background. Also, you can enable double buffering on the form. this should be a property called DoubleBuffering, or something similar. This should reduce the flashing you get when redrawing your form.
You could pre-compute the color values so you won't have to do it on every redraw. Other than that, there's not much more you can do without resorting to more lowlevel APIs, like XNA.
Update: it is perfectly feasible to host XNA within WinForms controls. There's some nice links forward in this question.
Perhaps specifying a ColorBlend to use with the LinearGradientBrush suggested by Pieter will address your concerns about being able to paint non-linear gradients in the future?
You can create a ColorBlend object that specifies the colors of your choice and an arbitrary position for each. By setting the InterpolationColors property of the LinearGradientBrush to your ColorBlend object, you should be able to get any effect that you want.
MSDN gives the following sample:
protected override void OnPaint(PaintEventArgs e)
{
//Draw ellipse using ColorBlend.
Point startPoint2 = new Point(20, 110);
Point endPoint2 = new Point(140, 110);
Color[] myColors = {Color.Green, Color.Yellow, Color.Yellow, Color.Blue, Color.Red, Color.Red};
float[] myPositions = {0.0f,.20f,.40f,.60f,.80f,1.0f};
ColorBlend myBlend = new ColorBlend();
myBlend.Colors = myColors;
myBlend.Positions = myPositions;
LinearGradientBrush lgBrush2 = new LinearGradientBrush(startPoint2, endPoint2, Color.Green, Color.Red);
lgBrush2.InterpolationColors = myBlend;
Rectangle ellipseRect2 = new Rectangle(20, 110, 120, 80);
e.Graphics.FillEllipse(lgBrush2, ellipseRect2);
}
This is a C# desktop application. The DrawStyle property of my ListBox is set to OwnerDrawFixed.
The problem: I override DrawItem to draw text in different fonts, and it works. But when I start resizing the form at the runtime, the selected item is drawn correctly, but the rest of them are not redrawn, causing text looking corrupt for unselected items.
Here's my code:
private void listDevices_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
string textDevice = ((ListBox)sender).Items[e.Index].ToString();
e.Graphics.DrawString(textDevice,
new Font("Ariel", 15, FontStyle.Bold), new SolidBrush(Color.Black),
e.Bounds, StringFormat.GenericDefault);
// Figure out where to draw IP
StringFormat copy = new StringFormat(
StringFormatFlags.NoWrap |
StringFormatFlags.MeasureTrailingSpaces
);
copy.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, textDevice.Length)});
Region[] regions = e.Graphics.MeasureCharacterRanges(
textDevice, new Font("Ariel", 15, FontStyle.Bold), e.Bounds, copy);
int width = (int)(regions[0].GetBounds(e.Graphics).Width);
Rectangle rect = e.Bounds;
rect.X += width;
rect.Width -= width;
// draw IP
e.Graphics.DrawString(" 255.255.255.255",
new Font("Courier New", 10), new SolidBrush(Color.DarkBlue),
rect, copy);
e.DrawFocusRectangle();
}
listDevices.Items.Add("Device001");
listDevices.Items.Add("Device002");
Also, the item that is drawn correctly (the selected one) is flickering on form resizing. No biggie, but if anyone know why.... tnx
Put the following code in the Resize event:
private void listDevices_Resize(object sender, EventArgs e) {
listDevices.Invalidate();
}
This should cause everything to be redrawn.
To stop the flickering, you need double buffering.
To do this, make a new class, derived from ListBox, and put the following in the constructor:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Or just paste this into a code file:
using System.Windows.Forms;
namespace Whatever {
public class DBListBox : ListBox {
public DBListBox(): base() {
this.DoubleBuffered = true;
// OR
// this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
}
}
Replace "Whatever" with the namespace your project uses, or make it something more useful. AFter compiling, you should be able to add a DBListBox in the form designer.
I repro the problem. There are several mistakes in the code, the font name is "Arial", you should not adjust rect.Width, you forget to call Dispose() on the fonts, brushes and regions. But they don't explain the behavior. There's something wrong with the clipping area that prevents the text from being properly updated. I don't see where that occurs, the Graphics object state is okay.
Graphics.DrawString() is a very troubled method, you should really avoid it. All Windows Forms controls, including ListBox, use TextRenderer.DrawText(). That solves the problem when I use it. I know measuring is more difficult, you could work around that by displaying the IP address at a fixed offset. Looks better too, they'll line up in a column that way.
It flickers because you use e.DrawBackground(). That erases the existing text, you draw the text right back on it. I don't think double-buffering is going to fix that, you'd have to draw the entire item so you don't have to draw the background. Tricky if you can't get the exact size of the text with the large font, a workaround is to draw into a bitmap first.