Niestandardowe właściwości zależności

W tym temacie opisano przyczyny, Windows Presentation Foundation (WPF) dla których deweloperzy aplikacji i autorzy składników mogą chcieć utworzyć niestandardową właściwość zależności, oraz opisano kroki implementacji, a także niektóre opcje implementacji, które mogą poprawić wydajność, użyteczność lub uniwersalność właściwości.

Wymagania wstępne

W tym temacie przyjęto założenie, że właściwości zależności są rozpoznawane w perspektywie odbiorcy istniejących właściwości zależności w WPF klasach, i zapoznaj się z tematem Omówienie właściwości zależności . Aby postępować zgodnie z przykładami w tym temacie, należy również zrozumieć Extensible Application Markup Language (XAML) i wiedzieć, jak pisać WPF aplikacje.

Co to jest właściwość zależności?

W przeciwnym razie można włączyć właściwość środowiska uruchomieniowego języka wspólnego (CLR) do obsługi stylów, powiązań danych, dziedziczenia, animacji i wartości domyślnych, implementując ją jako właściwość zależności. Właściwości zależności to właściwości, które są zarejestrowane w WPF systemie właściwości przez wywołanie Register metody (lub RegisterReadOnly ) i które są obsługiwane przez DependencyProperty pole identyfikatora. Właściwości zależności mogą być używane tylko przez DependencyObject typy, ale DependencyObject są dość duże w WPF hierarchii klas, dzięki czemu większość klas dostępnych w programie WPF może obsługiwać właściwości zależności. Aby uzyskać więcej informacji o właściwościach zależności i terminologii i konwencjach używanych do opisywania ich w tym zestawie SDK, zobacz Omówienie właściwości zależności.

Przykłady właściwości zależności

Przykłady właściwości zależności, które są implementowane w WPF klasach obejmują Background Właściwość, Width Właściwość i Text Właściwość, między wieloma innymi. Każda właściwość zależności udostępniona przez klasę ma odpowiednie publiczne pole statyczne typu DependencyProperty uwidocznione na tej samej klasie. To jest identyfikator właściwości zależności. Identyfikator jest nazwany przy użyciu konwencji: Nazwa właściwości zależności z Property dołączoną do niej ciągiem. Na przykład odpowiednie DependencyProperty pole identyfikatora dla Background Właściwości to BackgroundProperty . Identyfikator przechowuje informacje o właściwości zależności podczas rejestracji, a następnie identyfikator jest później używany do innych operacji obejmujących właściwość zależności, takich jak wywołanie SetValue .

Jak wspomniano we właściwościach zależności, wszystkie właściwości zależności w WPF (z wyjątkiem większości dołączonych właściwości) są również właściwościami CLR ze względu na implementację "otoka". Z tego względu kod można pobrać lub ustawić właściwości zależności przez wywołanie metod dostępu CLR, które definiują otoki w taki sam sposób, jak w przypadku innych właściwości środowiska CLR. Jako odbiorca ustanowionych właściwości zależności nie są zwykle używane DependencyObject metody GetValue i SetValue , które są punktem połączenia z systemem właściwości bazowe. Zamiast tego istniejąca implementacja właściwości CLR zostanie już wywołana GetValue i SetValue w ramach get implementacji otoki i w ramach tej set właściwości, odpowiednio przy użyciu pola identyfikatora. Jeśli samodzielnie implementujesz niestandardową właściwość zależności, będziesz definiować otokę w podobny sposób.

Kiedy należy zaimplementować właściwość zależności?

Podczas implementowania właściwości w klasie, tak długo, jak Klasa pochodzi z, istnieje DependencyObject możliwość utworzenia kopii zapasowej właściwości z DependencyProperty identyfikatorem i w związku z tym, aby uczynić ją właściwością zależności. Właściwość zależności nie zawsze jest konieczna lub odpowiednia i będzie zależeć od potrzeb scenariusza. Czasami zwykle jest to typowa technika tworzenia kopii zapasowej właściwości przy użyciu pola prywatnego. Należy jednak zaimplementować właściwość jako właściwość zależności za każdym razem, gdy właściwość ma obsługiwać co najmniej jedną z następujących WPF możliwości:

  • Chcesz, aby właściwość była settable w stylu. Aby uzyskać więcej informacji, zobacz Style i tworzenia szablonów.

  • Chcesz, aby Twoja Właściwość obsługiwała powiązanie danych. Aby uzyskać więcej informacji na temat właściwości zależności powiązań danych, zobacz Powiązywanie właściwości dwóch kontrolek.

  • Właściwość ma być ustawiana na wartość settable z odwołaniem do zasobu dynamicznego. Aby uzyskać więcej informacji, zobacz zasoby XAML.

  • Chcesz automatycznie dziedziczyć wartość właściwości z elementu nadrzędnego w drzewie elementów. W takim przypadku należy zarejestrować się przy użyciu RegisterAttached metody, nawet jeśli zostanie również utworzona otoka właściwości dla dostępu CLR. Aby uzyskać więcej informacji, zobacz dziedziczenie wartości właściwości.

  • Właściwość ma być animowana. Aby uzyskać więcej informacji, zobacz Omówienie animacji.

  • Ten system właściwości ma raportować, gdy Poprzednia wartość właściwości została zmieniona przez akcje podejmowane przez system właściwości, środowisko lub użytkownika albo przez odczytanie i użycie stylów. Za pomocą metadanych właściwości, właściwość może określić metodę wywołania zwrotnego, która będzie wywoływana za każdym razem, gdy system właściwości ustali, że wartość właściwości została ostatecznie zmieniona. Koncepcja pokrewna to wartość właściwości. Aby uzyskać więcej informacji, zobacz wywołania zwrotne właściwości zależności i walidacja.

  • Chcesz użyć ustalonych Konwencji metadanych, które są również używane przez WPF procesy, takie jak raportowanie, czy zmiana wartości właściwości powinna wymagać od systemu układu przetworzenia wizualizacji dla elementu. Lub chcesz mieć możliwość użycia zastąpień metadanych, aby klasy pochodne mogły zmieniać właściwości oparte na metadanych, takie jak wartość domyślna.

  • Chcesz, aby właściwości kontrolki niestandardowej otrzymywały wsparcie projektanta Visual Studio WPF, takie jak edytowanie okna Właściwości . Aby uzyskać więcej informacji, zobacz temat Tworzenie kontroli — przegląd.

Analizując te scenariusze, należy również rozważyć, czy można osiągnąć swój scenariusz, zastępując metadane istniejącej właściwości zależności, a nie implementując całkowicie nową właściwość. Określa, czy zastępowanie metadanych jest praktyczne, zależy od danego scenariusza i jak ściśle ten scenariusz jest podobny do implementacji we WPF właściwościach zależności i klas. Aby uzyskać więcej informacji na temat zastępowania metadanych dla istniejących właściwości, zobacz metadane właściwości zależności.

Lista kontrolna służąca do definiowania właściwości zależności

Definiowanie właściwości zależności obejmuje cztery różne koncepcje. Te pojęcia nie są koniecznie rygorystycznych kroków proceduralnych, ponieważ niektóre z nich są łączone jako pojedyncze wiersze kodu w implementacji:

  • Obowiązkowe Utwórz metadane właściwości dla właściwości zależności.

  • Zarejestruj nazwę właściwości w systemie właściwości, określając typ właściciela i typ wartości właściwości. Określ także metadane właściwości, jeśli są używane.

  • Zdefiniuj DependencyProperty Identyfikator jako public static readonly pole typu właściciel.

  • Zdefiniuj Właściwość "otoka" środowiska CLR, której nazwa jest zgodna z nazwą właściwości zależności. Zaimplementuj Właściwość "otoka" środowiska CLR get i metody set dostępu, aby połączyć się z właściwością zależności, która ją wykonuje.

Rejestrowanie właściwości przy użyciu systemu właściwości

Aby właściwość była właściwością zależności, należy zarejestrować tę właściwość w tabeli utrzymywanej przez system właściwości i nadać jej unikatowy identyfikator, który jest używany jako kwalifikator dla późniejszych operacji systemu właściwości. Te operacje mogą być operacjami wewnętrznymi lub własnym kodem wywołującym interfejsy API systemu właściwości. Aby zarejestrować właściwość, należy wywołać Register metodę w treści klasy (wewnątrz klasy, ale poza wszystkimi definicjami elementów członkowskich). Pole identyfikatora jest również udostępniane przez Register wywołanie metody jako wartość zwracaną. Powodem, że Register wywołanie jest wykonywane poza innymi definicjami elementów członkowskich, jest użycie tej wartości zwracanej do przypisania i utworzenia public static readonly pola typu DependencyProperty jako części klasy. To pole jest identyfikatorem właściwości zależności.

public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))

Konwencje nazw właściwości zależności

Istnieją ustalone konwencje nazewnictwa dotyczące właściwości zależności, które należy wykonać we wszystkich, ale wyjątkowych okolicznościach.

Sama właściwość zależności będzie miała podstawową nazwę "AquariumGraphic", jak w tym przykładzie, która jest podawana jako pierwszy parametr Register . Ta nazwa musi być unikatowa w obrębie każdego typu rejestrowania. Właściwości zależności dziedziczone za pomocą typów podstawowych są uznawane za należące już do typu rejestrowania; nie można ponownie zarejestrować nazw dziedziczonych właściwości. Istnieje jednak technika dodawania klasy jako właściciela właściwości zależności nawet wtedy, gdy ta właściwość zależności nie jest dziedziczona; Aby uzyskać szczegółowe informacje, zobacz metadane właściwości zależności.

Po utworzeniu pola identyfikatora Nazwij to pole za pomocą nazwy właściwości, która została zarejestrowana, oraz sufiksu Property . To pole jest identyfikatorem dla właściwości zależności i będzie używane później jako dane wejściowe dla SetValue GetValue wywołań i wywoływanych w otokach, przez każdy inny dostęp kodu do właściwości przez własny kod, przez dowolny dostęp do kodu zewnętrznego, przez system właściwości i potencjalnie przez XAML procesory.

Uwaga

Definiowanie właściwości zależności w treści klasy jest typową implementacją, ale można również zdefiniować właściwość zależności w konstruktorze statycznym klasy. Takie podejście może mieć sens, jeśli potrzebujesz więcej niż jednego wiersza kodu w celu zainicjowania właściwości zależności.

Implementacja "otoki"

Implementacja otoki powinna być wywoływana GetValue w get implementacji, a SetValue w set implementacji (oryginalne połączenie i pole rejestracji są wyświetlane w tym miejscu również na potrzeby przejrzystości).

We wszystkich ale wyjątkowych okolicznościach implementacje otoki powinny odpowiednio wykonywać tylko GetValue SetValue akcje i. Przyczyna tego problemu została omówiona w temacie Właściwości ładowania i zależności języka XAML.

Wszystkie istniejące właściwości zależności publicznej, które są udostępniane w WPF klasach, używają tego prostego modelu implementacji otoki. większość złożoności działania właściwości zależności jest zależne od zachowania systemu właściwości lub jest implementowana za pomocą innych koncepcji, takich jak wymuszone lub zmiany właściwości wywołania zwrotnego za pomocą metadanych właściwości.


public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
public Uri AquariumGraphic
{
  get { return (Uri)GetValue(AquariumGraphicProperty); }
  set { SetValue(AquariumGraphicProperty, value); }
}

Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))
Public Property AquariumGraphic() As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set(ByVal value As Uri)
        SetValue(AquariumGraphicProperty, value)
    End Set
End Property

Ponownie zgodnie z Konwencją nazwa właściwości otoki musi być taka sama jak nazwa wybrana i określona jako pierwszy parametr Register wywołania, które zarejestrował właściwość. Jeśli właściwość nie jest zgodna z Konwencją, nie należy wyłączać wszystkich możliwych użycia, ale wystąpi kilka istotnych problemów:

  • Niektóre aspekty stylów i szablonów nie będą działały.

  • Większość narzędzi i projektantów musi opierać się na konwencjach nazewnictwa, aby prawidłowo serializować XAML , lub aby zapewnić pomoc dla środowiska projektanta na poziomie właściwości.

  • Bieżąca implementacja WPF XAML modułu ładującego całkowicie pomija otoke i opiera się na konwencji nazewnictwa podczas przetwarzania wartości atrybutów. Aby uzyskać więcej informacji, zobacz temat Właściwości ładowania i zależności XAML.

Metadane właściwości dla nowej właściwości zależności

Po zarejestrowaniu właściwości zależności Rejestracja za pomocą systemu właściwości tworzy obiekt metadanych, który przechowuje charakterystyki właściwości. Wiele z tych cech ma ustawione wartości domyślne, jeśli właściwość jest zarejestrowana przy użyciu prostych podpisów Register . Inne podpisy Register umożliwiają określenie metadanych, które mają być rejestrowane podczas rejestrowania właściwości. Najczęstszymi metadanymi podaną dla właściwości zależności jest nadanie im wartości domyślnej stosowanej w nowych wystąpieniach, które używają właściwości.

Jeśli tworzysz właściwość zależności, która istnieje w klasie pochodnej FrameworkElement , można użyć bardziej wyspecjalizowanej klasy metadanych FrameworkPropertyMetadata zamiast PropertyMetadata klasy bazowej. Konstruktor dla FrameworkPropertyMetadata klasy ma kilka podpisów, w których można określić różne charakterystyki metadanych. Jeśli chcesz określić tylko wartość domyślną, Użyj podpisu, który przyjmuje jeden parametr typu Object . Przekaż ten parametr obiektu jako wartość domyślną specyficzną dla danej właściwości (podana wartość domyślna musi być typem dostarczonym jako propertyType parametr w Register wywołaniu).

Dla programu FrameworkPropertyMetadata można także określić flagi opcji metadanych dla właściwości. Te flagi są konwertowane na właściwości dyskretne w metadanych właściwości po rejestracji i są używane do komunikowania pewnych warunków z innymi procesami, takimi jak aparat układu.

Ustawianie odpowiednich flag metadanych

  • Jeśli właściwość (lub zmiany w jej wartości) wpływa na interfejs użytkownika (UI) , a w szczególności ma wpływ na to, w jaki sposób system układu ma rozmiar lub renderuje element na stronie, należy ustawić co najmniej jedną z następujących flag: AffectsMeasure , AffectsArrange , AffectsRender .

    • AffectsMeasure wskazuje, że zmiana tej właściwości wymaga zmiany w celu Interfejs użytkownika renderowania, gdzie obiekt zawierający może wymagać więcej lub mniej miejsca w obrębie elementu nadrzędnego. Na przykład właściwość "width" powinna mieć ustawioną tę flagę.

    • AffectsArrange wskazuje, że zmiana tej właściwości wymaga zmiany Interfejs użytkownika w celu renderowania, która zwykle nie wymaga zmiany w dedykowanym miejscu, ale wskazuje, że pozycjonowanie w miejscu zostało zmienione. Na przykład właściwość "Alignment" powinna mieć tę flagę.

    • AffectsRender wskazuje, że wystąpiła inna zmiana, która nie ma wpływu na układ i miarę, ale wymaga innego renderowania. Przykładem może być właściwość, która zmienia kolor istniejącego elementu, na przykład "background".

    • Te flagi są często używane jako protokół w metadanych dla własnych implementacji przesłonięcia systemu właściwości lub wywołania zwrotnego układu. Na przykład może istnieć OnPropertyChanged wywołanie zwrotne, które zostanie wywołane, InvalidateArrange Jeśli jakakolwiek Właściwość wystąpienia zgłasza zmianę wartości i ma AffectsArrange tak jak true w metadanych.

  • Niektóre właściwości mogą mieć wpływ na charakterystykę renderowania zawierającego element nadrzędny, w powyższych sposób i poza zmiany w wymaganym rozmiarze wymienionym powyżej. Przykładem jest MinOrphanLines Właściwość użyta w modelu dokumentu przepływu, gdzie zmiany tej właściwości mogą zmienić ogólne renderowanie dokumentu przepływu, który zawiera akapit. Użyj AffectsParentArrange lub AffectsParentMeasure , aby zidentyfikować podobne przypadki we własnych właściwościach.

  • Domyślnie właściwości zależności obsługują powiązanie danych. Można celowo wyłączyć powiązanie danych, w przypadku gdy nie istnieje realistyczny scenariusz dla powiązania danych lub w przypadku, gdy wydajność w powiązaniu danych dla dużego obiektu jest rozpoznawana jako problem.

  • Domyślnie powiązanie danych Mode dla właściwości zależności ma wartość domyślną OneWay . Można zawsze zmienić powiązanie na TwoWay wystąpienie powiązania. Aby uzyskać szczegółowe informacje, zobacz Określanie kierunku powiązania. Ale jako autor właściwości zależności można wybrać opcję TwoWay domyślnego użycia trybu powiązania. Przykładem istniejącej właściwości zależności jest MenuItem.IsSubmenuOpen ; scenariusz dla tej właściwości polega na tym, że IsSubmenuOpen logika ustawienia i przedniego MenuItem Praca z domyślnym stylem motywu. IsSubmenuOpenLogika właściwości używa powiązania danych natywnie do utrzymania stanu właściwości zgodnie z innymi właściwościami stanu i wywołaniami metod. Inna Przykładowa właściwość, która domyślnie tworzy powiązanie, TwoWay to TextBox.Text .

  • Dziedziczenie właściwości można także włączyć we właściwości zależności niestandardowej przez ustawienie Inherits flagi. Dziedziczenie właściwości jest przydatne w przypadku scenariusza, w którym elementy nadrzędne i podrzędne mają wspólną właściwość, i mają sens dla elementów podrzędnych, aby mieć ustawioną taką samą wartość właściwości, co ustawienie nadrzędne. Przykładową właściwością dziedziczenia jest DataContext , która jest używana na potrzeby operacji wiązania w celu włączenia ważnego scenariusza wzorca szczegółowości dla prezentacji danych. Dzięki DataContext dziedziczeniu wszystkie elementy podrzędne dziedziczą również ten kontekst danych. Ze względu na dziedziczenie wartości właściwości można określić kontekst danych na stronie lub w katalogu głównym aplikacji, a nie trzeba go ponownie określić dla powiązań we wszystkich możliwych elementach podrzędnych. DataContext jest również dobrym przykładem, aby zilustrować, że dziedziczenie przesłania wartość domyślną, ale zawsze można ją ustawić lokalnie dla każdego określonego elementu podrzędnego. Aby uzyskać szczegółowe informacje, zobacz Używanie wzorca Master-Detail z danymi hierarchicznymi. Dziedziczenie wartości właściwości ma możliwy koszt wydajności i dlatego powinno być używane oszczędnie. Aby uzyskać szczegółowe informacje, zobacz dziedziczenie wartości właściwości.

  • Ustaw Journal flagę, aby wskazać, czy właściwość zależności ma zostać wykryta lub użyta przez usługi rejestrowania nawigacji. Przykładem jest SelectedIndex Właściwość; wszelkie elementy wybrane w kontrolce wyboru powinny być utrwalane, gdy historia rejestrowania jest przechodzenie.

Właściwości zależności tylko do odczytu

Można zdefiniować właściwość zależności, która jest tylko do odczytu. Jednak scenariusze, dla których można zdefiniować właściwość jako tylko do odczytu, różnią się w zależności od procedury rejestrowania ich w systemie właściwości i ujawniania identyfikatora. Aby uzyskać więcej informacji, zobacz właściwości zależności tylko do odczytu.

Właściwości zależności typu kolekcji

Właściwości zależności typu kolekcji zawierają kilka dodatkowych problemów z implementacją, które należy wziąć pod uwagę. Aby uzyskać szczegółowe informacje, zobacz właściwości zależności typu kolekcji.

Zagadnienia dotyczące zabezpieczeń właściwości zależności

Właściwości zależności powinny być deklarowane jako właściwości publiczne. Pola identyfikatora właściwości zależności powinny być deklarowane jako publiczne pola statyczne. Nawet jeśli podjęto próbę zadeklarować inne poziomy dostępu (na przykład chronione), do właściwości zależności można zawsze uzyskać dostęp za pomocą identyfikatora w połączeniu z interfejsami API systemu właściwości. Nawet pole identyfikatora chronionego jest potencjalnie dostępne ze względu na raportowanie metadanych lub interfejsy API określania wartości, które są częścią systemu właściwości, na przykład LocalValueEnumerator . Aby uzyskać więcej informacji, zobacz temat Ochrona właściwości zależności.

Właściwości zależności i konstruktory klas

Istnieje ogólna zasada programowania kodu zarządzanego (często wymuszane przez narzędzia do analizy kodu, takie jak FxCop), które konstruktory klas nie powinny wywoływanie metod wirtualnych. Wynika to z faktu, że konstruktory mogą być wywoływane jako podstawowe inicjujące Konstruktor klasy pochodnej i wprowadzanie metody wirtualnej za pomocą konstruktora może wystąpić z niekompletnym stanem inicjalizacji konstruowanego wystąpienia obiektu. Gdy pochodzą z klasy, która już pochodzi z DependencyObject , należy pamiętać, że system właściwości wywołuje i udostępnia metody wirtualne wewnętrznie. Te metody wirtualne są częścią WPF usług systemu właściwości. Zastąpienie metod umożliwia klasom pochodnym uczestnictwo w wyznaczania wartości. Aby uniknąć potencjalnych problemów z inicjalizacją środowiska uruchomieniowego, nie należy ustawiać wartości właściwości zależności w konstruktorach klas, chyba że jest on zgodny z bardzo specyficznym wzorcem konstruktora. Aby uzyskać szczegółowe informacje, zobacz bezpieczne wzorce konstruktorów dla DependencyObjects.

Zobacz też