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

スヌープソースを調べました。私はそれを修正し、機能する非常に基本的な例を書きました。

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 に答える
1

コメントに記載されているように、あなたのコードは機能しません。PropertyChangedイベントがないため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 では実行できないことを理解しています。このようなヘルパー クラスを別のアセンブリにラップして、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 に答える