Kivannc
Kivannc

Reputation: 106

How to use common base class from System.Windows.Controls.Control for the different superclasses like Button, TextBox,TextBlock

I have a base class from System.Windows.Controls.Control that changes Visibility, Enabled , Background, Foreground properties according to data from outside.

when I use the class like below

public class RsdDesignBase : Button
    {
....
}

It works for Button Control. I want to use same class for other controls like TextBox, Image, TextBlock but if I use like this I neet copy paste same code for all other controls.

Is there a way to use my RsdDesignBase class as base class for others controls ? Or any other way to do this.

I will paste whole class below. What it does is waits for changes in DataTag objects when they change it changes to some properties. For example if _enabledTag.Value is 0 it disables the control.

public class RsdDesignButtonBase : Button
{
    private DataTag _visibilityTag;
    private DataTag _enabledTag;
    private DataTag _appearanceTag;
    public TagScriptObject TagScriptObject { get; set; }
    private readonly Timer _timer;

    protected RsdDesignButtonBase()
    {
        Loaded += RSD_ButtonBase_Loaded;
        Unloaded += OnUnloaded;
        _timer = new Timer(1000);
        _timer.Elapsed += TimerOnElapsed;
    }

    private void TimerOnElapsed(object sender, ElapsedEventArgs e)
    {
        Dispatcher.BeginInvoke(new Action(() =>
        {
            var background = Background;
            var foreground = Foreground;
            Background = foreground;
            Foreground = background;
        }), DispatcherPriority.Render);

    }

    private void OnUnloaded(object sender, RoutedEventArgs e)
    {
        if (_enabledTag != null) _enabledTag.DataChanged -= EnabledTagOnDataChanged;
        if (_visibilityTag != null) _visibilityTag.DataChanged -= VisibilityTagOnDataChanged;
        if (_appearanceTag != null) _appearanceTag.DataChanged -= AppearanceTagOnDataChanged;
    }

    private void RSD_ButtonBase_Loaded(object sender, RoutedEventArgs e)
    {
        DependencyPropertyDescriptor desc =
            DependencyPropertyDescriptor.FromProperty(FrameworkElement.TagProperty, typeof(FrameworkElement));
        desc.AddValueChanged(this, TagPropertyChanged);
        TagPropertyChanged(null, null);
    }

    private void TagPropertyChanged(object sender, EventArgs e)
    {
        if (Tag == null) return;
        TagScriptObject = JsonConvert.DeserializeObject<TagScriptObject>(Tag.ToString());

        if (TagScriptObject?.VisibilityProperty?.TagId > 0)
        {
            _visibilityTag =
                GlobalVars.AllDataTagList.FirstOrDefault(t => t.Id == TagScriptObject.VisibilityProperty?.TagId);
            if (_visibilityTag != null)
            {
                _visibilityTag.DataChanged += VisibilityTagOnDataChanged;
                VisibilityTagOnDataChanged(null, null);
            }
        }

        if (TagScriptObject?.EnableProperty?.TagId > 0)
        {
            _enabledTag =
                GlobalVars.AllDataTagList.FirstOrDefault(t => t.Id == TagScriptObject.EnableProperty?.TagId);
            if (_enabledTag != null)
            {
                _enabledTag.DataChanged += EnabledTagOnDataChanged;
                EnabledTagOnDataChanged(null, null);
            }
        }

        if (TagScriptObject?.AppearanceProperty?.TagId > 0)
        {
            _appearanceTag =
                GlobalVars.AllDataTagList.FirstOrDefault(t => t.Id == TagScriptObject.AppearanceProperty?.TagId);
            if (_appearanceTag != null && !_appearanceTag.IsEventHandlerRegistered(null))
            {
                _appearanceTag.DataChanged += AppearanceTagOnDataChanged;
                AppearanceTagOnDataChanged(null, null);
            }
        }
    }

    private void AppearanceTagOnDataChanged(object source, EventArgs args)
    {
        _timer.Enabled = false;
        _ = Dispatcher.BeginInvoke(new Action(() =>
        {
            double tagValue;
            bool result = true;
            if (_appearanceTag.VarType == VarType.Bit)
            {
                tagValue = _appearanceTag.TagValue ? 1 : 0;
            }
            else
            {
                result = double.TryParse(_appearanceTag.TagValue.ToString(), out tagValue);
            }

            if (result)
            {
                foreach (var controlColor in TagScriptObject.AppearanceProperty.ControlColors)
                {
                    if (tagValue >= controlColor.RangeMin &&
                        tagValue <= controlColor.RangeMax)
                    {
                        Background =
                            new BrushConverter().ConvertFromString(controlColor.Background) as SolidColorBrush;
                        Foreground =
                            new BrushConverter().ConvertFromString(controlColor.Foreground) as SolidColorBrush;
                        _timer.Enabled = controlColor.Flashing == ConfirmEnum.Yes;
                        break;
                        
                    }
                }
            }
        }), DispatcherPriority.Render);
    }

    private void EnabledTagOnDataChanged(object source, EventArgs args)
    {
        _ = Dispatcher.BeginInvoke(new Action(() =>
        {
            if (_enabledTag != null)
            {
                if (TagScriptObject.EnableProperty.IsRangeSelected)
                {
                    double tagValue;
                    bool result = true;
                    if (_enabledTag.VarType == VarType.Bit)
                    {
                        tagValue = _enabledTag.TagValue ? 1 : 0;
                    }
                    else
                    {
                        result = double.TryParse(_enabledTag.TagValue.ToString(), out tagValue);
                    }

                    if (result)
                    {
                        if (tagValue >= TagScriptObject.EnableProperty.RangeFrom &&
                            tagValue <= TagScriptObject.EnableProperty.RangeTo)
                        {
                            IsEnabled = TagScriptObject.EnableProperty.DefaultValue;
                        }
                        else
                        {
                            IsEnabled = !TagScriptObject.EnableProperty.DefaultValue;
                        }
                    }
                }
                else
                {
                    if (_enabledTag.IsNumeric || _enabledTag.VarType == VarType.Bit)
                    {
                        var bitArray = _enabledTag.GetBitArray();
                        var singleBit = TagScriptObject.EnableProperty.SingleBit;
                        if (bitArray.Count > singleBit)
                        {
                            if (bitArray[singleBit])
                            {
                                IsEnabled = TagScriptObject.EnableProperty.DefaultValue;
                            }
                            else
                            {
                                IsEnabled = !TagScriptObject.EnableProperty.DefaultValue;
                            }
                        }
                    }
                }
            }
        }), DispatcherPriority.Render);
    }

    private void VisibilityTagOnDataChanged(object source, EventArgs args)
    {
        _ = Dispatcher.BeginInvoke(new Action(() =>
        {
            if (_visibilityTag != null)
            {
                if (TagScriptObject.VisibilityProperty.IsRangeSelected)
                {
                    double tagValue;
                    bool result = true;
                    if (_visibilityTag.VarType == VarType.Bit)
                    {
                        tagValue = _visibilityTag.TagValue ? 1 : 0;
                    }
                    else
                    {
                        result = double.TryParse(_visibilityTag.TagValue.ToString(), out tagValue);
                    }

                    if (result)
                    {
                        if (tagValue >= TagScriptObject.VisibilityProperty.RangeFrom &&
                            tagValue <= TagScriptObject.VisibilityProperty.RangeTo)
                        {
                            Visibility = TagScriptObject.VisibilityProperty.DefaultValue
                                ? Visibility.Visible
                                : Visibility.Hidden;
                        }
                        else
                        {
                            Visibility = TagScriptObject.VisibilityProperty.DefaultValue
                                ? Visibility.Collapsed
                                : Visibility.Visible;
                        }
                    }
                }
                else
                {
                    if (_visibilityTag.IsNumeric || _visibilityTag.VarType == VarType.Bit)
                    {
                        var bitArray = _visibilityTag.GetBitArray();
                        var singleBit = TagScriptObject.VisibilityProperty.SingleBit;
                        if (bitArray.Count > singleBit)
                        {
                            if (bitArray[singleBit])
                            {
                                Visibility = TagScriptObject.VisibilityProperty.DefaultValue
                                    ? Visibility.Visible
                                    : Visibility.Hidden;
                            }
                            else
                            {
                                Visibility = TagScriptObject.VisibilityProperty.DefaultValue
                                    ? Visibility.Hidden
                                    : Visibility.Visible;
                            }
                        }
                    }
                }
            }
        }), DispatcherPriority.Render);
    }
}

Upvotes: 1

Views: 387

Answers (3)

Quido
Quido

Reputation: 659

If I understand you correctly, you want to add some feature to Button, TextBox, Image and TextBlock (and possibly more) and reuse that code for all classes, right?

What you're doing right now is adding a Base at the bottom of the inheritance tree. That way you can't share it with other classes. Ideally, you would want to change the System.Windows.Controls.Control, but that's part of the .NET Framework, so you can't change that...

This is the downside of inheritance...

The only possibility I see is to use composition:

  • Create a class containing the logic you want. Let's call it RsdDesign. No superclass needed. It will look a lot like your RsdDesignButtonBase.
  • Create a descendant for every Control you want to add this feature to
  • Give those descendants a private member of type ``RsdDesign````.
  • Connect all applicable methods of the Control to the member.
    public class RsdDesign
    {
        private DataTag _visibilityTag;
        private DataTag _enabledTag;
        private DataTag _appearanceTag;
        public TagScriptObject TagScriptObject { get; set; }
        private readonly Timer _timer;
        private System.Windows.Controls.Control _parentControl
    
        protected RsdDesign(System.Windows.Controls.Control parentControl)
        {
            _parentControl = parentControl;
            _parentControl.Loaded += RSD_ButtonBase_Loaded;
            _parentControl.Unloaded += OnUnloaded;
            _timer = new Timer(1000);
            _timer.Elapsed += TimerOnElapsed;
        }
    
        // The rest of your RsdDesignButtonBase implementation
        // ...
    }
    
    public class RsdDesignButton: Button
    {
        private RsdDesign _design;
    
        public RsdDesignButton(...)
        {
            _design = new RsdDesign(this);
        }
    
        // You may need to hook some of the methods explicitly like this:
        private void EnabledTagOnDataChanged(object source, EventArgs args)
        {
            _design.EnabledTagOnDataChanged(source, args);
        }
    }

I haven't tried this, but maybe the idea helps you to find a solution.

Upvotes: 1

lidqy
lidqy

Reputation: 2453

As far as I can see your control does two(three) things:

  • It sets a certain layout to the control (visibility, background etc)
  • it deals a lot with (de)serializing and processing JSON data.
  • Some of the processing in return modifies UI properties (e.g. Hide/Show) if certain data is available or not.

Following the helpful principal of "separation of concerns" - not because it sound academic or is 'awesome', but because you don't get into a mess of too tightly coupled code - I would much rather recommend to put all of this logic into an Attached Property or a set of Attached properties. And to pass the control as the first argument.

You would not have to change a lot of the implementation and you could use it for virtually all WPF elements that derive from Control or even FrameworkElement

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/attached-properties-overview?view=netframeworkdesktop-4.8

Upvotes: 1

mm8
mm8

Reputation: 169160

If you derive from your RsdDesignButtonBase class from FrameworkElement:

public class RsdDesignBase : FrameworkElement
{
    ...
}

...you should be able to extend and customize it for TextBox, Image, TextBlock and any other FrameworkElement, e.g.:

public class TextBlock : RsdDesignBase {}

Upvotes: 1

Related Questions