12

给定一个标准的视图模型实现,当一个属性发生变化时,有没有办法确定变化的发起者?换句话说,在下面的视图模型中,我希望“PropertyChanged”事件的“sender”参数是调用Prop1setter 的实际对象:

public class ViewModel : INotifyPropertyChanged
{
    public double Prop1
    {
        get { return _prop1; }
        set
        {
            if (_prop1 == value)
                return;
            _prop1 = value;

            // here, can I determine the sender?
            RaisePropertyChanged(propertyName: "Prop1", sender: this);
        }
    }
    private double _prop1;

    // TODO implement INotifyPropertyChanged
}

或者,是否可以应用于CallerMemberNameAttribute属性设置器?

4

4 回答 4

18

如果我理解正确,您是在询问 setter 的调用者。这意味着,调用堆栈中的前一个方法调用在到达 setter 本身之前(这也是一个方法)。

为此使用StackTrace.GetFrames方法。例如(取自http://www.csharp-examples.net/reflection-callstack/):

using System.Diagnostics;

[STAThread]
public static void Main()
{
  StackTrace stackTrace = new StackTrace();           // get call stack
  StackFrame[] stackFrames = stackTrace.GetFrames();  // get method calls (frames)

  // write call stack method names
  foreach (StackFrame stackFrame in stackFrames)
  {
    Console.WriteLine(stackFrame.GetMethod().Name);   // write method name
  }
}

输出:

Main
nExecuteAssembly
ExecuteAssembly
RunUsersAssembly
ThreadStart_Context
Run
ThreadStart

基本上,你要求的是stackFrames[1].GetMethod().Name.

于 2013-12-30T23:36:17.333 回答
5

我对您的问题的第一种方法是从PropertyEventArgs. 新类将有一个名为的成员,例如PropertyChangeOrigin除了PropertyName. 当您调用 时RaisePropertyChanged,您提供了一个新类的实例,其中包含PropertyChangeOriginCallerMemberName属性收集的信息中的集合。现在,当您订阅该事件时,订阅者可以尝试将 转换eventargs为您的新类,并在转换成功时使用该信息。

于 2013-12-24T23:58:05.333 回答
1

这是我一直使用的INotifyPropertyChanged和我的视图模型之间的中间立场:

public class NotifyOnPropertyChanged : INotifyPropertyChanged
{
    private IDictionary<string, PropertyChangedEventArgs> _arguments;

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public void OnPropertyChanged([CallerMemberName] string property = "")
    {
        if(_arguments == null)
        {
            _arguments = new Dictionary<string, PropertyChangedEventArgs>();
        }

        if(!_arguments.ContainsKey(property))
        {
            _arguments.Add(property, new PropertyChangedEventArgs(property));
        }

        PropertyChanged(this, _arguments[property]);
    }
}

这里有两件事。它使用[CallerMemberName]属性来设置属性名称。这使得使用语法如下:

public string Words
{
    set
    {
        if(value != _words)
        {
            _words = value;
            OnPropertyChanged( );
        }
    }
}

除此之外,它将PropertyChangedEventArgs对象存储在字典中,因此不会为频繁设置的属性创建大量时间。我相信这可以解决您的问题。祝你好运!

于 2013-12-31T16:46:04.520 回答
0

每当我不得不将额外的信息传递到 VM 中时,我都会使用命令取得巨大成功:

命令、RelayCommands 和 EventToCommand

于 2013-12-31T19:04:08.763 回答