我有一个自定义对象,我试图绑定到一个控件。在该自定义对象上,我实现了INotifyPropertyChanged接口。我已成功绑定到我的对象和该对象上的属性。
我无法弄清楚如何从那里开始。我现在已经工作了2天了,我仍然无法让它发挥作用。
我的假设是,当我更改绑定到控件的属性时,该属性中设置的值将显示在控件中。但是,无论我更改了多少属性,UI都不会更新超出其初始值。
我已经以这种方式实现了INotifyPropertyChanged: A base class which implements 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);
}
}
答案 0 :(得分:3)
在我的机器上运行。虽然,缓存有点古怪。我要么为每种类型创建静态只读版本,要么忘记缓存直到需要(过早优化和所有)。
我创建了一个示例项目。主窗口如下所示:
<Window x:Class="INPCTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:INPCTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<this:MyObject />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Text="{Binding MyOutProperty}" />
<TextBox
Grid.Row="1"
Text="{Binding MyInProperty, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
我绑定到MyObject的一个实例,我在xaml中创建(如果这对你来说不熟悉,你可以在代码隐藏中执行此操作)。
这是MyObject的代码:
class MyObject : BindableObject
{
private string _in;
private string _out;
public string MyOutProperty
{
get { return _out; }
set { _out = value; this.RaisePropertyChanged("MyOutProperty"); }
}
public string MyInProperty
{
get { return _in; }
set
{
_in = value;
MyOutProperty = "The textbox below says: \"" + value + "\"";
this.RaisePropertyChanged("MyInProperty");
}
}
}
它们如何一起工作是:
Window.DataContext
我怀疑你的问题不在于你的基类,它与你的子类的实现或你的绑定有关。
为了帮助调试绑定,follow the information at this link配置visual studio以进行详细的绑定跟踪输出(如果已配置,则在“输出”窗口或“立即”窗口中结束)。