1

我正在尝试在 DataGridView 和为 DGV 提供数据的 BindingList 之间实现双向绑定。有些列尚未反映基础列表中的更改,我认为这是因为我没有提供属性设置器来通知属性更改。我没有像为 Process 属性那样为 Rows 属性编写 setter 代码,而是试图变得更“优雅”,但我意识到我被困住了......

我偶然发现了一篇非常有趣的文章,提出了一种更优雅的方法,我正在尝试实现它的概念(请参阅): http ://www.gavaghan.org/blog/2007/07/17/use-inotifypropertychanged-与绑定列表/

这是我想使用的 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));
    }
    }
}

此类中第二个属性的设置器是我尝试使用 Mike 的静态方法的地方:

Utilities.Set(this, "Rows", ref rowsReturned, value, PropertyChanged);

如果我删除 Utilities.Set 并将其编码如下:

Set(this, "Rows", ref rowsReturned, value, PropertyChanged);

..然后我得到编译器抱怨“当前上下文中不存在名称'Set'”

我尝试添加一个using Utilities;指令,但这并没有解决问题。

最后,我不明白参数:ref T oldValue,T newValue,也不是调用Set方法
的参数称为value 。

有人可以帮助我解决有关此代码的这些多重困惑,以便我可以使用这些更高级的想法吗?

---- 编辑更新---- 两个好的答案帮助我完成了这项工作。上面原始帖子中的“第二个问题”仍然有点难以捉摸。为每个请求关于如何打包它的“最佳实践”添加注释,以便我可以使用 Mike 原始文章中的简单调用语法。也就是说,我正在寻求仅通过方法名称来调用“帮助”静态方法。我想了解如何调用:

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);
4

2 回答 2

1
  • 看起来返回类型应该从boolto更改,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,它是一个泛型类型,并充当该类型真正是什么的占位符。它会根据您传递给它的参数类型自动确定哪种类型。(如果您传递它 a string, T 的行为就像您使用string而不是T.)

  • value是 C# 中的一个特殊关键字,仅在set您定义的属性的访问器中具有特殊含义。这是您尝试分配给该属性的值。

于 2011-07-20T22:24:59.640 回答
1

1:所有非void方法都需要有明确的return语句。

2:CMBI.Common 是命名空间。实用程序是您的类的名称。Set() 是你的类的一个函数。

对 Set() 的调用仅在 Utilities 类的上下文中才有意义。Set() 不是全局命名空间的一部分——因此,如果你想在 Utilities 类之外调用 Set(),你必须指定你想要 Utilities.Set(),而不是 SomethingElse.Set() . (在 Utilities 中,编译器理解 Set() 指的是 Utilities.Set())。

如果您不希望命名空间中的每个类 (CMBI.Common.Utilities),使用语句只能包含命名空间 (CMBI.Common) 或命名空间内的特定类。但是,它们不能将类函数变成全局函数。

3:T 指的是该函数所操作的泛型类型的名称。http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx

泛型允许相同的代码操作,例如,整数集合和字符串集合,同时强制编译时类型安全(如果您尝试将整数推送到字符串集合中,编译器将给出错误。)

ref 表示参数作为引用传递 - 并且在函数体内对参数所做的更改将传播到函数调用者上下文中的参数值。

于 2011-07-20T22:20:26.600 回答