Qual è la differenza tra una proprietà di dipendenza (personalizzata) e una proprietà associata in WPF? Quali sono gli usi per ciascuno? In che modo le implementazioni differiscono tipicamente?
Risposte:
Dal momento che ho trovato poca o nessuna documentazione in merito, ci sono voluti un po 'di controllo sul codice sorgente , ma ecco una risposta.
Esiste una differenza tra la registrazione di una proprietà di dipendenza come proprietà regolare e come proprietà associata, diversa da una "filosofica" ( le proprietà regolari sono intese per essere utilizzate dal tipo dichiarante e dai suoi tipi derivati, le proprietà associate devono essere utilizzate come estensioni su DependencyObject
istanze arbitrarie ). "Filosofico", perché, come ha notato @MarqueIV nel suo commento alla risposta di @ ReedCopsey, le proprietà regolari possono essere utilizzate anche con DependencyObject
istanze arbitrarie .
Inoltre, devo essere in disaccordo con altre risposte che affermano che la proprietà allegata è "tipo di proprietà di dipendenza", perché è fuorviante - non ci sono "tipi" di proprietà di dipendenza. Al framework non interessa se la proprietà è stata registrata come allegata o meno - non è nemmeno possibile determinarla (nel senso che questa informazione non viene registrata, perché è irrilevante). Infatti, tutte le proprietà vengono registrate come se fossero proprietà associate, ma nel caso di quelle regolari vengono fatte alcune cose aggiuntive che modificano leggermente il loro comportamento.
Per risparmiarti la fatica di passare attraverso il codice sorgente da solo, ecco una versione ridotta di ciò che accade.
Quando si registra una proprietà senza metadati specificati, si chiama
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
restituisce esattamente lo stesso risultato della chiamata
DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
Tuttavia, quando si specificano i metadati, la chiamata
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
è equivalente a chiamare
var property = DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
defaultMetadata: new PropertyMetadata
{
DefaultValue = "default value",
});
property.OverrideMetadata(
forType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
La principale (e unica) differenza tra le proprietà di dipendenza regolari e associate sono i metadati predefiniti disponibili tramite la proprietà DependencyProperty.DefaultMetadata . Questo è anche menzionato nella sezione Osservazioni :
Per le proprietà non associate, non è possibile eseguire il cast del tipo di metadati restituito da questa proprietà a tipi derivati di tipo PropertyMetadata , anche se la proprietà è stata originariamente registrata con un tipo di metadati derivato. Se si desidera che i metadati registrati originariamente includano il tipo di metadati originale eventualmente derivato, chiamare GetMetadata (Type) , passando il tipo di registrazione originale come parametro.
Per le proprietà associate, il tipo di metadati restituiti da questa proprietà corrisponderà al tipo fornito nel metodo di registrazione RegisterAttached originale .
Questo è chiaramente visibile nel codice fornito. Piccoli suggerimenti sono nascosti anche nei metodi di registrazione, ad esempio per RegisterAttached
il parametro dei metadati è denominato defaultMetadata
, mentre per Register
è denominato typeMetadata
. Per le proprietà associate, i metadati forniti diventano i metadati predefiniti. In caso di proprietà regolari, tuttavia, i metadati predefiniti sono sempre una nuova istanza di PropertyMetadata
con solo DefaultValue
set (dai metadati forniti o automaticamente). Solo la chiamata successiva a OverrideMetadata
utilizza effettivamente i metadati forniti.
La principale differenza pratica è che in caso di proprietà regolari i CoerceValueCallback
e PropertyChangedCallback
sono applicabili solo per i tipi derivati dal tipo dichiarato come tipo proprietario e per le proprietà associate sono applicabili per tutti i tipi. Ad esempio in questo scenario:
var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");
il registrato PropertyChangedCallback
sarà chiamato se la proprietà è stata registrata come proprietà allegata, ma non sarà chiamato se è stato registrato come proprietà normale. Lo stesso vale per CoerceValueCallback
.
Una differenza secondaria deriva dal fatto che OverrideMetadata
richiede che derivi il tipo fornito DependencyObject
. In pratica significa che il tipo di proprietario per le proprietà regolari deve derivare da DependencyObject
, mentre per le proprietà associate in può essere qualsiasi tipo (incluse classi statiche, strutture, enumerazioni, delegati, ecc.).
Oltre al suggerimento di @ MarqueIV, in diverse occasioni mi sono imbattuto in opinioni secondo cui le proprietà regolari e associate differiscono nel modo in cui possono essere utilizzate in XAML . Vale a dire, che le proprietà regolari richiedono la sintassi del nome implicita rispetto alla sintassi del nome esplicita richiesta dalle proprietà associate. Questo tecnicamente non è vero , anche se in pratica di solito è così. Per chiarezza:
<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" />
<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />
In XAML puro , le uniche regole che governano l'utilizzo di queste sintassi sono le seguenti:
Il rispetto di queste condizioni consente di utilizzare la sintassi corrispondente indipendentemente dal fatto che la proprietà di dipendenza di supporto sia stata registrata come normale o allegata.
Ora il malinteso menzionato è causato dal fatto che la stragrande maggioranza dei tutorial (insieme a Visual Studio frammenti di codice di ) indica di utilizzare la proprietà CLR per le proprietà di dipendenza regolari e get / set accessors per quelle collegate. Ma nulla ti impedisce di utilizzare entrambi contemporaneamente, permettendoti di utilizzare la sintassi che preferisci.
Le proprietà allegate sono un tipo di proprietà di dipendenza. La differenza sta nel modo in cui vengono utilizzati.
Con una proprietà associata, la proprietà è definita su una classe che non è la stessa classe per la quale viene utilizzata. Di solito viene utilizzato per il layout. Buoni esempi sono Panel.ZIndex o Grid.Row: lo applichi a un controllo (ad esempio: Button), ma in realtà è definito in Panel o Grid. La proprietà è "collegata" all'istanza del pulsante.
Ciò consente a un contenitore, ad esempio, di creare proprietà che possono essere utilizzate su qualsiasi elemento dell'interfaccia utente.
Per quanto riguarda le differenze di implementazione, è fondamentalmente solo una questione di utilizzare Register e RegisterAttached quando si definisce la proprietà.
Le proprietà allegate sono fondamentalmente pensate per gli elementi del contenitore. Come se hai una griglia e hai grid.row ora questa è considerata una proprietà associata di un elemento della griglia. Inoltre puoi usare questa proprietà in texbox, pulsante ecc per impostarlo posto nella griglia.
La proprietà di dipendenza è come la proprietà fondamentalmente appartiene a qualche altra classe e viene utilizzata in un'altra classe. es: come se avessi un rettangolo qui l'altezza e la larghezza sono proprietà regolari del rettangolo, ma left e top sono le proprietà di dipendenza in quanto appartengono alla classe Canvass.
Le proprietà allegate sono un tipo speciale di DependencyProperties. Consentono di associare un valore a un oggetto che non sa nulla di questo valore. Un buon esempio di questo concetto sono i pannelli di layout. Ogni pannello di layout necessita di dati diversi per allineare i suoi elementi figlio. La tela ha bisogno di alto e di sinistra, il DockPanel ha bisogno di Dock, ecc. Dato che puoi scrivere il tuo pannello di layout, l'elenco è infinito. Quindi, vedi, non è possibile avere tutte queste proprietà su tutti i controlli WPF. La soluzione sono proprietà allegate. Sono definiti dal controllo che necessita dei dati di un altro controllo in un contesto specifico. Ad esempio un elemento allineato da un pannello di layout principale.
Penso che tu possa definire la proprietà associata nella classe stessa o puoi definirla in un'altra classe. Potremmo sempre usare la proprietà allegata per estendere i controlli microsoft standard. Ma la proprietà di dipendenza, la definisci nel tuo controllo personalizzato. Ad esempio, è possibile ereditare il controllo da un controllo standard e definire una proprietà di dipendenza nel proprio controllo e utilizzarla. Ciò equivale a definire una proprietà associata e utilizzare questa proprietà associata nel controllo standard.