我使用RealProxy
创建了一个' Aspect'如果属性被标记为通知,则触发PropertyChanged
INotifyPropertyChanged
事件。然而,这并不起作用。
调试时,我可以看到事件被触发,ComponentModel位于事件的InvocationList中,但我的UI没有得到更新。我已经在我的视图模型中迷上了这个事件,它会按预期触发,但仍然没有UI更新。
以下是一些用于演示此问题的代码;
C#
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Windows;
using System.Windows.Controls;
namespace SOSample
{
public abstract class NotifyBase : MarshalByRefObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
[AttributeUsage(AttributeTargets.Property)]
public class NotifyPropertyChangedAttribute : Attribute { }
public class PropertyChangeAspect<T> : RealProxy where T : NotifyBase
{
private readonly T _decorated = default(T);
private PropertyChangeAspect(T decorated) : base(typeof(T))
{
_decorated = decorated;
}
public static T Create(T decorated)
{
var aspect = new PropertyChangeAspect<T>(decorated);
return (T)aspect.GetTransparentProxy();
}
public override IMessage Invoke(IMessage msg)
{
try
{
var methodCall = msg as IMethodCallMessage;
if (methodCall == null) throw new ArgumentException(nameof(msg));
var result = typeof(T).InvokeMember(methodCall.MethodName, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, _decorated, methodCall.Args);
var methodInfo = methodCall.MethodBase as MethodInfo;
if (methodInfo != null)
{
var prop = FindProperty(methodInfo.Name);
var attr = prop?.GetCustomAttribute<NotifyPropertyChangedAttribute>(true);
if (attr != null) NotifyPropertyChanged(prop.Name);
}
return new ReturnMessage(result, methodCall.Args, methodCall.Args.Length, methodCall.LogicalCallContext, methodCall);
}
catch (Exception ex)
{
return new ReturnMessage(ex, methodCall);
}
}
private PropertyInfo FindProperty(string name)
{
var props = _decorated.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
var prop = props.FirstOrDefault(p => (p.SetMethod?.Name ?? string.Empty).Equals(name));
return prop;
}
private void NotifyPropertyChanged(string propName)
{
var method = _decorated.GetType().GetMethod("NotifyPropertyChanged", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
method.Invoke(_decorated, new object[] { propName });
}
}
public class TestViewModel : NotifyBase
{
[NotifyPropertyChanged]
public string Name { get; set; }
// If I switch to this and don't use the proxy, it works fine
//public string _name;
//public string Name
//{
// get => _name;
// set { _name = value; NotifyPropertyChanged(); }
//}
public TestViewModel()
{
PropertyChanged += PropChanged;
}
private void PropChanged(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine(e.PropertyName); // This writes to the output window, so I know the event is firing
}
}
public partial class MainWindow : Window
{
public NotifyBase ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
//ViewModel = new TestViewModel(); // Using this works fine
ViewModel = PropertyChangeAspect<TestViewModel>.Create(new TestViewModel()); // Using the proxy is problematic
DataContext = this;
}
}
public partial class TestView : UserControl
{
public TestView()
{
InitializeComponent();
}
}
}
的Xaml
<Window x:Class="SOSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SOSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:TestViewModel}">
<local:TestView />
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding ViewModel}" />
</Window>
<UserControl x:Class="SOSample.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding Name}" Margin="5" />
<TextBox Grid.Row="1" Text="{Binding Name}" Margin="5" />
</Grid>
</UserControl>
任何人都可以告诉我出了什么问题,我该如何修理它,或者让我再也不会发布SO了?