在C ++ / CLI中订阅PropertyChanged窗口事件

时间:2012-12-22 11:02:36

标签: wpf c++-cli dependency-properties visual-c++-2010 propertychanged

我刚尝试使用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'的元素。

我如何实现我的目标?

3 个答案:

答案 0 :(得分:1)

评论中提及,您的代码无效,因为PropertyChanged上没有Window个事件,就像那个一样简单。

您可以执行的操作是覆盖Window上的the OnPropertyChanged() method。在您的覆盖中,您可以执行任何操作,包括提升PropertyChanged(不要忘记先创建该事件)。

答案 1 :(得分: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中完成,因为它不支持托管lambdas。 Mayby你可以在一个单独的程序集中包装一个这样的帮助器类,并从你的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)
{
    ...
}

答案 2 :(得分: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)进行操作。并且它不需要任何属性的开关/案例。