簡體   English   中英

INotifyPropertyChanged和靜態屬性

[英]INotifyPropertyChanged and static properties

我把自己綁在一個簡單的問題上。 我有一個實現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因為它不是靜態的。

我可以在所有實例的類級別保留一個列表,並在每個實例上調用一個方法,但不知何故,這感覺就像是矯枉過正。 或者我可以將靜態屬性拉出到單例類中,但邏輯上它們仍然存在於類級別。 是否有既定的模式或者我應該以不同的方式考慮這個問題?

如果您傾向於維護該設計,那么我將使用如下解決方案:

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弱事件處理程序 - 第一部分

.NET弱事件處理程序 - 第一部分

在一個不相關的注釋中,您的代碼當前觸發屬性已更改,而實際上屬性值未更改。 例如:

  1. 將MinimumLength設置為5;
  2. 設定長度為10; (事件觸發,因為值從默認值0更改為5)
  3. 設定長度為11; (事件發生但不應該因為長度仍為5)

您可以使用Binding靜態屬性中提到的技術並實現INotifyPropertyChanged,但也可以針對“length”發出通知,例如

class ExampleClass : INotifyPropertyChanged
{
    private static int _MinimumLength = 5;

    public int MinimumLength
    {
        get
        {
            return _MinimumLength;
        }
        set
        {
            if (_MinimumLength != value)
            {
                _MinimumLength = value;

                OnPropertyChanged("MinimumLength");
                OnPropertyChanged("length");
            }
        }
    }
    ...
}

我遇到了同樣的問題。 這是我已經實施的解決方案。

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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM