Tengo una clase base de System.Windows.Controls.Control que cambia las propiedades de visibilidad, habilitado, fondo y primer plano de acuerdo con los datos del exterior.

Cuando uso la clase como a continuación

public class RsdDesignBase : Button
    {
....
}

Funciona para el control de botones. Quiero usar la misma clase para otros controles como TextBox, Image, TextBlock pero si uso así, necesito copiar y pegar el mismo código para todos los demás controles.

¿Hay alguna forma de usar mi clase RsdDesignBase como clase base para otros controles? O cualquier otra forma de hacer esto.

Pegaré toda la clase a continuación. Lo que hace es esperar cambios en los objetos DataTag cuando cambian los cambios en algunas propiedades. Por ejemplo, si _enabledTag.Value es 0, deshabilita el 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);
    }
}
1
Kivannc 9 jul. 2021 a las 15:56

3 respuestas

La mejor respuesta

Si lo entiendo correctamente, desea agregar alguna función a Button, TextBox, Image y TextBlock (y posiblemente más) y reutilizar ese código para todas las clases, ¿verdad?

Lo que está haciendo ahora es agregar una base en la parte inferior del árbol de herencia. De esa forma no podrás compartirlo con otras clases. Idealmente, querría cambiar System.Windows.Controls.Control, pero eso es parte de .NET Framework, por lo que no puede cambiar eso ...

Esta es la desventaja de la herencia ...

La única posibilidad que veo es usar la composición:

  • Cree una clase que contenga la lógica que desee. Llamémoslo RsdDesign. No se necesita superclase. Se parecerá mucho a su RsdDesignButtonBase.
  • Cree un descendiente para cada Control al que desee agregar esta función
  • Dé a esos descendientes un miembro privado de tipo `` RsdDesign ''.
  • Conecte todos los métodos aplicables del Control al miembro.
    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);
        }
    }

No he probado esto, pero tal vez la idea te ayude a encontrar una solución.

1
Quido 9 jul. 2021 a las 13:27

Si deriva de su clase RsdDesignButtonBase de FrameworkElement:

public class RsdDesignBase : FrameworkElement
{
    ...
}

... debería poder ampliarlo y personalizarlo para TextBox, Image, TextBlock y cualquier otro FrameworkElement, por ejemplo:

public class TextBlock : RsdDesignBase {}
1
mm8 9 jul. 2021 a las 13:06

Por lo que puedo ver, su control hace dos (tres) cosas:

  • Establece un cierto diseño para el control (visibilidad, fondo, etc.)
  • se ocupa mucho de (des) serializar y procesar datos JSON.
  • Parte del procesamiento a cambio modifica las propiedades de la interfaz de usuario (por ejemplo, Ocultar / Mostrar) si ciertos datos están disponibles o no.

Siguiendo el útil principio de "separación de preocupaciones", no porque suene académico o sea "asombroso", sino porque no te metas en un lío de código demasiado estrechamente acoplado, prefiero recomendar poner toda esta lógica en un Attached Property o un conjunto de propiedades adjuntas. Y pasar el control como primer argumento.

No tendría que cambiar gran parte de la implementación y podría usarla para prácticamente todos los elementos de WPF que se deriven de Control o incluso de FrameworkElement

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

1
lidqy 9 jul. 2021 a las 13:24