1


I've been searching for a solution but I can't seem to find one, though I guess this might be simple.
I've built a custom control with a property that is a Class. When I change any of this class properties, my control doesn't automatically refresh (neither in the designer nor programmatically). Is there any way to force Invalidate() method?
I've followed some tips that I found, but none seem to work.
Here's a code sample to demonstrate what I'm experiencing.
This is my custom type.

[TypeConverter(typeof(ExpandableObjectConverter))]
public class TextComponent
{
    public TextComponent()
    {
        Text= string.Empty;
        Font = Control.DefaultFont;
        ForeColor = Control.DefaultForeColor;
    }

    [NotifyParentProperty(true)]
    public string Text { get; set; }

    [NotifyParentProperty(true)]
    public Font Font { get; set; }

    [NotifyParentProperty(true)]
    public Color ForeColor { get; set; }
}

And this is my custom control:

public partial class myControl : Control
{
    private TextComponent tc = new TextComponent();

    protected override void OnPaint(PaintEventArgs pe)
    {
        pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

        GraphicsPath gp = new GraphicsPath();
        Rectangle rect = new Rectangle(1, 1, DisplayRectangle.Width - 3, DisplayRectangle.Height - 3);
        gp.AddRectangle(rect);
        pe.Graphics.FillPath(new SolidBrush(Color.LightCoral), gp);

        using (StringFormat sf = new StringFormat())
        {
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;

            pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            pe.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            pe.Graphics.DrawString(tc.Text, tc.Font, new SolidBrush(tc.ForeColor), rect, sf);
        }

        base.OnPaint(pe);
    }

    [Description("The Text Component for the Control"), Category("Text"), NotifyParentPropertyAttribute(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public TextComponent TextComponent { get { return tc; } }

    public myControl()
    {
        this.DoubleBuffered = true;
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        this.SetStyle(ControlStyles.ResizeRedraw, true);
        this.SetStyle(ControlStyles.Selectable, true);
    }

    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public new string Text { get { return string.Empty; } }
}
DMVC
  • 240
  • 1
  • 2
  • 15
  • I don't see anywhere in your code where you are `Calling the OnPaint method of the base class.` [MSDN User-Draw Controls](https://msdn.microsoft.com/en-us/library/b818z6z6(v=vs.71).aspx) – MethodMan Jan 26 '15 at 18:31
  • possible duplicate of [How to use the OnPaint event in C#?](http://stackoverflow.com/questions/10076116/how-to-use-the-onpaint-event-in-c) – MethodMan Jan 26 '15 at 18:32
  • @MethodMan, thanks for replying. Where would you put `Invalidate();` method call? Remember the properties are in another class. So I guess there should be some kind of attribute to force this automatically. My control paints just the way I want. It's not a painting issue. Suppose I change TextComponent.Text property in the designer properties, When I press ENTER I want the text to be drawn immediatly. – DMVC Jan 26 '15 at 18:36
  • I've edited my control's source and added a base.OnPaint() call. It didn't solve the issue. When I change any property of the TextComponent class in the designer, my control only gets refreshed after I click anywhere else on the designer window. – DMVC Jan 26 '15 at 19:02

1 Answers1

1

Try listening for a PropertyChanged event to make this work. You would have to add the INotifyPropertyChanged interface to your class and power it up:

[TypeConverter(typeof(ExpandableObjectConverter))]
public class TextComponent : INotifyPropertyChanged  {

  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnChanged(string propertyName) {
    if (PropertyChanged != null) {
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }

  private string text = string.Empty;

  [NotifyParentProperty(true)]
  public string Text {
    get { return text; }
    set {
      text = value;
      OnChanged("Text");
    }
  }
}

Then in your control class, listen for the event and invalidate your control:

public myControl() {
  this.DoubleBuffered = true;
  this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
  this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
  this.SetStyle(ControlStyles.ResizeRedraw, true);
  this.SetStyle(ControlStyles.Selectable, true);

  this.TextComponent.PropertyChanged += TextComponent_PropertyChanged;
}

void TextComponent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
  this.Invalidate();
}
LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • Excellent. Thanks. I was hoping for some kind of attribute (somethong like `NotifyParentProperty(true)`), but this solution is clean as well. – DMVC Jan 26 '15 at 21:44