Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / March 2008

Tip: Looking for answers? Try searching our database.

EventArgs and derived classes

Thread view: 
Enable EMail Alerts  Start New Thread
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

Rate this thread:







Free Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.