.NET Forum / Languages / C# / March 2008
EventArgs and derived classes
|
|
Thread rating:  |
ACS - 03 Mar 2008 00:22 GMT Hello,
I'm wondering for certain events, such as a MouseClick event which uses MouseEventArgs, how are these EventArgs classes' members populated? i.e. how do the .X and .Y members get assigned?
I ask because for a project I'm working on, I'm making a custom ListView control with custom everything: ColumnHeaders, ListViewItems and ListViewSubItems.
My DrawSubItem delegate method by default gets passed a DrawListViewSubItemEventArgs class, but the .SubItem member points to a standard ListViewItem.ListViewSubItem class, where instead I want it to point to my custom SubItem class (in this case called SubItemEx.)
I presume I would have to create my own EventArgs classes, derived from DrawListViewSubItemEventArgs, which I've already done. However I have no idea how to assign the proper SubItemEx class, since I don't know WHERE the SubItem class is assigned.
Anyone know how to do this?
Peter Duniho - 03 Mar 2008 00:35 GMT > Hello, > > I'm wondering for certain events, such as a MouseClick event which > uses MouseEventArgs, how are these EventArgs classes' members > populated? i.e. how do the .X and .Y members get assigned? The same way they'd be assigned/initialized in any other class.
> [...] > I presume I would have to create my own EventArgs classes, derived [quoted text clipped - 3 lines] > > Anyone know how to do this? Have you looked at the constructor for the DrawListViewSubItemEventArgs class? http://msdn2.microsoft.com/en-us/library/system.windows.forms.drawlistviewsubite meventargs.drawlistviewsubitemeventargs.aspx
Assuming you are raising the event yourself, you don't need to create your own subclass of DrawListViewSubItemEventArgs. Just pass the appropriate value for the "subItem" parameter when instantiating the DrawListViewSubItemEventArgs object.
Pete
ACS - 03 Mar 2008 00:40 GMT On Mar 2, 7:35 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Hello, > [quoted text clipped - 21 lines] > > Pete Hi Pete,
Yes I've looked at the constructor. My problem is I don't know WHERE that class is created. My DrawSubItem event handler is called and that parameter, which is created and assigned behind-the-scenes, just gets passed. I need to know how and where it's populated.
Peter Duniho - 03 Mar 2008 01:11 GMT > Yes I've looked at the constructor. My problem is I don't know WHERE > that class is created. My DrawSubItem event handler is called and that > parameter, which is created and assigned behind-the-scenes, just gets > passed. I need to know how and where it's populated. Maybe you could be more clear on how you've actually overridden the default behavior then. You wrote in your original post that you've got "custom everything", including a custom ListViewSubItem.
If you're not raising the event yourself, then presumably you've left the original ListView implementation alone for that. But assuming you've replaced the ListViewSubItem instances in the ListView with your own sub-class, then those should already be in the DrawListViewSubItemEventArgs as the SubItem property.
The type of the property is ListViewSubItem, but assuming you've correctly overridden the rest of the implementation, then that should actually be an instance of your sub-class of ListViewSubItem. Just cast it to your sub-class of ListViewSubItem and it should work.
If that doesn't work, then you're not actually replacing the default implementation with your own as implied by your original post. A concise-but-complete code sample provided by you would go a long way toward helping others understand your question better.
Pete
ACS - 03 Mar 2008 01:53 GMT On Mar 2, 8:11 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Yes I've looked at the constructor. My problem is I don't know WHERE > > that class is created. My DrawSubItem event handler is called and that [quoted text clipped - 22 lines] > > Pete Hi Peter,
Ok, I understand what you mean. Since my ListViewSubItemEx class is derived directly from ListViewSubItem, then assuming I've overriden the latter correctly, the former should already be assigned as the .SubItem member.
I tested this out by merely casting from SubItem to SubItemEx in my DrawSubItem handler, with curious results. Just by merely performing the cast the function partially fails; before I was able to draw a particular bitmap in the subitem, but after I casted with:
ListViewSubItemEx subItem = (ListViewSubItemEx)e.SubItem;
Just this line by itself causes a completely unrelated failure. Very strange...
Anyways, here's my code: http://rafb.net/p/sqA5mr60.html
I test this code out by adding the ListViewEx control, a few columsn, and a couple of items. Then I set one of the subitem columns to have checkboxes, thusly:
lvxTest.Columns[1].HasCheckBox = true;
Now this works fine; the checkbox appears for each subitem in the specified column. But if I uncomment Line 53, then Line 60 completely fails. Of course this is a completely different problem, although it seems like it's related.
Any ideas?
ACS - 03 Mar 2008 02:03 GMT > On Mar 2, 8:11 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> > wrote: [quoted text clipped - 58 lines] > > Any ideas? Ok well, I found out the reason uncommenting Line 53 causes Line 60 to fail is, that if Line 53 is executed, the program will quite that function without processing anything else. I put some checkpoints in the method to verify this...
Peter Duniho - 03 Mar 2008 02:53 GMT > Ok, I understand what you mean. Since my ListViewSubItemEx class is > derived directly from ListViewSubItem, then assuming I've overriden > the latter correctly, the former should already be assigned as > the .SubItem member. Correct.
> I tested this out by merely casting from SubItem to SubItemEx in my > DrawSubItem handler, with curious results. Just by merely performing [quoted text clipped - 7 lines] > > Anyways, here's my code: http://rafb.net/p/sqA5mr60.html I wouldn't say that's a "concise-but-complete" example. It's neither concise (has lots of stuff that's not necessary), nor complete (can't be compiled and run by itself).
See Jon Skeet's article http://www.yoda.arachsys.com/csharp/complete.html for more details on what an appropriate sample would actually look like.
I will say that at first glance, the code you posted "smells funny". It's unusual for hiding a base method or property to be the right thing to do (e.g. lines 83, 87, 213, 224, etc.). Also, the cast on line 217 looks suspicious (but that may just be because there's so much other stuff in the example, that I can't find the other part of the code that makes that work). Why should you be able to cast the return value from the base class implementation to your own sub-class? How does the base class know to instantiate your sub-class?
It would be more appropriate for you to be explicitly instantiating your sub-classes, ListViewItem with the appropriate ListViewSubItem attached, adding the ListViewSubItem explicitly to the ListViewItem's sub-items, and then adding that ListViewItem explicitly to the ListView. Once you've done that, then those will be the instances used for drawing, which you can then reliably cast back to your own sub-classes.
From a design/maintainability standpoint, since the base classes don't offer virtual methods for adding things, obviously the interface wasn't designed with the intent that you be able to override those methods. Rather than hiding those methods, you should just provide alternative ways to populate the ListView (for example, new versions of the methods overloaded to take your custom sub-classes). You can hide the methods, but since that doesn't actually ensure that the base class methods don't get used somewhere else, it's a poor way to ensure that your sub-classed versions get called. It's better to just go ahead and be explicit about it, so that it's obvious elsewhere what's going on.
Anyway, if you can pare down your example to the bare minimum demonstrating the basic sub-classing of the ListViewItem and ListViewSubItem (leave out the column header stuff for sure), and provide a complete example, then maybe more useful advice can be provided.
Pete
Peter Duniho - 03 Mar 2008 04:14 GMT > [...] > It would be more appropriate for you to be explicitly instantiating your [quoted text clipped - 3 lines] > you've done that, then those will be the instances used for drawing, > which you can then reliably cast back to your own sub-classes. I've posted below an example of what I mean. I've only sub-classed ListViewSubItem, but the basic idea applies generally. I didn't bother putting any actual custom behavior in my sub-class, as it's sufficient for the example to demonstrate that the sub-class is indeed passed in the DrawListViewSubItemEventArgs class.
Also note that the DrawListViewSubItem event will be raised for each column in the details view, including the first one representing the item itself. The ListViewItem's SubItems collection always includes a ListViewSubItem representing itself as the first element in the collection. It's possible you could replace this element with your own sub-class, but I didn't try that and it seems hazardous to me. As long as in your handler for DrawListViewSubItem you make sure that you're dealing with that case properly, by only casting to your own class when appropriate, I don't think it's necessary to replace the ListViewSubItem that the ListViewItem puts there itself.
I hope the example helps. As you can see, there's not really any need to override the various collection and main control classes, if you are not actually wanting to override the behavior of those classes. Just override the classes you really need to override, and let the other classes do their jobs as normal.
Pete
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Diagnostics;
namespace TestCustomListView { public class Form1 : Form { public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e) { if (textBox1.Text != "") { ListViewItem lvi = new ListViewItem(textBox1.Text);
lvi.SubItems.Add(new MyListViewSubItem(lvi, "sub item (" + textBox1.Text + ")"));
listView1.Items.Add(lvi); } }
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { MyListViewSubItem mysubitem = e.SubItem as MyListViewSubItem;
if (mysubitem != null) { Debug.WriteLine("mysubitem class: " + mysubitem.GetType().Name); }
e.DrawBackground(); e.DrawText(); }
private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) { e.DrawDefault = true; }
/// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null;
/// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }
#region Windows Form Designer generated code
/// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.textBox1 = new System.Windows.Forms.TextBox(); this.columnHeader1 = new System.Windows.Forms.ColumnHeader(); this.columnHeader2 = new System.Windows.Forms.ColumnHeader(); this.listView1 = new System.Windows.Forms.ListView(); this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(13, 13); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(75, 23); this.button1.TabIndex = 0; this.button1.Text = "button1"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(94, 15); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(100, 20); this.textBox1.TabIndex = 1; // // columnHeader1 // this.columnHeader1.Text = "Item"; // // columnHeader2 // this.columnHeader2.Text = "Sub-Item"; this.columnHeader2.Width = 93; // // listView1 // this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.columnHeader1, this.columnHeader2}); this.listView1.Location = new System.Drawing.Point(13, 43); this.listView1.Name = "listView1"; this.listView1.OwnerDraw = true; this.listView1.Size = new System.Drawing.Size(399, 254); this.listView1.TabIndex = 2; this.listView1.UseCompatibleStateImageBehavior = false; this.listView1.View = System.Windows.Forms.View.Details; this.listView1.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.listView1_DrawSubItem); this.listView1.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.listView1_DrawColumnHeader); // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(424, 309); this.Controls.Add(this.listView1); this.Controls.Add(this.textBox1); this.Controls.Add(this.button1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1; private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.ColumnHeader columnHeader1; private System.Windows.Forms.ColumnHeader columnHeader2; private System.Windows.Forms.ListView listView1; }
class MyListViewSubItem : ListViewItem.ListViewSubItem { public MyListViewSubItem(ListViewItem owner, string strItem) : base(owner, strItem) { } }
static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } }
ACS - 05 Mar 2008 04:03 GMT Hi Peter,
Ah, I see what you mean.
Well the only reason I overrided ListViewItem and the collections is that I wanted this whole thing to be transparent-- that is, when using the ListViewEx control, all the methods, members and properties remain exactly the same to the user, it's just the underlying functionality that's different. But I suppose you have a point-- override only what I need change and leave everything else intact. It would certainly make my code a lot easier.
I have a question though about the way you performed your cast--
MyListViewSubItem mysubitem = e.SubItem as MyListViewSubItem;
I don't recognize the "as" keyword. I would perform that cast as follows:
MyListViewSubItem mysubitem = (MyListViewSubItem)e.SubItem;
Do they not do the same thing? I've used the latter casting method in some of my other programs and it seems to work properly. If there is a difference, what is it?
On Mar 2, 11:14 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> On Sun, 02 Mar 2008 18:53:48 -0800, Peter Duniho > [quoted text clipped - 215 lines] > > } Peter Duniho - 05 Mar 2008 04:25 GMT > Hi Peter, > [quoted text clipped - 5 lines] > exactly the same to the user, it's just the underlying functionality > that's different. Well, you can build on what I showed and do that. But you'll run into issues wherever the base class wasn't designed to be overridden. That may not be so bad when you're just returning stuff, but methods that add instances will always have the possibility of your class being bypassed by an upcast to the base type.
> But I suppose you have a point-- override only what I need change and > leave everything else intact. It would certainly make my code a lot > easier. I think so. :) It also makes it clearer as to what's really going on, given the lack of support for overriding the base class behaviors. Since you can't guarantee that your subclasses will always be used, you might as well just be very explicit about doing so. That way the contract is more clear.
> I have a question though about the way you performed your cast-- > [quoted text clipped - 8 lines] > some of my other programs and it seems to work properly. If there is a > difference, what is it? They do _almost_ the same thing. The difference is, a straight cast will throw an exception if the original reference cannot be validly cast to the target type. With the "as" keyword, the result will simply be a null reference if the cast is invalid.
I used it there because not all of the e.SubItem references will be castable to MyListViewSubItem. So I use "as", and check for null.
Other than that, it's the same as casting. For references that are valid to cast to the target type, there is no difference.
Generally speaking, I use a regular cast when it's an error for the original reference to fail to be cast (i.e. absent any bugs, no exception will ever be thrown), and I use "as" when it's not an error (i.e. I'm going to include some code that checks for a null result and handles it appropriately).
Pete
ACS - 05 Mar 2008 05:19 GMT Hi Peter,
Alright, well this is certainly helping, my code is much cleaner now! :)
The problem is, the DrawSubItem event handler won't work when drawing the main ListView item. Now I know that SubItem[0] is SUPPOSED to be the main ListView item, in which case the event handler should work the same way.
However I'm finding that the method won't properly draw the first item, because of course the cast to MyListViewSubItem fails for that column. Other than creating an if-else clause to check if the cast fails, and then write a duplicate piece of code to work with e.Item rather than e.SubItem, is there a way around this?
On Mar 4, 11:25 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Hi Peter, > [quoted text clipped - 53 lines] > > Pete Peter Duniho - 05 Mar 2008 07:12 GMT > Hi Peter, > [quoted text clipped - 12 lines] > then write a duplicate piece of code to work with e.Item rather than > e.SubItem, is there a way around this? Well, that depends on what your code looks like. If you have some code that should work equally well whether or not the cast succeeds, then that code doesn't really need a MyListViewSubItem instance, but rather can work with a regular ListViewSubItem. For that code, just always execute it on the e.SubItem property directly regardless of the result of the cast. Then, whatever code really does need a MyListViewSubItem, only make that part conditional on the success of the cast (and of course, use the result of the cast in that case).
Without more specifics, I'm afraid I can't really comment on how exactly you'd handle the situation. I really don't think you should _need_ to duplicate code, but it's certainly possible that the way you've structured your code now that that's something that looks like you'd have to do.
If you post the event handler, maybe I can offer more specific advice.
Pete
ACS - 06 Mar 2008 01:49 GMT Hi Pete,
That's what I'm trying to do at the moment. I'm only using MyListViewSubItem when absolutely needed. Basically I'm trying to implement checkboxes for every SubItem, not just the main Item.
Actually I find that the event IS handling the main Item properly, because if I create a new ListViewSubItemEx and assign it to subItem instead of e.subItem, the method DOES work. Furthermore I tried checking if subItem == null after a cast and it NEVER seems to fail. So something else is going on here.
Here's my event handler code...
// Method for handling drawing of the subitems (and the main item, which is index 0) private void ListViewEx_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { Graphics g = e.Graphics; StringFormat fmt = new StringFormat(); ListViewSubItemEx subItem;
fmt.Trimming = StringTrimming.EllipsisCharacter; // Ensure ellipses appear if the text won't fit if ((this.Columns[e.ColumnIndex] as ColumnHeaderEx).HasCheckbox == true) { subItem = e.SubItem as ListViewSubItemEx; if (subItem.Checked == true) g.DrawImage(new Bitmap(GetType(), "checked.bmp"), e.Bounds.Left + 1, e.Bounds.Top + 1); else g.DrawImage(new Bitmap(GetType(), "unchecked.bmp"), e.Bounds.Left + 1, e.Bounds.Top + 1); g.DrawString(e.SubItem.Text, this.Font, new SolidBrush(Color.Black), new Rectangle(e.Bounds.Left + 15, e.Bounds.Top, e.Bounds.Width - 15, e.Bounds.Height), fmt); } else e.DrawText(TextFormatFlags.EndEllipsis); }
On Mar 5, 2:12 am, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Hi Peter, > [quoted text clipped - 30 lines] > > Pete ACS - 06 Mar 2008 01:52 GMT Another thing--
I realize the standard ListViewItem already has a .Checked property, however since I want to draw my own checkboxes I'd rather handle the drawing in the above method. If push comes to shove I can always custom-draw it in another handler method for ListViewItem. However it would be nice if I could figure out WHY this one isn't working.
> Hi Pete, > [quoted text clipped - 78 lines] > > > Pete Peter Duniho - 06 Mar 2008 02:49 GMT > Hi Pete, > [quoted text clipped - 8 lines] > Furthermore I tried checking if subItem == null after a cast and it > NEVER seems to fail. So something else is going on here. Well, you haven't been very specific about what's going wrong. In your previous post, you said that the cast was failing because the first ListViewSubItem isn't your own class. But now you are saying that even for the first column, there's no problem.
Can you confirm that all of your column headers are your own ColumnHeaderEx class, and that they always have "true" for the HasCheckbox property? If the first condition doesn't hold, I would expect you to get a null-reference exception on the first if() in your handler. If the first condition holds, but the second condition doesn't for the first column, then the first column is always going to wind up just calling e.DrawText(), rather than going through the block of code that draws a checkbox.
Basically, the code you posted and your current description do not match what you wrote before. So it's not an example that helps understand your previous question.
At this point, I'm back to suggesting that you post a concise-but-complete sample of code that reliably demonstrates whatever your question is asking about. Again, please make sure it's really concise. _Nothing_ extra that's not immediately related to your question. Otherwise, it's much less likely I or anyone else will bother trying to do anything with it.
I'll take as granted that there's something about the checkboxes that the built-in class doesn't provide that you really need. Generally speaking, you should think twice before customing a UI like this, because it can potentially be confusing to the user. I'm assuming you've already gone through the process of determining that this is really necessary.
Pete
ACS - 06 Mar 2008 03:41 GMT Hi Pete,
Ok, here's a (hopefully) concise-but-complete listing.
http://utilitybase.com/paste/6152
I've tried to remove everything extra, leaving in only what's necessary. This single-file code will compile all on its own. The only stipulation is that two files, named "checked.bmp" and "unchecked.bmp" should be in the same directory. They can be any valid image, really; it's enough to demonstrate the problem.
I'll try to clarify things a bit further:
The event handler ListViewEx_DrawSubItem is correctly being called for all subitems, including the subitem at index 0 (which is the main item.) However, as you will see upon running this program, the main item will NEVER draw properly. It always draws the default text without any image, despite my repeated efforts to make it draw as just another subitem.
In regards to your question, I'm sure that the first if statement (line 116) is always executing, since the else clause (displaying "No check." in RED) is never executed. I've further verified this by replacing Line 127 with a MessageBox statement, and I saw no message box.
I've even tried stepping through the program line-by-line and found that when it comes to the if statement on Line 119, the program will jump back to the beginning of the ListViewEx_DrawSubItem handler, and then draw the default text in the main item. I don't see why it would do this-- it seems to be "crashing" at that point (I know not the right terminology, but I don't know what else to call it.)
On Mar 5, 9:49 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Hi Pete, > [quoted text clipped - 40 lines] > > Pete Peter Duniho - 06 Mar 2008 04:18 GMT > Hi Pete, > [quoted text clipped - 7 lines] > should be in the same directory. They can be any valid image, really; > it's enough to demonstrate the problem. Well, that's not concise or complete. Unless the bitmaps are directly related to the specific question (and I doubt they are), they don't belong in the sample, nor does any code that depends on them. That breaks "concise", and the fact that the images are not included breaks "complete".
It also appears that you're doing customization of the column headers and sub items. Again, that breaks "concise", since it doesn't really appear that the customization of the column headers has anything to do with the customization of the sub items (after all, I was able to post a working example of customized sub items that didn't touch the column headers).
Also, rather than posting your code somewhere else, you should be including it in your message. I'll grant that it makes it slightly less convenient to reference specific lines in the code, but these threads go in archives, and it's much better for the lifetime of the post to match the lifetime of any referenced material. The only way to guarantee that is for all referenced material to be a part of the post.
> I'll try to clarify things a bit further: > [quoted text clipped - 5 lines] > image, despite my repeated efforts to make it draw as just another > subitem. If the main item is not represented by a ListViewSubItemEx instance, then why would you expect it to draw otherwise? And how is the default drawing not considered "properly", considering that the main "0 column" sub item isn't your custom class?
> In regards to your question, I'm sure that the first if statement > (line 116) is always executing, since the else clause (displaying "No [quoted text clipped - 8 lines] > do this-- it seems to be "crashing" at that point (I know not the > right terminology, but I don't know what else to call it.) Well, that sounds to me as though an exception is happening, but it's being caught. This isn't necessarily all that unusual. It's possible that the ListView class is catching the exception, and upon doing so reverts to default behavior. It's protecting itself from your bug.
So, what is your bug? Well, the most obvious issue is that you're using "as" but you never bother to check whether the cast succeeded. You might as well just do a straight cast (and the code likely would be just as wrong in that case).
It _seems_ to me that a better way to write the handler might be like this:
private void ListViewEx_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { Graphics g = e.Graphics; StringFormat fmt = new StringFormat();
fmt.Trimming = StringTrimming.EllipsisCharacter; // Ensure ellipses appear if the text won't fit if ((this.Columns[e.ColumnIndex] as ColumnHeaderEx).HasCheckbox == true) { ListViewSubItemEx subItem = e.SubItem as ListViewSubItemEx;
if (subItem != null && subItem.Checked == true) g.DrawImage(Image.FromFile(".\checked.bmp"), e.Bounds.Left + 1, e.Bounds.Top + 1); else g.DrawImage(Image.FromFile(".\unchecked.bmp"), e.Bounds.Left + 1, e.Bounds.Top + 1); g.DrawString(e.SubItem.Text, this.Font, new SolidBrush(Color.Black), new Rectangle(e.Bounds.Left + 15, e.Bounds.Top, e.Bounds.Width - 15, e.Bounds.Height), fmt); } else g.DrawString("No check.", this.Font, new SolidBrush(Color.Red), e.Bounds); }
However, it seems a bit odd to me that you'd have a column that has the "HasCheckbox" property set to "true" if the items in that column aren't your ListViewSubItemEx instances. In fact, why put a special "ColumnHeaderEx" instance in a column header that won't have a "ListViewSubItemEx" as the items in that column?
If you made the column headers and sub items correlate better, such that any column that has an extended header will always have extended sub items in the column, you'd wind up with code that's more like this:
private void ListViewEx_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { Graphics g = e.Graphics; StringFormat fmt = new StringFormat(); ColumnHeaderEx header = Columns[e.ColumnIndex] as ColumnHeaderEx;
fmt.Trimming = StringTrimming.EllipsisCharacter; // Ensure ellipses appear if the text won't fit if (header != null && header.HasCheckbox) { ListViewSubItemEx subItem = (ListViewSubItemEx)e.SubItem;
using(Image img = subItem.Checked ? Image.FromFile(".\checked.bmp") : Image.FromFile(".\unchecked.bmp")) { g.DrawImage(img, e.Bounds.Left + 1, e.Bounds.Top + 1); }
g.DrawString(e.SubItem.Text, this.Font, new SolidBrush(Color.Black), new Rectangle(e.Bounds.Left + 15, e.Bounds.Top, e.Bounds.Width - 15, e.Bounds.Height), fmt); } else g.DrawString("No check.", this.Font, new SolidBrush(Color.Red), e.Bounds); }
Finally, in rearranging the above code, I noticed you are a) creating a new image each time you call this method (inefficient) and b) failing to dispose the image (VERY bad). I fixed the lack of disposal, but really you should just be loading the bitmaps once as static elements in the class that uses them, and then refer to those static instances. Creating a new image each time the sub item is drawn is just not a good approach.
Hope that helps.
Pete
ACS - 07 Mar 2008 23:01 GMT Hi Peter,
Well thanks to all your help I've finally figured out what's going on! :)
Turns out that I was assuming SubItem[0] was automatically a SubItemEx when actually it was just a plain old SubItem. So I added in some code to ensure that SubItem[0] will always be a SubItemEx, and now it works perfectly!
Thanks for all your help.
On Mar 5, 11:18 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Hi Pete, > [quoted text clipped - 145 lines] > > Pete ACS - 05 Mar 2008 05:19 GMT Hi Peter,
Alright, well this is certainly helping, my code is much cleaner now! :)
The problem is, the DrawSubItem event handler won't work when drawing the main ListView item. Now I know that SubItem[0] is SUPPOSED to be the main ListView item, in which case the event handler should work the same way.
However I'm finding that the method won't properly draw the first item, because of course the cast to MyListViewSubItem fails for that column. Other than creating an if-else clause to check if the cast fails, and then write a duplicate piece of code to work with e.Item rather than e.SubItem, is there a way around this?
On Mar 4, 11:25 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Hi Peter, > [quoted text clipped - 53 lines] > > Pete
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|