Catalin Hoha Catalin Hoha - 3 months ago 7
C# Question

How can I initialize only properties that have a value?

I have a class CustomControl which inherits from System.Windows.Forms.Control.
I will also create a new class named GraphicsData which will have all the graphical information about my CustomControl (I need this because it's easier to serialize the data for saving it in a DB, json, etc.)

The CustomControl object will get the GraphicsData at the initialization(in the constructor) and I want it to get all the properties that have a value in GraphicsData (sometimes I don't want to initialize all of the properties from GraphicsData and I want them to remain the default from System.Windows.Forms.Control class).

The problem is that most of the proprierties are not nullable and I cannot check if they are null so I can't do a simple:

customControl.BackColor = graphicsData.BackColor.HasValue ? graphicsData.BackColor.Value : BackColor;


I can of course work this around if I create my own Nullable class but this got really ugly and hard to understand the code. Also, it is very hard to add a new property when needed.

Now, what I did and I think this is a much cleaner way is the following:

GraphicsData class:

public class GraphicsData : INotifyPropertyChanged
{
private readonly List<string> _initializedProperties = new List<string>();
public List<string> InitializedProperties { get { return _initializedProperties; } }

public event PropertyChangedEventHandler PropertyChanged;

private Size _size;
private Point _location;
private AnchorStyles _anchor;
private Color _backColor;
private Image _backgroundImage;
private Cursor _cursor;
private Font _font;
private Color _foreColor;
private bool _enabled;
private bool _visible;

public Size Size
{
get { return _size; }
set
{
_size = value;
OnPropertyChanged("Size");
}
}

public Point Location
{
get { return _location; }
set
{
_location = value;
OnPropertyChanged("Location");
}
}

public AnchorStyles Anchor
{
get { return _anchor; }
set
{
_anchor = value;
OnPropertyChanged("Anchor");
}
}

public Color BackColor
{
get { return _backColor; }
set
{
_backColor = value;
OnPropertyChanged("BackColor");
}
}

public Image BackgroundImage
{
get { return _backgroundImage; }
set
{
_backgroundImage = value;
OnPropertyChanged("BackgroundImage");
}
}

public Cursor Cursor
{
get { return _cursor; }
set
{
_cursor = value;
OnPropertyChanged("Cursor");
}
}

public Font Font
{
get { return _font; }
set
{
_font = value;
OnPropertyChanged("Font");
}
}

public Color ForeColor
{
get { return _foreColor; }
set
{
_foreColor = value;
OnPropertyChanged("ForeColor");
}
}

public bool Enabled
{
get { return _enabled; }
set
{
_enabled = value;
OnPropertyChanged("Enabled");
}
}

public bool Visible
{
get { return _visible; }
set
{
_visible = value;
OnPropertyChanged("Visible");
}
}

protected void OnPropertyChanged(string propertyName)
{
if (!_initializedProperties.Contains(propertyName))
_initializedProperties.Add(propertyName);

if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}


And in my custom control I have a method:

public void LoadGraphics()
{
var initializedProperties = graphics.InitializedProperties;
foreach (string propertyName in initializedProperties)
{
var value = graphics.GetType()
.GetProperty(propertyName)
.GetValue(graphics, null);
_customControl.GetType()
.GetProperty(propertyName)
.SetValue(_customControl, value, null);
}
}


Basically, I created a List named InitializedProperties and in the properties "set" I add the property in the list.
After that, using reflection in my CustomControl, I can load all the initialized properties.

I implemented the INotifyPropertyChanged because I also want to change the customControl properties each time a property is changed in GraphicsData.

Is this the proper way to do what I want ? I don't think the reflection code is that readable and I am concerned about the performance.

Answer

Using nullable values is a much easier method for achieving this.

C# already has a built-in Nullable class, but it also offers a simple way to make a value nullable without the excess verbosity that the Nullable class introduces: ?.

All of your values can be made nullable by appending the ? operator to the value types:

private Size? _size;
private Point? _location;
private AnchorStyles? _anchor;
private Color? _backColor;
private Image _backgroundImage;
private Cursor _cursor;
private Font _font;
private Color? _foreColor;
private bool? _enabled;
private bool? _visible;

Your LoadGraphics method can easily check to see if the GraphicsData property has a non-null value, and set the corresponding control property if so.

public void LoadGraphics(GraphicsData gfx)
{
    // It may be permissible to utilize a null value for BackgroundImage!
    // In this case, utilizing a separate field (IsBackgroundImageSet) may be a necessary
    if (gfx.BackgroundImage != null) { _customControl.BackgroundImage = gfx.BackgroundImage; }

    if (gfx.Size != null) { _customControl.Size = gfx.Size.Value; }
    if (gfx.Location != null) { _customControl.Location = gfx.Location.Value }
    if (gfx.Anchor != null) { _customControl.Anchor = gfx.Anchor.Value; }
    if (gfx.BackColor != null) { _customControl.BackColor = gfx.BackColor .Value; }
    if (gfx.Cursor != null) { _customControl.Cursor = gfx.Cursor; }
    if (gfx.Font != null) { _customControl.Font = gfx.Font; }
    if (gfx.Color != null) { _customControl.Color = gfx.Color.Value; }
    if (gfx.Enabled != null) { _customControl.Enabled = gfx.Enabled.Value; }
    if (gfx.Visible != null) { _customControl.Visible = gfx.Visible.Value; }
}