私は単純な問題をめぐって自分自身を結びつけています。 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
を呼び出すことはできません。
すべてのインスタンスのクラスレベルでリストを保持し、各インスタンスでメソッドを呼び出すことはできますが、どういうわけかそれはやり過ぎのように感じます。または、静的プロパティをシングルトンクラスに引き出すこともできますが、論理的にはクラスレベルで動作します。これには確立されたパターンがありますか、それとも私はこれについて別の方法で考える必要がありますか?
あなたがそのデザインを維持する傾向があるなら、私は次のような解決策を選びます:
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ルートのように機能する静的イベントに関連付けられるため、ガベージコレクションされません。
弱いイベントハンドラーの詳細については、次のブログ投稿を参照してください(私が書いたので、偏見があります)。
関係のないメモでは、実際にはプロパティ値が変更されていないのに、コードは現在プロパティ変更を実行しています。例えば:
静的プロパティのバインドと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");
}
}
}
...
}
私は同じ問題に直面しました。これが私が実施した解決策です。
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>