Control doesn't repaint when using object with custom type converter

C

CroDude

I've made a custom groupbox control. Inside, as one of it's members is
CSimpleGradient object. CSimpleGradient is a wrapper class for gradient
usage. Basically it looks like that:

[TypeConverter(typeof(CSimpleGradientConverter))]
public class CSimpleGradient
{
private float m_GradientAngle;
private Color m_ColorA;
private Color m_ColorB;

// Properties
blah blah ...

// Constructor
public CSimpleGradient(float _angle, Color _colorA, Color _colorB)
{
this.m_ColorA = _colorA;
this.m_ColorB = _colorB;
this.m_GradientAngle = _angle;
}
#endregion
}

It uses custom type converter(ExpandableObjectConverter). It's code goes
like this:
internal class CSimpleGradientConverter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type
sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
// Overrides the ConvertFrom method of TypeConverter.
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(new char[] {','});
float angle = float.Parse(v[0]);
Color colorA = Color.FromName(v[1].Trim());
Color colorB = Color.FromName(v[2].Trim());
return new CSimpleGradient(angle, colorA, colorB);
}
return base.ConvertFrom(context, culture, value);
}
// Overrides the ConvertTo method of TypeConverter.
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo
culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return ((CSimpleGradient)value).Angle + "," +
((CSimpleGradient)value).ColorA + "," + ((CSimpleGradient)value).ColorB;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}

In my custom group-box class, CSimpleGradient properties looks like this:
[Category("Appearance_Header"), Description("Gets/Sets header gradient color
fill/gradient angle if HeaderFillType is set to Gradient."),
RefreshProperties(RefreshProperties.All),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public CSimpleGradient HeaderGradientColor
{
get{return this.m_HeaderGradient;}
set
{
this.m_HeaderGradient = value;
this.Invalidate();
}
Everything works fine, but after changing values of CSimpleGradent object I
have to ALT+TAB forth and back to designer editor for changes to be
repainted.
I'm not sure why it doesn't work automatically since there is Invalidate
call in property's SET.

Please help! Thanks in advance!
D.
 
A

AlexS

Problem could be related to how Windows posts messages

As SDK help says

Calling the Invalidate method does not force a synchronous paint; to force a
synchronous paint, call the Update method after calling the Invalidate
method. When this method is called with no parameters, the entire client
area is added to the update region

Are you looking for synchronous paint?

HTH
Alex

CroDude said:
I've made a custom groupbox control. Inside, as one of it's members is
CSimpleGradient object. CSimpleGradient is a wrapper class for gradient
usage. Basically it looks like that:

[TypeConverter(typeof(CSimpleGradientConverter))]
public class CSimpleGradient
{
private float m_GradientAngle;
private Color m_ColorA;
private Color m_ColorB;

// Properties
blah blah ...

// Constructor
public CSimpleGradient(float _angle, Color _colorA, Color _colorB)
{
this.m_ColorA = _colorA;
this.m_ColorB = _colorB;
this.m_GradientAngle = _angle;
}
#endregion
}

It uses custom type converter(ExpandableObjectConverter). It's code goes
like this:
internal class CSimpleGradientConverter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type
sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
// Overrides the ConvertFrom method of TypeConverter.
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(new char[] {','});
float angle = float.Parse(v[0]);
Color colorA = Color.FromName(v[1].Trim());
Color colorB = Color.FromName(v[2].Trim());
return new CSimpleGradient(angle, colorA, colorB);
}
return base.ConvertFrom(context, culture, value);
}
// Overrides the ConvertTo method of TypeConverter.
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo
culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return ((CSimpleGradient)value).Angle + "," +
((CSimpleGradient)value).ColorA + "," + ((CSimpleGradient)value).ColorB;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}

In my custom group-box class, CSimpleGradient properties looks like this:
[Category("Appearance_Header"), Description("Gets/Sets header gradient color
fill/gradient angle if HeaderFillType is set to Gradient."),
RefreshProperties(RefreshProperties.All),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public CSimpleGradient HeaderGradientColor
{
get{return this.m_HeaderGradient;}
set
{
this.m_HeaderGradient = value;
this.Invalidate();
}
Everything works fine, but after changing values of CSimpleGradent object I
have to ALT+TAB forth and back to designer editor for changes to be
repainted.
I'm not sure why it doesn't work automatically since there is Invalidate
call in property's SET.

Please help! Thanks in advance!
D.
 
C

CroDude

Thanks Alex for help, but that didn't fix it.

I just want to repaint the control when HeaderGradientColor property changes
either from within DesignView or through the code.
It is so strange since all other properties are repainting correctly in
DesignView when using Invalidate in Set method.
All these(working) are standard types such as int, float, color etc. this
happens only with that custom one(CSimpleGradient).
Reallly strange!
 
A

AlexS

Invalidate should post wm_paint. Update should call Paint.
You can try to find out what is going in with some profiler or just by
monitoring what you get in message loop.
 
C

CroDude

I found the solution ... You must invalidate through the designer object.
Here it goes:

public override void Initialize(IComponent component)
{
base.Initialize(component);
// Record instance of control we're designing
this.m_GroupBox = (CDizzyGroupBox) component;
// Hook up events
ISelectionService s = (ISelectionService)
GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)
GetService(typeof(IComponentChangeService));
s.SelectionChanged += new EventHandler(OnSelectionChanged);
c.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving);
c.ComponentChanged+=new ComponentChangedEventHandler(OnComponentChanged);
}

private void OnComponentChanged(object sender, ComponentChangedEventArgs e)
{
this.m_GroupBox.Update();
this.m_GroupBox.Invalidate();
}

protected override void Dispose(bool disposing)
{
ISelectionService s = (ISelectionService)
GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)
GetService(typeof(IComponentChangeService));
// Unhook events
s.SelectionChanged -= new EventHandler(OnSelectionChanged);
c.ComponentRemoving -= new ComponentEventHandler(OnComponentRemoving);
c.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged);
base.Dispose(disposing);
}
 

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