确定setter内的调用者 - 或者静默设置属性

时间:2013-11-01 23:59:48

标签: c# mvvm inotifypropertychanged c#-5.0

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

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应用于属性设置器?

5 个答案:

答案 0 :(得分:14)

如果我理解正确的话,你会问到二传手的来电者。这意味着,前一个方法在到达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

答案 1 :(得分:5)

我对问题的第一个解决方法是从PropertyEventArgs派生。除了PropertyChangeOrigin之外,新类将有一个名为PropertyName的成员。当您调用RaisePropertyChanged时,您将从PropertyChangeOrigin属性收集的信息中提供具有CallerMemberName集的新类的实例。现在,当您订阅该活动时,订阅者可以尝试将eventargs转换为您的新类,并在演员表成功时使用该信息。

答案 2 :(得分:1)

你看过CallerMemberName msdn page吗?

我正在使用它来避免在基类中使用RaisePropertyChanged("some_name_here");,然后所有属性都只调用RaisePropertyChanged();

我相信它也可以在setter中使用......

修改

根据评论问题,这里有一些代码可以做到这一点(愚蠢,但仍然会做所谓的问题)。

public int some_int
{
    get { return _someInt; }
    set
    {
        _someInt = value;
        var v = check_sender();
        Console.Out.WriteLine("in the setter of {0}", v);
    }
}
private int _someInt;

private string check_sender([CallerMemberName]string property = "")
{
    return property;
}

然后,如果在设定者中,我会写:

some_int = 7; // obviously, any number will cause it to fire

你会进入输出(注意中间线,添加其余部分,这样你就会看到它是一些控制台输出):

'WPF MVVM for SO.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework-SystemXmlLinq\v4.0_4.0.0.0__b77a5c561934e089\PresentationFramework-SystemXmlLinq.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
in the setter of some_int
'WPF MVVM for SO.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.

答案 3 :(得分:1)

这是我一直用作INotifyPropertyChanged和我的View Models:

之间的中间地带
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对象存储在字典中,因此对于经常设置的属性,它不会被创建很多次。我相信这可以解决你的问题。祝你好运!

答案 4 :(得分:0)

每当我必须将额外信息传递到VM时,我在使用命令方面取得了巨大成功:

Commands, RelayCommands and EventToCommand