我在一个简单的问题上陷入困境。我有一个实现INotifyPropertyChanged
的类。某些实例属性的getter使用静态属性,因此如果静态属性发生变化,它们的值可能会发生变化?这是一个简化的例子。
class ExampleClass : INotifyPropertyChanged
{
private static int _MinimumLength = 5;
public static int MinimumLength
{
get
{
return _MinimumLength;
}
set
{
if (_MinimumLength != value)
{
_MinimumLength = value;
//WHAT GOES HERE
}
}
}
private int _length = -1;
public int length
{
get
{
return (_length > _MinimumLength) ? _length : _MinimumLength;
}
set
{
var oldValue = (_length > _MinimumLength) ? _length : _MinimumLength;
if (_length != value)
{
_length = value;
var newValue = (_length > _MinimumLength) ? _length : _MinimumLength;
if (newValue != oldValue)
{
OnPropertyChanged("length");
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
显然,如果静态属性MinimumLength
发生变化,那么每个实例的属性length
也可能会发生变化。但静态属性应该如何表示对实例的可能更改?它不能调用OnPropertyChanged
,因为那不是静态的。
我可以在所有实例的类级别保留一个列表,并在每个实例上调用一个方法,但不知怎的,这感觉有些过分。或者我可以将静态属性拉出到单例类中,但逻辑上它们仍然存在于类级别。是否有既定的模式,或者我应该以不同的方式思考这个问题吗?
答案 0 :(得分:13)
如果您倾向于维护该设计,那么我会选择以下解决方案:
public static int MinimumLength
{
get { return _MinimumLength; }
set
{
if (_MinimumLength != value)
{
_MinimumLength = value;
OnGlobalPropertyChanged("MinimumLength");
}
}
}
static event PropertyChangedEventHandler GlobalPropertyChanged = delegate { };
static void OnGlobalPropertyChanged(string propertyName)
{
GlobalPropertyChanged(
typeof (ExampleClass),
new PropertyChangedEventArgs(propertyName));
}
public ExampleClass()
{
// This should use a weak event handler instead of normal handler
GlobalPropertyChanged += this.HandleGlobalPropertyChanged;
}
void HandleGlobalPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "MinimumLength":
if (length > MinimumLength)
length = MinimumLength;
break;
}
}
这几乎等同于维护实例列表,但我发现它更易于维护和清晰。此外,您确实需要使用弱事件处理程序策略,否则,您的实例将不会被垃圾收集,因为它们将始终与静态事件关联,该事件的行为类似于GC根。
您可以在以下博文中了解有关弱事件处理程序的更多信息(由我编写,因此我有偏见):
.NET Weak Event Handlers – Part I
.NET Weak Event Handlers – Part I
在一个不相关的注释中,当实际上属性值没有改变时,您的代码当前正在触发属性更改。例如:
答案 1 :(得分:6)
您可以使用Binding static property and implementing INotifyPropertyChanged中提到的技术,但也可以针对“长度”发出通知,例如
class ExampleClass : INotifyPropertyChanged
{
private static int _MinimumLength = 5;
public int MinimumLength
{
get
{
return _MinimumLength;
}
set
{
if (_MinimumLength != value)
{
_MinimumLength = value;
OnPropertyChanged("MinimumLength");
OnPropertyChanged("length");
}
}
}
...
}
答案 2 :(得分:0)
我遇到了同样的问题。这是我已经实施的解决方案。
public class ZoomDataVm : ModelBase
{
public ZoomDataVm()
{
// initialise the zoom
}
private double _zoomLevel;
public double ZoomLevel
{
get { return _zoomLevel; }
set
{
if (_zoomLevel != value)
{
_zoomLevel = value;
RaisePropertyChanged(() => ZoomLevel);
//
// persist zoom info
}
}
}
}
public class ZoomVm : ModelBase
{
public static ZoomDataVm _instance;
static ZoomVm()
{
_instance = new ZoomDataVm();
}
public ZoomDataVm Instance
{
get { return _instance; }
}
}
然后我在XAML中使用它
<StackPanel>
<StackPanel.Resources>
<screenControls:ZoomVm x:Key="ZoomVm" />
</StackPanel.Resources>
<TextBlock Text="{Binding ElementName=uiScaleSliderContainer, Path=Tag}" Foreground="White" Margin="0,0,20,0" />
<Control Name="uiScaleSliderContainer"
Margin="0,0,0,0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Tag="{Binding Source={StaticResource ZoomVm}, Path=Instance.ZoomLevel}">
<Control.Template>
<ControlTemplate>
<Slider Orientation="Horizontal"
Width="400"
x:Name="uiScaleSlider"
ToolTip="Zoom"
Value="{Binding ElementName=uiScaleSliderContainer, Path=Tag}"
Minimum="0.1"
Maximum="2"
TickFrequency="0.1"
IsSnapToTickEnabled="True">
</Slider>
</ControlTemplate>
</Control.Template>
</Control>
</StackPanel>