How do I achieve the equivalent of the LVIS_CUT style for a listview item? It looks like it's not exposed by the framework? Should I P/Invoke?
Edit: LVIS_CUT is a Win32 style that affects the look of the item: It grays the item image. You can see it in action in Windows Explorer: Select a file and type Ctrl+X.
TIA.
Well, one way to "achieve the equivalent of the LVIS_CUT style" would be the following:
Use a function along the lines of
private void MakeCutList(ImageList sourceList, Color background)
{
Brush overlay = new SolidBrush(Color.FromArgb(128, BackColor));
Rectangle rect = new Rectangle(new Point(0, 0), sourceList.ImageSize);
foreach (Image img in sourceList.Images)
{
Bitmap cutBmp = new Bitmap(img.Width, img.Height);
using (Graphics g = Graphics.FromImage(cutBmp))
{
g.DrawImage(img, 0, 0);
g.FillRectangle(overlay, rect);
}
sourceList.Images.Add(cutBmp);
}
}
to take the image list used by your ListView (i.e. listView1.ImageList) and add the "cut" versions of all the icons. You might call this immediately after InitializeComponent in your form, like
public Form1()
{
InitializeComponent();
MakeCutList(listView1.LargeImageList, listView1.BackColor);
}
Then you could use code like this
private void SetCutState(ListViewItem lvi, Boolean isItemCut)
{
int originalListSize = lvi.ImageList.Images.Count / 2;
int baseIndex = lvi.ImageIndex % originalListSize;
int cutImagesOffset = originalListSize;
if (isItemCut)
{
lvi.ImageIndex = cutImagesOffset + baseIndex;
lvi.ForeColor = SystemColors.GrayText;
}
else
{
lvi.ImageIndex = baseIndex;
lvi.ForeColor = SystemColors.WindowText;
}
}
to change the state of an item to being cut or not.
Once you get that working, you could try to put similar code into a subclassed version of the ListView control.
Are you referring to when it's grayed out? Like when you do a "cut" on it? If so, I would just set the forecolor to Inactive or something along those lines. Not sure that you need to pinvoke for something like that.
I used this for my file explorer application ..
private void MakeCutItem()
{
foreach (ListViewItem item in listView1.SelectedItems)
{
Image img = item.ImageList.Images[item.ImageIndex];
Brush overlay = new SolidBrush(Color.FromArgb(128, BackColor));
Rectangle rect = new Rectangle(new Point(0, 0), item.ImageList.ImageSize);
using (Graphics g = Graphics.FromImage(img))
{
g.FillRectangle(overlay, rect);
}
item.ImageIndex = item.ImageList.Images.Add(img,Color.Empty);
}
}
Related
As you know there is no simple way for changing selected item back color in listview control and for that you should use drawItem event.
(Am I right or there is a way which I do not know?)
so, I use drawitem and try to make everything normal, except this property. it's good but in title view there is a small box after icon that show item name( also in large icon view),it's invisible but when I click on its location it shows. How should I make it visible always? thanks.
I added code and image,tanks for your response
void FileList_DrawItem(object sender, DrawListViewItemEventArgs e)
{
if (e.Item.Selected)
{
Rectangle rectangle = e.Bounds;
rectangle.Inflate(-1, -1);
Image image;
if (this.View == System.Windows.Forms.View.LargeIcon || this.View == System.Windows.Forms.View.Tile)
{
image = LargeImageList.Images[e.Item.ImageKey];
}
else
{
image = SmallImageList.Images[e.Item.ImageKey];
}
Rectangle rectPoint = new Rectangle(e.Item.Bounds.X + 3, e.Item.Bounds.Y + 3, image.Width - 3, image.Height - 3);
Rectangle srcRect = new Rectangle(0, 0, image.Width, image.Height);
using (Graphics g = e.Graphics)
{
g.DrawRectangle(Pens.Red, rectangle);
g.DrawImage(image, rectPoint, srcRect, GraphicsUnit.Pixel);
Rectangle rectangle2 =new Rectangle(e.Item.Bounds.X+image.Width+5,e.Item.Bounds.Y+(image.Height/2)+7,50,15);
using (StringFormat sf = new StringFormat())
{
using (Font headerFont =
new Font("Helvetica", 8, FontStyle.Regular))
{
e.Graphics.DrawString("Folder", headerFont,
Brushes.Black, rectangle2, sf);
}
}
}
}
else
{
e.DrawDefault = true;
}
}
I done it somehow, first maybe you need to set each item text to string.Empty or something like that, then you should change rectangle for text yourself. it is not very good but it can be done in no time. for each view change and test until it be good.
I'm trying to copy from the screen the position of a PictureBox. My code looks like this:
Form1.cs:
_recorder = new ScreenRecord();
_recorder.StartRecording(
pictureBox1,
pictureBox1.RectangleToScreen(new Rectangle())
);
ScreenRecord.cs:
class ScreenRecord
{
private ScreenBitmap scBitmap;
public void StartRecording(PictureBox cam, Rectangle rect)
{
scBitmap = new ScreenBitmap(cam, rect);
cam.Image = scBitmap.GetBitmap();
}
}
ScreenBitmap.cs:
class ScreenBitmap
{
private PictureBox camBox;
private Rectangle camLocation;
public ScreenBitmap(PictureBox cam, Rectangle rect)
{
camBox = cam;
camLocation = rect;
}
public Bitmap GetBitmap()
{
Bitmap screenBitmap = GetScreen();
return screenBitmap;
}
private Bitmap GetScreen()
{
Bitmap scBitmap = new Bitmap(camBox.Width, camBox.Height);
Graphics g = Graphics.FromImage(scBitmap);
g.CopyFromScreen(
camLocation.X,
camLocation.Y,
0,
0,
new Size(camBox.Width, camBox.Height)
);
return scBitmap;
}
}
I'm getting the pictureBox1 rectangle and then copying from the screen, but it looks like its not working. If I try the following code:
g.CopyFromScreen(
camLocation.X,
121,
0,
0,
new Size(camBox.Width, camBox.Height)
);
where 121 is a random number it works (I get an image, not the part I want, but it works) so the Y coordinate of the rectangle may be wrong? Or I'm missing something...
This will get you what is behind the PictureBox, with the opacity trick. The rest you can easily transfer to your code:
//just when you are about to capture screen take opacity and later restore it.
this.Opacity = 0.0;
Point first = PointToScreen(pictureBox1.Location);
Bitmap bit = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics g = Graphics.FromImage(bit);
g.CopyFromScreen(first.X,first.Y, 0, 0, pictureBox1.Size);
this.Opacity = 1.0;
pictureBox1.Image = bit;
You can test this code by creating a new WinForms project, add a Button and a PictureBox, and place this code in the Button's Click event handler.
I am having a problem on my extendedtabcontol class, I cannot get rid of the dotted box or the visual style box on the selected tab. I have my own DrawItem (see below), I have overridden several methods from the tabcontrol and I have even overridden the WM_SETFOCUS and WM_PAINT in WndProc but the box and highlight will not go away. Is there anyway to turn them off (the box or visual style) or a simple way to draw over them / stop them drawing?
The user can tell which tab is selected because it is drawn in black when the others are grey.
protected void OnDrawItem(object sender, DrawItemEventArgs e)
{
// VisualStyleRenderer renderer =
// new VisualStyleRenderer(VisualStyleElement.Tab.TabItem.Disabled);
DrawItemState ds = e.State;
SolidBrush mybrush = new SolidBrush(Color.FromArgb(255, 151, 0, 11));
Rectangle tabArea2 = new Rectangle(0, 0, this.Size.Width+10, this.Size.Height+10);
//renderer.DrawBackground(e.Graphics, tabArea2);
e.Graphics.FillRectangle(mybrush, tabArea2);
int i = 0;
foreach (TabPage tb in this.TabPages)
{
Rectangle tabArea = this.GetTabRect(i);
Point newp = new Point(tabArea.Location.X,tabArea.Location.Y + 2);
tabArea.Location = newp;
if (this.SelectedIndex != i)
{
RectangleF tabTextArea = (RectangleF)this.GetTabRect(i);
e.Graphics.DrawImage(global::Config.Properties.Resources.Tab2, tabArea.Location);
}
else
{
e.Graphics.DrawImage(global::Config.Properties.Resources.Tab1, tabArea.Location);
}
SizeF size = e.Graphics.MeasureString(tb.Name.ToString().Trim(), drawFont);
PointF pf = new PointF();
pf.X = tabArea.X + (tabArea.Width / 2) - (size.Width/2);
pf.Y = tabArea.Y + (tabArea.Height / 2) - (size.Height/2);
e.Graphics.DrawString(tb.Name.ToString().Trim(), drawFont, drawBrush, pf);
i++;
}
}
I would post an image but I don't have the reputation.
Similar question for example:
Can I remove the dotted focus rectangle over tabs on a TabControl?
Is it possible to Draw any Form (without overridding the Paint method) in grayscale.
If I show a Form in a Modal() Dialog, I wan't do show its parent as grayscale.
I noticed this in the Visual Studio Extension Manager. If a progressbar is downloading a package, the underlying window is grayed out.
I am thinking of this:
private void Button1_Click(object sender, EventArgs e)
{
using (var dialog = new Form2())
{
SetGrayscale(this, true);
dialog.ShowDialog();
SetGrayscale(this, false);
}
}
Update
Just setting Form.Enabled = false; is not what I intended. That does not look as good as a grayscale representation of my form.
I think the compiz window decorator for Linux did this with apps that are unresponsive.
As has already been said the way to do this is to overlay another control / form on top of your existing form and have it render a grayscale version of this on top, you could either do this using an additional form placed exactly over the original form, or using something like a Panel positioned on top of all other controls.
Here is a working example of how you might do this when placing another form exactly over the client area of the first. How to use it
using (Grayscale(this))
{
MessageBox.Show("Test");
}
Implementation
public static Form Grayscale(Form tocover)
{
var frm = new Form
{
FormBorderStyle = FormBorderStyle.None,
ControlBox = false,
ShowInTaskbar = false,
StartPosition = FormStartPosition.Manual,
AutoScaleMode = AutoScaleMode.None,
Location = tocover.PointToScreen(tocover.ClientRectangle.Location),
Size = tocover.ClientSize
};
frm.Paint += (sender, args) =>
{
var bmp = GetFormImageWithoutBorders(tocover);
bmp = ConvertToGrayscale(bmp);
args.Graphics.DrawImage(bmp, args.ClipRectangle.Location);
};
frm.Show(tocover);
return frm;
}
private static Bitmap ConvertToGrayscale(Bitmap source)
{
var bm = new Bitmap(source.Width, source.Height);
for (int y = 0; y < bm.Height; y++)
{
for (int x = 0; x < bm.Width; x++)
{
Color c = source.GetPixel(x, y);
var luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11);
bm.SetPixel(x, y, Color.FromArgb(luma, luma, luma));
}
}
return bm;
}
private static Bitmap GetControlImage(Control ctl)
{
var bm = new Bitmap(ctl.Width, ctl.Height);
ctl.DrawToBitmap(bm, new Rectangle(0, 0, ctl.Width, ctl.Height));
return bm;
}
private static Bitmap GetFormImageWithoutBorders(Form frm)
{
// Get the form's whole image.
using (Bitmap wholeForm = GetControlImage(frm))
{
// See how far the form's upper left corner is
// from the upper left corner of its client area.
Point origin = frm.PointToScreen(new Point(0, 0));
int dx = origin.X - frm.Left;
int dy = origin.Y - frm.Top;
// Copy the client area into a new Bitmap.
int wid = frm.ClientSize.Width;
int hgt = frm.ClientSize.Height;
var bm = new Bitmap(wid, hgt);
using (Graphics gr = Graphics.FromImage(bm))
{
gr.DrawImage(wholeForm, 0, 0,
new Rectangle(dx, dy, wid, hgt),
GraphicsUnit.Pixel);
}
return bm;
}
}
Note that:
The implementation of Paint is fairly poor - really it should use double buffering so that the grayscale image is pre-rendered to a buffered graphics context so the Paint method just needs to paint the pre-drawn buffer contents. See Custom Drawing Controls in C# – Manual Double Buffering
ConvertToGrayscale is a tad on the slow side, but can probably be sped up
Things will go wrong if someone manages to move the original form for any reason
The image is static, if the base control gets redrawn then ideally the top form should redraw too. I'm not sure how best to detect when a portion of another form has been invalidated.
If I find the time I'll try and fix some of those problems, but the above at least gives you the general idea.
Note that in WPF this would be a lot easier.
Sources:
How to convert a colour image to grayscale
Get the image of a control or form, or a form's client area in C#
I don't think there is a way to do it directly - I think all forms are rendered with sRGB.
A hacky way could be to overlay the form with a copy of it as an image (this is simple to do with Control.DrawToBitMap) and then pass it through a simple GDI matrix to desaturate https://web.archive.org/web/20141230145627/http://bobpowell.net/grayscale.aspx.
Try something like this which would work for most simple controls (you would need to recurse into containers to switch all controls correctly).
private void button1_Click(object sender, EventArgs e)
{
using (var dialog = new Form())
{
Dictionary<Control, Tuple<Color, Color>> oldcolors = new Dictionary<Control, Tuple<Color, Color>>();
foreach (Control ctl in this.Controls)
{
oldcolors.Add(ctl, Tuple.Create(ctl.BackColor, ctl.ForeColor));
// get rough avg intensity of color
int bg = (ctl.BackColor.R + ctl.BackColor.G + ctl.BackColor.B) / 3;
int fg = (ctl.ForeColor.R + ctl.ForeColor.G + ctl.ForeColor.B) / 3;
ctl.BackColor = Color.FromArgb(bg, bg, bg);
ctl.ForeColor = Color.FromArgb(fg, fg, fg);
}
dialog.ShowDialog();
foreach (Control ctl in this.Controls)
{
ctl.BackColor = oldcolors[ctl].Item1;
ctl.ForeColor = oldcolors[ctl].Item2;
}
}
}
I have a Winforms app that allows the user to drag and drop some labels around the screen.
The objective being to put the matching labels on top of each other.
I keep a reference to these labels in a list, and at the moment i'm checking to see if they're overlapping by doing the following.
foreach (List<Label> labels in LabelsList)
{
var border = labels[1].Bounds;
border.Offset(pnl_content.Location);
if (border.IntersectsWith(labels[0].Bounds))
{
labels[1].ForeColor = Color.Green;
}
else
{
labels[1].ForeColor = Color.Red;
}
}
The problem being that this is only good for Winforms (Bounds.Intersect). What can I do in WPF to achieve the same result?
If it makes a difference, i'm currently adding both labels to different <ItemsControl> in my view.
So thanks to the comments I was able to do what I needed.
The WPF code now looks like this for all those playing at home:
public void Compare()
{
foreach (List<Label> labels in LabelsList)
{
Rect position1 = new Rect();
position1.Location = labels[1].PointToScreen(new Point(0, 0));
position1.Height = labels[1].ActualHeight;
position1.Width = labels[1].ActualWidth;
Rect position2 = new Rect();
position2.Location = labels[0].PointToScreen(new Point(0, 0));
position2.Height = labels[0].ActualHeight;
position2.Width = labels[0].ActualWidth;
if (position1.IntersectsWith(position2))
{
labels[1].Foreground = new SolidColorBrush(Colors.Green);
continue;
}
labels[1].Foreground = new SolidColorBrush(Colors.Red);
}
}