コントロールにバインドしようとしているカスタム オブジェクトがあります。そのカスタム オブジェクトに INotifyPropertyChanged インターフェイスを実装しました。オブジェクトとそのオブジェクトのプロパティに正常にバインドされました。
私が理解できないのは、そこからどのように行くかです。私はこれに2日間取り組んでいますが、まだ機能させることができません。
私の仮定は、コントロールにバインドされたプロパティを変更すると、そのプロパティに設定された値がコントロールに表示されるということでした。ただし、プロパティをいくら変更しても、UI は初期値を超えて更新されることはありません。
INotifyPropertyChanged を次のように実装しました。 INotifyPropertyChanged を実装する基本クラス
したがって、私の基本クラスは次のとおりです。
[Serializable]
public abstract class BindableObject : INotifyPropertyChanged
{
#region Data
private static readonly Dictionary<string, PropertyChangedEventArgs> eventArgCache;
private const string ERROR_MSG = "{0} is not a public property of {1}";
#endregion // Data
#region Constructors
static BindableObject()
{
eventArgCache = new Dictionary<string, PropertyChangedEventArgs>();
}
protected BindableObject()
{
}
#endregion // Constructors
#region Public Members
/// <summary>
/// Raised when a public property of this object is set.
/// </summary>
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Returns an instance of PropertyChangedEventArgs for
/// the specified property name.
/// </summary>
/// <param name="propertyName">
/// The name of the property to create event args for.
/// </param>
public static PropertyChangedEventArgs
GetPropertyChangedEventArgs(string propertyName)
{
if (String.IsNullOrEmpty(propertyName))
throw new ArgumentException(
"propertyName cannot be null or empty.");
PropertyChangedEventArgs args;
// Get the event args from the cache, creating them
// and adding to the cache if necessary.
lock (typeof(BindableObject))
{
bool isCached = eventArgCache.ContainsKey(propertyName);
if (!isCached)
{
eventArgCache.Add(
propertyName,
new PropertyChangedEventArgs(propertyName));
}
args = eventArgCache[propertyName];
}
return args;
}
#endregion // Public Members
#region Protected Members
/// <summary>
/// Derived classes can override this method to
/// execute logic after a property is set. The
/// base implementation does nothing.
/// </summary>
/// <param name="propertyName">
/// The property which was changed.
/// </param>
protected virtual void AfterPropertyChanged(string propertyName)
{
}
/// <summary>
/// Attempts to raise the PropertyChanged event, and
/// invokes the virtual AfterPropertyChanged method,
/// regardless of whether the event was raised or not.
/// </summary>
/// <param name="propertyName">
/// The property which was changed.
/// </param>
protected void RaisePropertyChanged(string propertyName)
{
this.VerifyProperty(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
// Get the cached event args.
PropertyChangedEventArgs args =
GetPropertyChangedEventArgs(propertyName);
// Raise the PropertyChanged event.
handler(this, args);
}
this.AfterPropertyChanged(propertyName);
}
#endregion // Protected Members
#region Private Helpers
[Conditional("DEBUG")]
private void VerifyProperty(string propertyName)
{
Type type = this.GetType();
// Look for a public property with the specified name.
PropertyInfo propInfo = type.GetProperty(propertyName);
if (propInfo == null)
{
// The property could not be found,
// so alert the developer of the problem.
string msg = string.Format(
ERROR_MSG,
propertyName,
type.FullName);
Debug.Fail(msg);
}
}
#endregion // Private Helpers
}
上記のクラスから継承し、派生クラスで自分のプロパティでこれを行います。
public virtual string Name
{
get
{
return m_strName;
}
set
{
m_strName = value;
RaisePropertyChanged("Name");
}
}
私の XAML は次のようになります (省略版):
<Window x:Class="PSSPECApplication.Windows.Project"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
DataContext="{Binding SizingProject, RelativeSource={RelativeSource Self}}">
<StackPanel VerticalAlignment="Center">
<TextBox Name="txtProjectName" Text="{Binding Name}" />
</StackPanel>
ウィンドウのデータ コンテキストが SizingProject というプロパティであることがわかります。SizingProject は派生型 (BindableObject から派生) であり、Name プロパティを持ち、PropertyChanged イベント ハンドラーを発生させます。
ウィンドウのコンストラクターで SizingProject を設定すると、その Name プロパティが設定されます。
これをテストするために、ウィンドウに、Name プロパティを元の値とは異なる値に設定するイベントを発生させるボタンもあります。ただし、name プロパティが変更されると、何も起こりません。私は BindableObject までさかのぼりましたが、PropertyChanged イベントは常に null に設定されているため、ハンドラーが設定および実行されることはありません。どうしてこれなの?
INotifyPropertyChanged を実装し、バインディングでその型を使用すると、WPF はそのイベント ハンドラーを自動的に設定し、正しい動作が発生すると思いましたか? 私にとって、私はその行動を見たことがありません。
私は問題を理解しました。私がしなければならなかったことは、プロパティ SizingProject の DependencyProperty を作成することでした。私がそれをした後、すべてがうまくいきました。
public static readonly DependencyProperty SizingProjectProperty =
DependencyProperty.Register("SizingProject", typeof(Sizing.Project), typeof(Project), new UIPropertyMetadata());
public Sizing.Project SizingProject
{
get
{
return (Sizing.Project)GetValue(Project.SizingProjectProperty);
}
set
{
SetValue(Project.SizingProjectProperty, value);
}
}