web-dev-qa-db-ja.com

INotifyPropertyChangedおよび静的プロパティ

私は単純な問題をめぐって自分自身を結びつけています。 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を呼び出すことはできません。

すべてのインスタンスのクラスレベルでリストを保持し、各インスタンスでメソッドを呼び出すことはできますが、どういうわけかそれはやり過ぎのように感じます。または、静的プロパティをシングルトンクラスに引き出すこともできますが、論理的にはクラスレベルで動作します。これには確立されたパターンがありますか、それとも私はこれについて別の方法で考える必要がありますか?

18
dumbledad

あなたがそのデザインを維持する傾向があるなら、私は次のような解決策を選びます:

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. MinimumLengthを5に設定します。
  2. 長さを10に設定します。 (値がデフォルトの0から5に変更されたため、イベントが発生します)
  3. 長さを11に設定します。 (イベントは発生しますが、長さがまだ5であるため発生しないはずです)
13
João Angelo

静的プロパティのバインドと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");
            }
        }
    }
    ...
}
6
Phil

私は同じ問題に直面しました。これが私が実施した解決策です。

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
Claude Catonio