INotifyPropertyChanged 和静态属性

问题描述 投票:0回答:4

我因为一个简单的问题而陷入困境。我有一个实现

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
,因为它不是静态的。

我可以在所有实例的类级别保留一个列表,并在每个实例上调用一个方法,但不知何故,这感觉有点矫枉过正。或者我可以将静态属性提取到单例类中,但逻辑上它们位于类级别。是否有一个既定的模式,或者我应该以不同的方式思考这个问题?

c# .net static inotifypropertychanged
4个回答
18
投票

如果您倾向于维持该设计,那么我会采用如下解决方案:

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 弱事件处理程序 – 第 I 部分

.NET 弱事件处理程序 – 第 I 部分

在一个不相关的注释中,您的代码当前正在触发属性更改,而实际上属性值并未更改。例如:

  1. 将最小长度设置为 5;
  2. 设置长度为10; (由于值从默认的 0 更改为 5,因此会触发事件)
  3. 设置长度为11; (事件会触发,但它不应该触发,因为长度仍然是 5)

5
投票

您可以使用绑定静态属性并实现 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
投票

我也面临着同样的问题。这是我已经采取的解决方案。

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>

0
投票

您可以将类的实例设为静态,例如

class ExampleClass : INotifyPropertyChanged
{
    private static ExampleClass _Current = new ExampleClass();
    public static ExampleClass Current => _Current;

    private int _MinimumLength = 5;
    public int MinimumLength
    {
        get => _MinimumLength;
        set
        {
            if (_MinimumLength != value)
            {
                _MinimumLength = value;
                OnPropertyChanged("MinimumLength");
            }
        }
    }
}

然后你可以定义一个以静态类实例作为绑定源的

Binding
,即

<Label Text="{Binding MinimumLength,Source={x:Static local:ExampleClass.Current}}"/>
© www.soinside.com 2019 - 2024. All rights reserved.