1

我只是尝试使用 C++/CLI 订阅 WPF 属性更改事件。我没想到这会变得困难。

首先,我尝试订阅某个窗口的特定属性(IsMouseDirectlyOver),最后通过以下代码成功:

void MyClass::DependencyPropertyChanged(Object^ sender, DependencyPropertyChangedEventArgs args)
{
   Debug::WriteLine("DependencyPropertyChanged: "+sender->ToString()+", "+args.Property->Name);
}

window->IsMouseDirectlyOverChanged += gcnew DependencyPropertyChangedEventHandler(this, &MyClass::DependencyPropertyChanged);

然后我尝试订阅对象的任何属性更改(这对我来说最重要,因为我的最终代码必须能够通过属性名称处理属性更改)。我在这方面完全失败了。

我尝试了各种方法,但没有任何效果。我找不到任何 C++/CLI 示例,但根据文档和 C# 示例,以下对我来说似乎是最明智的代码:

window->PropertyChanged += gcnew PropertyChangedEventHandler(this, &MyClass::PropertyChanged);

void MyClass::PropertyChanged(Object^ sender, PropertyChangedEventArgs^ args)
{
   ...
}

但是编译器通过错误 C2039 告诉我“PropertyChangedEvent”不是“System::Windows::Window”的元素。

我怎样才能达到我想要的?

4

3 回答 3

1

阿尔在评论中提到,你的代码不起作用,因为没有PropertyChanged事件 on Window,就这么简单。

相反,您可以做的是覆盖方法,OnPropertyChanged()方法存在于Window. 在您的覆盖中,您可以做任何您想做的事情,包括引发PropertyChanged(不要忘记先创建该事件)。

于 2012-12-22T11:59:02.790 回答
1

PropertyDescriptor(或派生的DependencyPropertyDescriptor )提供了一种通过其AddValueChanged方法添加属性更改处理程序的机制:

DependencyPropertyDescriptor^ propertyDescriptor = DependencyPropertyDescriptor::FromName(
    "ActualWidth", component->GetType(), component->GetType());

propertyDescriptor->AddValueChanged(component, gcnew EventHandler(ActualWidthChanged));
...

static void ActualWidthChanged(Object^ component, EventArgs^ e)
{
    ...
}

不幸的是,处理程序没有通过更改的属性,所以我想您必须为要监视的所有属性添加不同的处理程序。


编辑:您可能会实现类似于下面显示的代码,该代码使用匿名委托将属性名称传递给适当的处理程序。但是请注意,这是 C#,据我了解,这不能在 C++/CLI 中完成,因为它不支持托管 lambda。也许你可以将这样的帮助类包装在一个单独的程序集中,并从你的 C++/CLI 代码中使用它。

public delegate void PropertyChangedHandler(object component, string propertyName);

public static class DependencyPropertyDescriptorExt
{
    public static void AddPropertyChangedHandler(
        this object component, string propertyName, PropertyChangedHandler handler)
    {
        var propertyDescriptor = DependencyPropertyDescriptor.FromName(
            propertyName, component.GetType(), component.GetType());
        propertyDescriptor.AddValueChanged(component, (o, e) => handler(o, propertyName));
    }
}

现在您可以像这样编写和使用这样的 PropertyChangedHandler:

this.AddPropertyChangedHandler("ActualHeight", PropertyChanged);
...

private void PropertyChanged(object component, string propertyName)
{
    ...
}
于 2012-12-22T15:12:25.693 回答
1

我查看了窥探源。我修改了它并写了一个非常非常基本的例子:

String^ ownerPropertyName = "IsActive";
DependencyObject^ propertyOwner = window;

DependencyPropertyDescriptor^ ownerPropertyDescriptor = DependencyPropertyDescriptor::FromName(ownerPropertyName, propertyOwner->GetType(), propertyOwner->GetType());
DependencyProperty^ ownerProperty = ownerPropertyDescriptor->DependencyProperty;
Type^ ownerPropertyType = ownerProperty->PropertyType;

DependencyProperty^ myProperty = DependencyProperty::Register(ownerPropertyName, ownerPropertyType, GetType(), gcnew PropertyMetadata(gcnew PropertyChangedCallback(&MyClass::BoundPropertyChangedCallback)));

Binding^ myPropertyToOwnerPropertyBinding = gcnew Binding(ownerPropertyName);
myPropertyToOwnerPropertyBinding->Mode = BindingMode::OneWay;
myPropertyToOwnerPropertyBinding->Source = propertyOwner;
BindingOperations::SetBinding(this, myProperty, myPropertyToOwnerPropertyBinding);

和:

static void BoundPropertyChangedCallback(DependencyObject^ me, DependencyPropertyChangedEventArgs args)
{
   Debug::WriteLine("BoundPropertyChangedCallback: "+args.OldValue+", "+args.NewValue+", "+args.Property->Name);
}

对我来说看起来很复杂。我不知道那些绑定的东西是否真的有必要。事实上,它甚至可以订阅没有事件的属性(比如 IsMouseOver),并且可以对没有实现 INotifyPropertyChanged 的​​对象(比如 Window)进行操作。而且它不需要任何属性的开关/案例。

于 2012-12-22T23:16:51.507 回答