Changing the font in a listbox

  • Thread starter Thread starter Dom
  • Start date Start date
D

Dom

In some programs, I see that certain items in a listbox have been
grayed, to indicate that they are "there" but can not be chosen. This
is very helpful. Can you do this in CSharp? Is there a
lstMain.SelectedItem.Enabled?

Dom
 
In some programs, I see that certain items in a listbox have been
grayed, to indicate that they are "there" but can not be chosen. This
is very helpful. Can you do this in CSharp? Is there a
lstMain.SelectedItem.Enabled?

Dom

No, but you can make it appear non selectable by ownerdrawing the ListBox.

[pseudocode]
OnDrawItem(DrawItemEventArgs e)
{
MyObject o = (MyObject)listMain.Items[e.Index];
if( o.NotSelectable )
// DrawGrayedOut
else
// DrawNormal
}
 
Dom said:
In some programs, I see that certain items in a listbox have been
grayed, to indicate that they are "there" but can not be chosen. This
is very helpful. Can you do this in CSharp? Is there a
lstMain.SelectedItem.Enabled?


I don't believe that the ListBox class supports this directly. I
haven't tried this, but I think you might be able to do it yourself by
deriving a new class from ListBox and in that class:

* Keep your own list of disabled items.

* Override OnDrawItem, and when drawing a disabled item, change the
font color before calling the base.OnDrawItem() method and then
restoring the font color to what it was before.

* Override WndProc, and before allowing a mouse click to be passed
to the base.WndProc() method, use the IndexFromPoint() method to
determine the item being clicked, and don't call the base.WndProc()
method if it's a disabled item.

* Override ProcessDialogKey, and just as you intercept mouse
events, intercept the keyboard events that might select a disabled item,
and do something to prevent that (IMHO the most user-friendly way would
be to repeat the keyboard events as necessary to skip over disabled
items, but there are a variety of ways you could choose to do this).

That's the general idea...I may have missed some specifics, or it may
turn out that there are better ways to do some of the above. As you can
see, it would be non-trivial to implement the behavior you want, but it
is probably less difficult to do that than to write a whole new listbox
class that does what you want. :)

Pete
 
In some programs, I see that certain items in a listbox have been
grayed, to indicate that they are "there" but can not be chosen. This
is very helpful. Can you do this in CSharp? Is there a
lstMain.SelectedItem.Enabled?

No, but you can make it appear non selectable by ownerdrawing the ListBox.

[pseudocode]
OnDrawItem(DrawItemEventArgs e)
{
MyObject o = (MyObject)listMain.Items[e.Index];
if( o.NotSelectable )
// DrawGrayedOut
else
// DrawNormal

}

This led me to something in the help files. Thanks. Before I get
started, how do I trigger the OnDrawItem? Let me give you a fuller
picture.

I have two listboxes, side by side. One is the "Not included" list,
and the other is the "Included" list. The user can move an item back
and forth between these listboxes by buttons. After an item moves
from the "Not included" list to the "included list", I want it to
appear grayed out, indicating that it is already used up.

So I guess I have to get the button to trigger the OnDrawItem event.
How is that done?

Dom
 
Dom said:
I have two listboxes, side by side. One is the "Not included" list,
and the other is the "Included" list. The user can move an item back
and forth between these listboxes by buttons. After an item moves
from the "Not included" list to the "included list", I want it to
appear grayed out, indicating that it is already used up.

So I guess I have to get the button to trigger the OnDrawItem event.
How is that done?

The easiest way is to simply invalidate the entire control. See the
Invalidate() method. OnDrawItem is a method, not an event, and it gets
called when the control needs to be redrawn, in response to invalidation
of the control.

Personally, as a UI paradigm I think it's better to remove the items
altogether rather than disabling them. This fits better with the "move"
paradigm that you seem to be using already. And as an added benefit,
implementing that would be a LOT easier than dealing with disabling the
list items (I've done the former myself, and it's only 20-30 lines of
code, none of them nearly as tricky as overriding basic Window message
event logic in the control).

Pete
 
In some programs, I see that certain items in a listbox have been
grayed, to indicate that they are "there" but can not be chosen. This
is very helpful. Can you do this in CSharp? Is there a
lstMain.SelectedItem.Enabled?

No, but you can make it appear non selectable by ownerdrawing the ListBox.

[pseudocode]
OnDrawItem(DrawItemEventArgs e)
{
MyObject o = (MyObject)listMain.Items[e.Index];
if( o.NotSelectable )
// DrawGrayedOut
else
// DrawNormal

}

This led me to something in the help files. Thanks. Before I get
started, how do I trigger the OnDrawItem? Let me give you a fuller
picture.

I have two listboxes, side by side. One is the "Not included" list,
and the other is the "Included" list. The user can move an item back
and forth between these listboxes by buttons. After an item moves
from the "Not included" list to the "included list", I want it to
appear grayed out, indicating that it is already used up.

So I guess I have to get the button to trigger the OnDrawItem event.
How is that done?

Dom

OnDrawItem is enabled when you set DrawMode to either of the OwnerDraw modes (OwnerDrawFixed is what you need here). In addition you need to subscribe to the ListBox' DrawItem event. The code below demonstrates theprinciples. You can still select the item, though, but it won't appearselected. To simulate an item that should be disabled I used three string items "one", "two", and "three" and item "two" appears with gray background and text.

protected override void OnLoad(EventArgs e)
{
listBox1.DrawMode = DrawMode.OwnerDrawFixed;
listBox1.DrawItem += new DrawItemEventHandler(listBox1_DrawItem);
}

void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
if (listBox1.Items[e.Index].ToString() == "Two")
{
e.Graphics.FillRectangle(Brushes.Gray, e.Bounds);
e.Graphics.DrawString(listBox1.Items[e.Index].ToString(), listBox1.Font, SystemBrushes.InactiveCaptionText, e.Bounds);
}
else
{
e.DrawBackground();
if ((e.State & DrawItemState.Focus) > 0)
e.Graphics.DrawString(listBox1.Items[e.Index].ToString(), listBox1.Font, SystemBrushes.HighlightText, e.Bounds);
else
e.Graphics.DrawString(listBox1.Items[e.Index].ToString(), listBox1.Font, SystemBrushes.ControlText, e.Bounds);
}
}
 
The easiest way is to simply invalidate the entire control. See the
Invalidate() method. OnDrawItem is a method, not an event, and it gets
called when the control needs to be redrawn, in response to invalidation
of the control.

Personally, as a UI paradigm I think it's better to remove the items
altogether rather than disabling them. This fits better with the "move"
paradigm that you seem to be using already. And as an added benefit,
implementing that would be a LOT easier than dealing with disabling the
list items (I've done the former myself, and it's only 20-30 lines of
code, none of them nearly as tricky as overriding basic Window message
event logic in the control).

Pete

Yes, I'm starting to think it is better just to remove the item. It's
a shame though, we really should be easy to do
"lstMain.SelectedItem.Enabled = false".
 
In some programs, I see that certain items in a listbox have been
grayed, to indicate that they are "there" but can not be chosen. This
is very helpful. Can you do this in CSharp? Is there a
lstMain.SelectedItem.Enabled?

Dom

Actually, guys, it turned out to be very simple. Here's what I did:

private void btnSelect_Click(object sender, EventArgs e)
{
((ListItem)lstMain.SelectedItem).IsSelected = true;
lstMain.Invalidate();
}



private void lstMain_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
e.DrawBackground();
Brush myBrush = ((ListItem)lstMain.Items[e.Index]).IsSelected ?
Brushes.Gray : Brushes.Black;

e.Graphics.DrawString(lstMain.Items[e.Index].ToString(),
e.Font, myBrush,
e.Bounds,
StringFormat.GenericDefault);
e.DrawFocusRectangle();
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top