displaying bitflags as enumerated text and not int

P

Peted

Hi

i am trying to make a small mdoification to and exisiting dll control,
in its source code.

Im trying to expose one of its internal properties so the user can set
it in the ide at designtime, but the property consists of enumerated
constants used in bit operations, and when the values are exposed they
show up as integers instead of the text contstants listed


EG

//Initial value
private int dwFlags = Gdi.PFD_DRAW_TO_WINDOW |
Gdi.PFD_SUPPORT_OPENGL | Gdi.PFD_DOUBLEBUFFER;

// I expose the property to the ide user here
#region DWFlags
/// <summary>
/// sets the OpenGL control's Flag properties.
/// </summary>
[Category("OpenGL Properties"), Description("Flag
properties.")]
public String DWFlags {
get {
return dwFlags);
}
set {
dwFlags = value);
}
}
#endregion DWFlags


When you see it in the ide the DWFlags value shows as 37, but i want
it to show as
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;

in the control field.

I also want the user to be able to enter the flags as the above text,
even adding more flags to also be used in this bit operation and have
it translated or sent to the control in form that it can be used.

the flags are used in the following opengl structure

Gdi.PIXELFORMATDESCRIPTOR pfd = new Gdi.PIXELFORMATDESCRIPTOR();// The
pixel format descriptor
pfd.nSize = (short) Marshal.SizeOf(pfd);
// Size of the pixel format descriptor
pfd.nVersion = 1;
// Version number (always 1)


--------------->>>> i want to replace this line bellow

pfd.dwFlags =Gdi.PFD_DRAW_TO_WINDOW |
Gdi.PFD_SUPPORT_OPENGL | Gdi.PFD_DOUBLEBUFFER;

----------------------------->>>> with pfd.dwFlags = dwFlags;





pfd.iPixelType = (byte) Gdi.PFD_TYPE_RGBA;


etc etc etc etc


You can see the flags are enumerated constants from opengl


Can anyone help please on how i can pass these flag data beckwards and
forwards to the control and have the user edit it, and still have it
come back and be usuable in the structure


thanks for any help

Peted
 
M

Marc Gravell

OK, you have an odd combination of int, string (so that your posted
code doesn't compile), and what perhaps could be an enum. Realistic
code would help people give a realistic answer!

However; *assuming* that Gdi doesn't declare a suitable enum, I would
simply do it myself; the following does most of what you want. If you
want a check-box style value-editor, then you'll need to add a custom
[Editor]; I'm pretty certain you could pick up a popup (or pop-down)
check-box style editor easily enough (it would take about 10 minutes
to write one from scratch).

If you don't want to go down the enum route, then you'd need to write
a TypeConverter, and specify this against the property; then you can
handle the parsing and formatting yourself - but I'd recommend using
enum!

Of course, if the "Gdi" class is declared in your project, then simply
define the enum values directly. Note that you can cast directly from
enum to/from int to get the int value to pass to whatever needs it.

using System;
using System.ComponentModel;
using System.Windows.Forms;
static class Program {
static void Main() {
Application.EnableVisualStyles();
using(Form form = new Form())
using (PropertyGrid grid = new PropertyGrid()) {
grid.Dock = DockStyle.Fill;
grid.SelectedObject = new SomeClass();
form.Controls.Add(grid);
Application.Run(form);
}
}
}
[Flags]
enum GdiDwFlags {
None = 0,
DrawToWindow = Gdi.PFD_DRAW_TO_WINDOW,
SupportOpenGl = Gdi.PFD_SUPPORT_OPENGL,
DoubleBuffer = Gdi.PFD_DOUBLEBUFFER
}
static class Gdi { // I'm making up the values... presumably defined
externally
public const int PFD_DRAW_TO_WINDOW = 1, PFD_SUPPORT_OPENGL = 2,
PFD_DOUBLEBUFFER = 4;
}
class SomeClass {
private GdiDwFlags dwFlags = GdiDwFlags.DrawToWindow |
GdiDwFlags.SupportOpenGl | GdiDwFlags.DoubleBuffer;
[Category("OpenGL Properties"), Description("Flag properties.")]
public GdiDwFlags DWFlags {
get {return dwFlags;}
set {dwFlags = value;}
}
}
 
M

Marc Gravell

And the same but with a multi-select editor:

class SomeClass {
private GdiDwFlags dwFlags = GdiDwFlags.DrawToWindow |
GdiDwFlags.SupportOpenGl | GdiDwFlags.DoubleBuffer;
[Category("OpenGL Properties"), Description("Flag properties.")]
[Editor(typeof(CheckBoxEnumEditor), typeof(UITypeEditor))]
public GdiDwFlags DWFlags {
get {return dwFlags;}
set {dwFlags = value;}
}
}
class CheckBoxEnumEditor : UITypeEditor {
public override UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context) {
return UITypeEditorEditStyle.DropDown;
}
public override bool IsDropDownResizable {
get {return true;}
}
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value) {
Type enumType = null;
if (context != null && context.PropertyDescriptor != null)
enumType = context.PropertyDescriptor.PropertyType;
IWindowsFormsEditorService svc = provider == null ? null :
provider.GetService(typeof(IWindowsFormsEditorService)) as
IWindowsFormsEditorService;
if (enumType !=null &&enumType.IsEnum && svc != null) {
using (CheckBoxEnumControl editor = new
CheckBoxEnumControl(enumType)) {
editor.Value = (int) value;
editor.ValueSelected += delegate
{svc.CloseDropDown();};
svc.DropDownControl(editor);
value = editor.Value;
}
}
return value;
}
}
class CheckBoxEnumControl : UserControl {
CheckedListBox box;
private static readonly object EVENT_ValueSelected = new object();
public event EventHandler ValueSelected {
add { Events.AddHandler(EVENT_ValueSelected, value); }
remove { Events.RemoveHandler(EVENT_ValueSelected, value); }
}
protected virtual void OnValueSelected() {
EventHandler handler = Events[EVENT_ValueSelected] as
EventHandler;
if (handler != null) handler(this, EventArgs.Empty);
}
public CheckBoxEnumControl(Type enumType) {
Button b = new Button();
b.Text = "OK";
b.Dock = DockStyle.Bottom;
b.Click += delegate { OnValueSelected(); };
Controls.Add(b);
box = new CheckedListBox();
box.Dock = DockStyle.Fill;
foreach (object value in Enum.GetValues(enumType)) {
if ((int)value != 0) {
box.Items.Add(value);
}
}
Controls.Add(box);
}
public int Value {
get {
int value = 0;
foreach (int val in box.CheckedItems) {
value |= val;
}
return value;
}
set {
for (int i = 0; i < box.Items.Count; i++) {
int val = (int) box.Items;
box.SetItemChecked(i, (value & val) == val);
}
}
}
}
 
P

Peted

Hi Marc

thankyou for your effort, i apollogies my description of what i was
after was realy bad.

I will repost my question with a better explanation

Peted
 
P

Peted

Hi Marc,

you are correct, this is a better way especially with the checkedlist
box to make the selection.

Its not a solution that would have accured to me, i spent a lot of
time looking at any number of other other options that all had one
flaw or another.

Iunderstand most of what is happening in this code, but some o what is
done in creating the checked listbox control is obscure to me,
especially in the adding of events to the button click.

I was hoping you would have time to add comments to clear up exactly
what is happening in the code.

The control is the simpleopenglcontrol part of the opengl mono
bindings of the TAO framework.

thanks for any help

Peted



And the same but with a multi-select editor:

class SomeClass {
private GdiDwFlags dwFlags = GdiDwFlags.DrawToWindow |
GdiDwFlags.SupportOpenGl | GdiDwFlags.DoubleBuffer;
[Category("OpenGL Properties"), Description("Flag properties.")]
[Editor(typeof(CheckBoxEnumEditor), typeof(UITypeEditor))]
public GdiDwFlags DWFlags {
get {return dwFlags;}
set {dwFlags = value;}
}
}
class CheckBoxEnumEditor : UITypeEditor {
public override UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context) {
return UITypeEditorEditStyle.DropDown;
}
public override bool IsDropDownResizable {
get {return true;}
}
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value) {
Type enumType = null;
if (context != null && context.PropertyDescriptor != null)
enumType = context.PropertyDescriptor.PropertyType;
IWindowsFormsEditorService svc = provider == null ? null :
provider.GetService(typeof(IWindowsFormsEditorService)) as
IWindowsFormsEditorService;
if (enumType !=null &&enumType.IsEnum && svc != null) {
using (CheckBoxEnumControl editor = new
CheckBoxEnumControl(enumType)) {
editor.Value = (int) value;
editor.ValueSelected += delegate
{svc.CloseDropDown();};
svc.DropDownControl(editor);
value = editor.Value;
}
}
return value;
}
}
class CheckBoxEnumControl : UserControl {
CheckedListBox box;
private static readonly object EVENT_ValueSelected = new object();
public event EventHandler ValueSelected {
add { Events.AddHandler(EVENT_ValueSelected, value); }
remove { Events.RemoveHandler(EVENT_ValueSelected, value); }
}
protected virtual void OnValueSelected() {
EventHandler handler = Events[EVENT_ValueSelected] as
EventHandler;
if (handler != null) handler(this, EventArgs.Empty);
}
public CheckBoxEnumControl(Type enumType) {
Button b = new Button();
b.Text = "OK";
b.Dock = DockStyle.Bottom;
b.Click += delegate { OnValueSelected(); };
Controls.Add(b);
box = new CheckedListBox();
box.Dock = DockStyle.Fill;
foreach (object value in Enum.GetValues(enumType)) {
if ((int)value != 0) {
box.Items.Add(value);
}
}
Controls.Add(box);
}
public int Value {
get {
int value = 0;
foreach (int val in box.CheckedItems) {
value |= val;
}
return value;
}
set {
for (int i = 0; i < box.Items.Count; i++) {
int val = (int) box.Items;
box.SetItemChecked(i, (value & val) == val);
}
}
}
}
 
M

Marc Gravell

Basically, when things like PropertyGrid want to ask for user input,
they will look for a "UITypeEditor"; we create a new subclass of
UITypeEditor for our specific implementation, and use the
EditorAttribute to tell the runtime about it
(snip)
[Editor(typeof(CheckBoxEnumEditor), typeof(UITypeEditor))]




class CheckBoxEnumEditor : UITypeEditor {
// our custom helper should appear in place of a drop-down, just
like "Docked" etc
public override UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context) {
return UITypeEditorEditStyle.DropDown;
}
// our control is fine when resized (since we dock-fill it)
public override bool IsDropDownResizable {
get {return true;}
}
// EditValue is invoked when you hit the drop-arrow in
PropertyGrid
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value) {
Type enumType = null;
// "context" tells us about the property that the user is
editing;
// get the Type that the property represents (we wil ensure
// that it is an enum later)
if (context != null && context.PropertyDescriptor != null)
enumType = context.PropertyDescriptor.PropertyType;
// "provider" gives us access to services; form-based editors
// typically provide IWindowsFormsEditorService, which allows
// us to show modal dialogs (like collection-editor), and show
// drop-controls (like Dock, etc); try to obtain this service
IWindowsFormsEditorService svc = provider == null ? null :
provider.GetService(typeof(IWindowsFormsEditorService)) as
IWindowsFormsEditorService;
if (enumType !=null &&enumType.IsEnum && svc != null) {
// we have an enum property, and a forms service; all set!
// so create a new control for our data
using (CheckBoxEnumControl editor = new
CheckBoxEnumControl(enumType)) {
// tell the control which flags were set when we
started
editor.Value = (int) value;
// note that DropDownControl is "sync"; need to call
// CloseDropDown when done, so hook into the
ValueSelected
// event, so that we close the drop when the user
clicks OK
editor.ValueSelected += delegate {
// find which options are selected now [MOVED]
value = editor.Value;
// close the editor
svc.CloseDropDown();
};
// show the drop control *and wait*
svc.DropDownControl(editor);
}
}
// return the updated value
return value;
}

}

class CheckBoxEnumControl : UserControl {
CheckedListBox box; // allows us to find it easily later
// declare a simple ValueSelected event, using the more-efficient
// "Events" approach; note that these lines could be replaced with
// public event EventHandler ValueSelected; (but less efficient)
private static readonly object EVENT_ValueSelected = new
object();
public event EventHandler ValueSelected {
add { Events.AddHandler(EVENT_ValueSelected, value); }
remove { Events.RemoveHandler(EVENT_ValueSelected, value); }
}
// wrapper method that raises the ValueSelected event, in a safe
manner
protected virtual void OnValueSelected() {
EventHandler handler = Events[EVENT_ValueSelected] as
EventHandler;
if (handler != null) handler(this, EventArgs.Empty);
}
// ctor
public CheckBoxEnumControl(Type enumType) {
// add an "OK" button to the bottom of the form,
// that raises ValueSelected when clicked
Button b = new Button();
b.Text = "OK";
b.Dock = DockStyle.Bottom;
b.Click += delegate { OnValueSelected(); };
Controls.Add(b);
// add a filled CheckedListBox, and use Enum.GetValues to
// obtain the options available for this Enum type
box = new CheckedListBox();
box.Dock = DockStyle.Fill;
foreach (object value in Enum.GetValues(enumType)) {
if ((int)value != 0) { // skip 0 / None, since doesn't
contribute bits
// note that we are adding enums, so the displayed
text will be the
// enum name, but that value is still castable to int
box.Items.Add(value);
}
}
Controls.Add(box);
}
public int Value {
get {
// loop thru each checked value, and use bitwise "or"
// to build the combined value as int
int value = 0;
foreach (int val in box.CheckedItems) {
value |= val;
}
return value;
}
set {
// loop thru each box (Enum value); if this bit is set
// in the supplied value ("value"), then select this box -
// otherwise clear it
for (int i = 0; i < box.Items.Count; i++) {
int val = (int) box.Items;
box.SetItemChecked(i, (value & val) == val);
}
}
}
 
M

Marc Gravell

especially in the adding of events to the button click

b.Click += delegate { OnValueSelected(); };

This is an anonymous method; it is equivalent to having:


public void OKButtonClicked(object sender, EventArgs args) {
OnValueSelected(); // raise the ValueSelected event
}
and using
b.Click += new EventHandler(OKButtonClicked);

Basically, it just saves me some typing, which I consider a good thing.
 
P

Peted

thanks heaps

Peted


b.Click += delegate { OnValueSelected(); };

This is an anonymous method; it is equivalent to having:


public void OKButtonClicked(object sender, EventArgs args) {
OnValueSelected(); // raise the ValueSelected event
}
and using
b.Click += new EventHandler(OKButtonClicked);

Basically, it just saves me some typing, which I consider a good thing.
 

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

Top