如何将 System.Windows.Controls.Control 中的公共基础 class 用于不同的超级 class,例如 Button、TextBox、TextBlock
How to use common base class from System.Windows.Controls.Control for the different superclasses like Button, TextBox,TextBlock
我有一个来自 System.Windows.Controls.Control 的基础 class,它根据来自外部的数据更改 Visibility、Enabled、Background、Foreground 属性。
当我像下面这样使用 class 时
public class RsdDesignBase : Button
{
....
}
它适用于按钮控件。我想对 TextBox、Image、TextBlock 等其他控件使用相同的 class,但如果我这样使用,我需要为所有其他控件复制粘贴相同的代码。
有没有办法使用我的 RsdDesignBase class 作为其他控件的基础 class?或者任何其他方式来做到这一点。
我会在下面粘贴整个 class。它所做的是等待 DataTag 对象的更改,当它们更改它更改某些属性时。例如,如果 _enabledTag.Value 为 0,则禁用控件。
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);
}
}
如果您从 RsdDesignButtonBase
class 派生自 FrameworkElement
:
public class RsdDesignBase : FrameworkElement
{
...
}
...您应该能够为 TextBox
、Image
、TextBlock
和任何其他 FrameworkElement
扩展和自定义它,例如:
public class TextBlock : RsdDesignBase {}
据我所知,您的控件做了两(三)件事情:
- 它为控件设置特定的布局(可见性、背景等)
- 它处理很多(反)序列化和处理 JSON 数据。
- 如果某些数据可用或不可用,return 中的一些处理会修改 UI 属性(例如 Hide/Show)。
遵循“关注点分离”的有用原则 - 不是因为它听起来很学术或者 'awesome',而是因为你不会陷入耦合过于紧密的代码中 - 我宁愿推荐将所有这些逻辑放入 Attached Property
或一组附加属性中。并将控件作为第一个参数传递。
您不必更改很多实现,并且几乎可以将它用于派生自 Control
甚至 FrameworkElement
的所有 WPF 元素
如果我没理解错的话,您想为 Button、TextBox、Image 和 TextBlock(可能还有更多)添加一些功能并为所有 class 重用该代码,对吗?
您现在正在做的是在继承树的底部添加一个 Base。这样您就无法与其他 classes 共享它。理想情况下,您可能想要更改 System.Windows.Controls.Control,但那是 .NET Framework 的一部分,因此您无法更改它...
这是继承的缺点...
我看到的唯一可能是使用合成:
- 创建一个包含您想要的逻辑的 class。我们称它为
RsdDesign
。不需要超级class。它看起来很像您的 RsdDesignButtonBase。
- 为每个要添加此功能的控件创建一个后代
- 为这些后代提供类型为``RsdDesign```` 的私有成员。
- 将控件的所有适用方法连接到成员。
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);
}
}
我还没有尝试过,但也许这个想法可以帮助您找到解决方案。
我有一个来自 System.Windows.Controls.Control 的基础 class,它根据来自外部的数据更改 Visibility、Enabled、Background、Foreground 属性。
当我像下面这样使用 class 时
public class RsdDesignBase : Button
{
....
}
它适用于按钮控件。我想对 TextBox、Image、TextBlock 等其他控件使用相同的 class,但如果我这样使用,我需要为所有其他控件复制粘贴相同的代码。
有没有办法使用我的 RsdDesignBase class 作为其他控件的基础 class?或者任何其他方式来做到这一点。
我会在下面粘贴整个 class。它所做的是等待 DataTag 对象的更改,当它们更改它更改某些属性时。例如,如果 _enabledTag.Value 为 0,则禁用控件。
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);
}
}
如果您从 RsdDesignButtonBase
class 派生自 FrameworkElement
:
public class RsdDesignBase : FrameworkElement
{
...
}
...您应该能够为 TextBox
、Image
、TextBlock
和任何其他 FrameworkElement
扩展和自定义它,例如:
public class TextBlock : RsdDesignBase {}
据我所知,您的控件做了两(三)件事情:
- 它为控件设置特定的布局(可见性、背景等)
- 它处理很多(反)序列化和处理 JSON 数据。
- 如果某些数据可用或不可用,return 中的一些处理会修改 UI 属性(例如 Hide/Show)。
遵循“关注点分离”的有用原则 - 不是因为它听起来很学术或者 'awesome',而是因为你不会陷入耦合过于紧密的代码中 - 我宁愿推荐将所有这些逻辑放入 Attached Property
或一组附加属性中。并将控件作为第一个参数传递。
您不必更改很多实现,并且几乎可以将它用于派生自 Control
甚至 FrameworkElement
如果我没理解错的话,您想为 Button、TextBox、Image 和 TextBlock(可能还有更多)添加一些功能并为所有 class 重用该代码,对吗?
您现在正在做的是在继承树的底部添加一个 Base。这样您就无法与其他 classes 共享它。理想情况下,您可能想要更改 System.Windows.Controls.Control,但那是 .NET Framework 的一部分,因此您无法更改它...
这是继承的缺点...
我看到的唯一可能是使用合成:
- 创建一个包含您想要的逻辑的 class。我们称它为
RsdDesign
。不需要超级class。它看起来很像您的 RsdDesignButtonBase。 - 为每个要添加此功能的控件创建一个后代
- 为这些后代提供类型为``RsdDesign```` 的私有成员。
- 将控件的所有适用方法连接到成员。
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);
}
}
我还没有尝试过,但也许这个想法可以帮助您找到解决方案。