Свойства INotifyPropertyChanged и static - программирование
Подтвердить что ты не робот

Свойства INotifyPropertyChanged и static

Я связываю себя в узлах по простой проблеме. У меня есть класс, который реализует INotifyPropertyChanged. Некоторые из получателей свойств экземпляра используют статические свойства, и поэтому их значения могут меняться, если изменяется статическое свойство? Здесь упрощенный пример.

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, поскольку это не является статическим.

Я мог бы вести список на уровне класса всех экземпляров и вызывать метод на каждом из них, но почему-то это кажется излишним. Или я мог бы вытащить статические свойства в одноэлементный класс, но логически они живут на уровне класса. Есть ли устоявшийся образец для этого или я должен думать об этом по-другому?

4b9b3361

Ответ 1

Если вы склонны поддерживать этот дизайн, я бы пошел с таким решением, как следующее:

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

В несвязанной заметке ваш код в настоящее время меняет свойство, измененное, когда на самом деле значение свойства не изменилось. Например:

  • Установите MinimumLength в 5;
  • Установить длину до 10; (событие срабатывает, поскольку значение изменяется от значения по умолчанию от 0 до 5)
  • Установить длину до 11; (событие срабатывает, но оно не должно, так как длина по-прежнему 5)

Ответ 2

Вы можете использовать технику, упомянутую в Связывании статического свойства и реализации 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");
            }
        }
    }
    ...
}

Ответ 3

я столкнулся с той же проблемой. Вот решение, которое я установил.

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>