我正在尝试在DataGridView和为DGV提供数据的BindingList之间实现双向绑定。有些列尚未反映基础列表中的更改,我认为这是因为我没有提供属性设置器来通知属性更改。而不是为Rows属性设置setter,就像我对Process属性所做的那样,我试图让它更“优雅”,我意识到我被卡住了....
我偶然发现了一个非常有趣的文章,以便采用更优雅的方法,我正在努力实现它的概念(请参阅参考资料): http://www.gavaghan.org/blog/2007/07/17/use-inotifypropertychanged-with-bindinglist/
以下是Mike想要使用的文章中的代码(在我的CBMI.Common项目中建立为 Utilities.cs ):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace CBMI.Common
{
public static class Utilities
{
public static bool Set<T>(object owner, string propName,
ref T oldValue, T newValue, PropertyChangedEventHandler eventHandler)
{
// make sure the property name really exists
if (owner.GetType().GetProperty(propName) == null)
{
throw new ArgumentException("No property named '" + propName + "' on " + owner.GetType().FullName);
}
if (!Equals(oldValue, newValue)) // we only raise an event if the value has changed
{
oldValue = newValue;
if (eventHandler != null)
{
eventHandler(owner, new PropertyChangedEventArgs(propName));
}
}
return true; // Please NOTE: I had to add this statement to avoid compile error:
// "not all code paths return a value".
}
}
}
所以,我的第一个问题关于此:作者在他的文章中没有返回语句,我添加了它解决了编译器错误。我猜测eventHandler执行并返回,这是作者的遗漏,这应该返回true,因为该方法需要bool返回类型。这是正确的假设吗?
我的第二个问题显示了当我尝试使用上面的辅助方法时,我是一个C#新手。我已将此类编码为与上述相同的项目(和命名空间)中名为 InputFileInfo.cs 的单独文件:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace CBMI.Common
{
public class InputFileInfo : INotifyPropertyChanged
{
private bool processThisFile;
public bool Process
{
get { return processThisFile; }
set
{
processThisFile = value;
this.NotifyPropertyChanged("Process");
}
}
public string FileName { get; set; }
private long rowsReturned;
public long Rows
{
get { return rowsReturned; }
set
{
Utilities.Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
}
}
public string Message { get; set; }
// constructor
public InputFileInfo(string fName)
{
Process = true;
FileName = fName;
Rows = 0;
Message = String.Empty;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
此类中第二个属性的setter是我尝试使用Mike的静态方法的地方:
Utilities.Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
如果我删除Utilities.Set并按如下方式对其进行编码:
Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
..然后我让编译器抱怨“当前上下文中不存在名称'Set'”。
我尝试使用Utilities添加;指令,但没有解决问题。
最后,我不明白参数:ref T oldValue,T newValue
也不是调用Set方法的名为 value 的参数。
有人可以帮助我解决有关此代码的多种混淆,以便我可以使用这些更高级的想法吗?
----编辑更新---- 两个好的答案帮助我实现了这个目标。上面原帖中的“第二个问题”仍然有点难以捉摸。为每个请求关于如何打包它的“最佳实践”添加了注释,因此我可以使用Mike的原始文章中的简单调用语法。也就是说,我只想通过方法名称调用“helper”静态方法。我想了解如何调用:
set
{
Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
}
而不是编码为:
set
{
Utilities.Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
}
我通过编码Utilities.Set得到了这个工作但是我猜这个问题有点变成了 - “我在哪里放静态方法以及如何调用它们所以我不必”限定“他们使用类名?“ 我想了解如何打包通常有用的”实用程序“类型方法,它们不需要对象的实例。在这种情况下,静态方法称为Set,但我希望能够添加其他静态方法,例如:
public static int HelpfulMethodXXXX(string s, int num)
我有一个单独编译的DLL(Vstudio项目),只包含类文件。最后,我想我可以在其他应用程序中使用此类。
声明这些静态方法的最佳位置在哪里,以便可以将它们调用为:
int i = HelpfulMethodXXXX("Sample", testNumber);
而不是:
int i = ContainingClassName.HelpfulMethodXXXX("Sample", testNumber);
答案 0 :(得分:1)
1:所有非void方法都需要有明确的return语句。
2:CMBI.Common是命名空间。 Utilities是您班级的名称。 Set()是你班级的一个功能。
对Set()的调用仅在Utilities类的上下文中有意义。 Set()不是全局命名空间的一部分 - 因此,如果要在Utilities类之外调用Set(),则必须指定您想要Utilities.Set(),而不是SomethingElse.Set() 。 (Inside Utilities,编译器理解Set()引用Utilities.Set())。
如果您不想要命名空间中的每个类(CMBI.Common.Utilities),则使用语句只能包含命名空间(CMBI.Common)或命名空间内的特定类。但是,它们不能将类函数转换为全局函数。
3:T指的是此功能操作的Generic类型的名称。 http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx
泛型允许相同的代码操作,例如,整数集合和字符串集合,同时强制编译时类型安全(如果您尝试将整数推入集合中,编译器将给出错误字符串。)
ref意味着参数作为引用传递 - 并且对函数体内参数所做的更改将传播到函数调用者上下文中的参数值。
答案 1 :(得分:1)
看起来返回类型应该从bool
更改为void
,因为它似乎不会返回不同的值。
是的,Utilities.Set
是正确的语法。例如,Java中没有静态导入模拟,因此您必须使用类限定它。如果您选择了可以使用扩展方法,并且能够调用this.Set(...)
之类的方法。为此,只需在this
方法的第一个参数前添加Set
关键字:
public static bool Set<T>(this object owner, string propName,
ref T oldValue, T newValue, PropertyChangedEventHandler eventHandler)
ref T oldValue
表示您可以向其传递T
类型的变量,并将旧值写入其中(换句话说,值类型的传递引用)。通过这种方式,您可以了解oldValue
的内容。 (虽然在我看来out
参数会更有意义。)
T newValue
是您尝试将其设置为的新值。如果你问的是T
是什么,它是一个泛型类型,并且作为 类型的whta的占位符。它根据您传递给它的参数类型自动计算出哪种类型。 (如果您传递string
,则T的行为就像您使用string
而不是T
一样。)
value
是C#中的特殊关键字,仅在您定义的属性的set
访问器中具有特殊含义。这是您尝试分配给房产的价值。