C# INotifyPropertyChanged 和静态属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14614190/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
INotifyPropertyChanged and static properties
提问by dumbledad
I'm tying myself in knots over a simple problem. I have a class that implements INotifyPropertyChanged
. Some of the instance properties' getters use static properties and thus their values may change if the static property changes? Here's a simplified example.
我因一个简单的问题而束手无策。我有一个实现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));
}
}
}
Clearly if the static property MinimumLength
changes then every instance's property length
may also change. But how should the static property signal the possible change to the instances? It cannot call OnPropertyChanged
since that is not static.
显然,如果静态属性MinimumLength
发生变化,那么每个实例的属性length
也可能发生变化。但是静态属性应该如何表示实例可能发生的变化呢?它不能调用,OnPropertyChanged
因为它不是静态的。
I could keep a list at the class level of all the instances and call a method on each one, but somehow that feels like overkill. Or I could pull the static properties out into a singleton class but logically they live at the class level. Is there an established pattern for this or should I be thinking about this differently?
我可以在所有实例的类级别保留一个列表,并对每个实例调用一个方法,但不知何故,这感觉有点矫枉过正。或者我可以将静态属性提取到一个单例类中,但从逻辑上讲,它们存在于类级别。是否有一个既定的模式,或者我应该以不同的方式思考这个问题?
采纳答案by Jo?o Angelo
If you're inclined to maintain that design then I would go with a solution like the following:
如果您倾向于保持该设计,那么我将采用如下解决方案:
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;
}
}
This is pretty much equivalent to maintaining a list of instances but I find it more maintainable and clearer. Also, you really need to use a weak event handler strategy, otherwise, your instances will not be garbage collected because they will always be associated with the static event which acts like a GC root.
这几乎相当于维护一个实例列表,但我发现它更易于维护和更清晰。此外,您确实需要使用弱事件处理程序策略,否则,您的实例将不会被垃圾收集,因为它们将始终与充当 GC 根的静态事件相关联。
You can read more about weak event handlers in the following blog posts (which were written by me so I'm biased):
您可以在以下博客文章中阅读有关弱事件处理程序的更多信息(这是我写的,所以我有偏见):
.NET Weak Event Handlers – Part I
.NET Weak Event Handlers – Part I
In an unrelated note your code is currently firing property changed when in fact the property value did not change. For example:
在一个不相关的注释中,您的代码当前正在触发属性更改,而实际上属性值没有更改。例如:
- Set MinimumLength to 5;
- Set length to 10; (event fires since the value changes from the default 0 to 5)
- Set length to 11; (event fires but it should not since the length is still 5)
- 将最小长度设置为 5;
- 将长度设置为 10;(事件触发,因为值从默认值 0 更改为 5)
- 将长度设置为 11;(事件触发但不应该,因为长度仍然是 5)
回答by Phil
You could use the technique mentioned in Binding static property and implementing INotifyPropertyChangedbut also raise a notification against "length", e.g.
您可以使用绑定静态属性和实现 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");
}
}
}
...
}
回答by Claude Catonio
i was faced to the same problem. Here is the solution i have put in place.
我面临同样的问题。这是我已经实施的解决方案。
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; }
}
}
Then i use it in the XAML like that
然后我像这样在 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>