给定标准视图模型实现,当属性发生变化时,有没有办法确定变更的发起者?换句话说,在下面的视图模型中,我希望“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
应用于属性设置器?
答案 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时,我在使用命令方面取得了巨大成功: