Passare parametri a un effetto come proprietà associate
Le proprietà associate possono essere usate per definire i parametri di effetto che rispondono a modifiche delle proprietà in fase di esecuzione. Questo articolo spiega come usare le proprietà associate per passare i parametri a un effetto e come modificare un parametro in fase di esecuzione.
Il processo di creazione dei parametri per un effetto che rispondono a modifiche delle proprietà in fase di esecuzione è il seguente:
- Creare una classe
static
che contenga una proprietà associata per ogni parametro che deve essere passato all'effetto. - Aggiungere alla classe una proprietà associata aggiuntiva che verrà usata per controllare l'aggiunta dell'effetto al controllo, o la sua rimozione dal controllo, a cui la classe verrà associata. Assicurarsi che tale proprietà associata registri un delegato
propertyChanged
che verrà eseguito quando cambia il valore della proprietà. - Creare un getter e un setter
static
per ogni proprietà associata. - Implementare la logica nel delegato
propertyChanged
per aggiungere e rimuovere l'effetto. - Implementare una classe annidata all'interno della classe
static
, denominata in base all'effetto, che crea una sottoclasse della classeRoutingEffect
. Per il costruttore, chiamare il costruttore della classe di base, passando una concatenazione del nome del gruppo di risoluzione e dell'ID univoco specificato per ogni classe di effetto specifica della piattaforma.
I parametri possono quindi essere passati all'effetto aggiungendo le proprietà associate, e i valori delle proprietà, al controllo appropriato. Inoltre, i parametri possono essere modificati in fase di esecuzione specificando un nuovo valore della proprietà associata.
Nota
Le proprietà associate sono un tipo speciale di proprietà associabile, definite in una classe ma associate ad altri oggetti. Sono riconoscibili in XAML perché si presentano come attributi che contengono una classe e un nome di proprietà separati da un punto. Per altre informazioni, vedere Proprietà associate.
L'applicazione di esempio illustra un ShadowEffect
che aggiunge un'ombreggiatura al testo visualizzato da un controllo Label
. Inoltre, il colore dell'ombreggiatura può essere modificato in fase di esecuzione. Il diagramma seguente illustra le responsabilità di ogni progetto nell'applicazione di esempio, insieme alle relazioni tra di essi:
Un controllo Label
per la HomePage
è personalizzato da LabelShadowEffect
in ogni progetto specifico della piattaforma. I parametri vengono passati a ogni LabelShadowEffect
tramite le proprietà associate nella classe ShadowEffect
. Ogni classe LabelShadowEffect
deriva dalla classe PlatformEffect
per ogni piattaforma. Il risultato è l'aggiunta di un'ombreggiatura al testo visualizzato dal controllo Label
, come illustrato negli screenshot seguenti:
Creazione di parametri per l'effetto
È necessario creare una classe static
per rappresentare i parametri di effetto, come illustrato nell'esempio di codice seguente:
public static class ShadowEffect
{
public static readonly BindableProperty HasShadowProperty =
BindableProperty.CreateAttached ("HasShadow", typeof(bool), typeof(ShadowEffect), false, propertyChanged: OnHasShadowChanged);
public static readonly BindableProperty ColorProperty =
BindableProperty.CreateAttached ("Color", typeof(Color), typeof(ShadowEffect), Color.Default);
public static readonly BindableProperty RadiusProperty =
BindableProperty.CreateAttached ("Radius", typeof(double), typeof(ShadowEffect), 1.0);
public static readonly BindableProperty DistanceXProperty =
BindableProperty.CreateAttached ("DistanceX", typeof(double), typeof(ShadowEffect), 0.0);
public static readonly BindableProperty DistanceYProperty =
BindableProperty.CreateAttached ("DistanceY", typeof(double), typeof(ShadowEffect), 0.0);
public static bool GetHasShadow (BindableObject view)
{
return (bool)view.GetValue (HasShadowProperty);
}
public static void SetHasShadow (BindableObject view, bool value)
{
view.SetValue (HasShadowProperty, value);
}
...
static void OnHasShadowChanged (BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as View;
if (view == null) {
return;
}
bool hasShadow = (bool)newValue;
if (hasShadow) {
view.Effects.Add (new LabelShadowEffect ());
} else {
var toRemove = view.Effects.FirstOrDefault (e => e is LabelShadowEffect);
if (toRemove != null) {
view.Effects.Remove (toRemove);
}
}
}
class LabelShadowEffect : RoutingEffect
{
public LabelShadowEffect () : base ("MyCompany.LabelShadowEffect")
{
}
}
}
L'elemento ShadowEffect
contiene cinque proprietà associate, con getter e setter static
per ogni proprietà associata. Quattro di queste proprietà rappresentano i parametri da passare a ogni LabelShadowEffect
specifico della piattaforma. La classe ShadowEffect
definisce anche una proprietà associata HasShadow
che verrà usata per controllare l'aggiunta dell'effetto al controllo, o la sua rimozione dal controllo, a cui la classe ShadowEffect
verrà associata. Tale proprietà associata registra il metodo OnHasShadowChanged
che verrà eseguito quando cambia il valore della proprietà. Questo metodo aggiunge o rimuove l'effetto in base al valore della proprietà HasShadow
associata.
La classe LabelShadowEffect
annidata, che crea una sottoclasse della classe RoutingEffect
, supporta l'aggiunta e la rimozione dell'effetto. La classe RoutingEffect
rappresenta un effetto indipendente dalla piattaforma che esegue il wrapping di un effetto interno, in genere specifico della piattaforma. Ciò semplifica il processo di rimozione dell'effetto, poiché non è previsto l'accesso alle informazioni sul tipo in fase di compilazione per un effetto specifico della piattaforma. Il costruttore LabelShadowEffect
chiama il costruttore della classe di base, passando un parametro costituito da una concatenazione del nome del gruppo di risoluzione e dell'ID univoco specificato per ogni classe di effetto specifica della piattaforma. Questo abilita l'aggiunta e la rimozione dell'effetto nel metodo OnHasShadowChanged
, come indicato di seguito:
- Aggiunta dell'effetto: una nuova istanza di
LabelShadowEffect
viene aggiunta alla raccoltaEffects
del controllo. Questa operazione sostituisce l'utilizzo del metodoEffect.Resolve
per aggiungere l'effetto. - Rimozione dell'effetto: la prima istanza di
LabelShadowEffect
nella raccolta delEffects
controllo viene recuperata e rimossa.
Utilizzo dell'effetto
Ogni elemento LabelShadowEffect
specifico della piattaforma può essere utilizzato aggiungendo le proprietà associate a un controllo Label
, come illustrato nell'esempio di codice XAML seguente:
<Label Text="Label Shadow Effect" ...
local:ShadowEffect.HasShadow="true" local:ShadowEffect.Radius="5"
local:ShadowEffect.DistanceX="5" local:ShadowEffect.DistanceY="5">
<local:ShadowEffect.Color>
<OnPlatform x:TypeArguments="Color">
<On Platform="iOS" Value="Black" />
<On Platform="Android" Value="White" />
<On Platform="UWP" Value="Red" />
</OnPlatform>
</local:ShadowEffect.Color>
</Label>
Il codice C# equivalente per Label
è visualizzato nell'esempio seguente:
var label = new Label {
Text = "Label Shadow Effect",
...
};
Color color = Color.Default;
switch (Device.RuntimePlatform)
{
case Device.iOS:
color = Color.Black;
break;
case Device.Android:
color = Color.White;
break;
case Device.UWP:
color = Color.Red;
break;
}
ShadowEffect.SetHasShadow (label, true);
ShadowEffect.SetRadius (label, 5);
ShadowEffect.SetDistanceX (label, 5);
ShadowEffect.SetDistanceY (label, 5);
ShadowEffect.SetColor (label, color));
L'impostazione della proprietà associata ShadowEffect.HasShadow
su true
esegue il metodo ShadowEffect.OnHasShadowChanged
che aggiunge o rimuove LabelShadowEffect
dal controllo Label
. In entrambi gli esempi di codice, la proprietà associata ShadowEffect.Color
fornisce i valori di colore specifici della piattaforma. Per altre informazioni, vedere Classe Device.
Inoltre, con un Button
il colore dell'ombreggiatura può essere modificato in fase di esecuzione. Quando si fa clic su Button
, il codice seguente cambia il colore dell'ombreggiatura impostando la proprietà associata ShadowEffect.Color
:
ShadowEffect.SetColor (label, Color.Teal);
Utilizzo dell'effetto con uno stile
Gli effetti che possono essere utilizzati aggiungendo le proprietà associate a un controllo possono anche essere utilizzati da uno stile. L'esempio di codice XAML seguente mostra uno stile esplicito per l'effetto di ombreggiatura che può essere applicato ai controlli Label
:
<Style x:Key="ShadowEffectStyle" TargetType="Label">
<Style.Setters>
<Setter Property="local:ShadowEffect.HasShadow" Value="True" />
<Setter Property="local:ShadowEffect.Radius" Value="5" />
<Setter Property="local:ShadowEffect.DistanceX" Value="5" />
<Setter Property="local:ShadowEffect.DistanceY" Value="5" />
</Style.Setters>
</Style>
Style
può essere applicato a un elemento Label
impostando la relativa proprietà Style
sull'istanza di Style
utilizzando l'estensione di markup StaticResource
, come illustrato nell'esempio di codice seguente:
<Label Text="Label Shadow Effect" ... Style="{StaticResource ShadowEffectStyle}" />
Per altre informazioni sugli stili, vedere Stili.
Creazione dell'effetto in ogni piattaforma
Le sezioni seguenti illustrano l'implementazione specifica della piattaforma della classe LabelShadowEffect
.
Progetto iOS
L'esempio di codice seguente mostra l'implementazione di LabelShadowEffect
per il progetto iOS:
[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.iOS
{
public class LabelShadowEffect : PlatformEffect
{
protected override void OnAttached ()
{
try {
UpdateRadius ();
UpdateColor ();
UpdateOffset ();
Control.Layer.ShadowOpacity = 1.0f;
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached ()
{
}
...
void UpdateRadius ()
{
Control.Layer.ShadowRadius = (nfloat)ShadowEffect.GetRadius (Element);
}
void UpdateColor ()
{
Control.Layer.ShadowColor = ShadowEffect.GetColor (Element).ToCGColor ();
}
void UpdateOffset ()
{
Control.Layer.ShadowOffset = new CGSize (
(double)ShadowEffect.GetDistanceX (Element),
(double)ShadowEffect.GetDistanceY (Element));
}
}
Il metodo OnAttached
chiama i metodi che recuperano i valori della proprietà associata utilizzando i getter ShadowEffect
e che impostano le proprietà Control.Layer
sui valori necessari per creare l'ombreggiatura. Questa funzionalità è incapsulata in un blocco try
/catch
, nell'evenienza in cui il controllo a cui l'effetto è associato non avesse le proprietà Control.Layer
. Il metodo OnDetached
non fornisce alcuna implementazione perché non sono necessarie operazioni di pulizia.
Rispondere alle modifiche delle proprietà
Se uno qualsiasi dei valori della proprietà associata ShadowEffect
cambia in fase di esecuzione, l'effetto dovrà rispondere visualizzando le modifiche. La risposta alle modifiche delle proprietà associabili avviene in una versione sottoposta a override del metodo OnElementPropertyChanged
, nella classe dell'effetto specifico della piattaforma, come illustrato nell'esempio di codice seguente:
public class LabelShadowEffect : PlatformEffect
{
...
protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
{
if (args.PropertyName == ShadowEffect.RadiusProperty.PropertyName) {
UpdateRadius ();
} else if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
UpdateColor ();
} else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
UpdateOffset ();
}
}
...
}
Il metodo OnElementPropertyChanged
aggiorna il raggio, il colore o l'offset dell'ombreggiatura, a condizione che valore della proprietà associata ShadowEffect
appropriata sia stato modificato. Deve sempre essere eseguito un controllo della proprietà modificata, dal momento che l'override può essere chiamato più volte.
Progetto Android
L'esempio di codice seguente mostra l'implementazione di LabelShadowEffect
per il progetto Android:
[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.Droid
{
public class LabelShadowEffect : PlatformEffect
{
Android.Widget.TextView control;
Android.Graphics.Color color;
float radius, distanceX, distanceY;
protected override void OnAttached ()
{
try {
control = Control as Android.Widget.TextView;
UpdateRadius ();
UpdateColor ();
UpdateOffset ();
UpdateControl ();
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached ()
{
}
...
void UpdateControl ()
{
if (control != null) {
control.SetShadowLayer (radius, distanceX, distanceY, color);
}
}
void UpdateRadius ()
{
radius = (float)ShadowEffect.GetRadius (Element);
}
void UpdateColor ()
{
color = ShadowEffect.GetColor (Element).ToAndroid ();
}
void UpdateOffset ()
{
distanceX = (float)ShadowEffect.GetDistanceX (Element);
distanceY = (float)ShadowEffect.GetDistanceY (Element);
}
}
Il metodo OnAttached
chiama i metodi che recuperano i valori della proprietà associata utilizzando i getter ShadowEffect
e chiama il metodo TextView.SetShadowLayer
per creare un'ombreggiatura utilizzando i valori della proprietà. Questa funzionalità è incapsulata in un blocco try
/catch
, nell'evenienza in cui il controllo a cui l'effetto è associato non avesse le proprietà Control.Layer
. Il metodo OnDetached
non fornisce alcuna implementazione perché non sono necessarie operazioni di pulizia.
Rispondere alle modifiche delle proprietà
Se uno qualsiasi dei valori della proprietà associata ShadowEffect
cambia in fase di esecuzione, l'effetto dovrà rispondere visualizzando le modifiche. La risposta alle modifiche delle proprietà associabili avviene in una versione sottoposta a override del metodo OnElementPropertyChanged
, nella classe dell'effetto specifico della piattaforma, come illustrato nell'esempio di codice seguente:
public class LabelShadowEffect : PlatformEffect
{
...
protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
{
if (args.PropertyName == ShadowEffect.RadiusProperty.PropertyName) {
UpdateRadius ();
UpdateControl ();
} else if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
UpdateColor ();
UpdateControl ();
} else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
UpdateOffset ();
UpdateControl ();
}
}
...
}
Il metodo OnElementPropertyChanged
aggiorna il raggio, il colore o l'offset dell'ombreggiatura, a condizione che valore della proprietà associata ShadowEffect
appropriata sia stato modificato. Deve sempre essere eseguito un controllo della proprietà modificata, dal momento che l'override può essere chiamato più volte.
Progetto UWP (Universal Windows Platform)
L'esempio di codice seguente mostra l'implementazione di LabelShadowEffect
per il progetto UWP (Universal Windows Platform):
[assembly: ResolutionGroupName ("MyCompany")]
[assembly: ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.UWP
{
public class LabelShadowEffect : PlatformEffect
{
Label shadowLabel;
bool shadowAdded = false;
protected override void OnAttached ()
{
try {
if (!shadowAdded) {
var textBlock = Control as Windows.UI.Xaml.Controls.TextBlock;
shadowLabel = new Label ();
shadowLabel.Text = textBlock.Text;
shadowLabel.FontAttributes = FontAttributes.Bold;
shadowLabel.HorizontalOptions = LayoutOptions.Center;
shadowLabel.VerticalOptions = LayoutOptions.CenterAndExpand;
UpdateColor ();
UpdateOffset ();
((Grid)Element.Parent).Children.Insert (0, shadowLabel);
shadowAdded = true;
}
} catch (Exception ex) {
Debug.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached ()
{
}
...
void UpdateColor ()
{
shadowLabel.TextColor = ShadowEffect.GetColor (Element);
}
void UpdateOffset ()
{
shadowLabel.TranslationX = ShadowEffect.GetDistanceX (Element);
shadowLabel.TranslationY = ShadowEffect.GetDistanceY (Element);
}
}
}
La piattaforma UWP non include un effetto di ombreggiatura, pertanto l'implementazione di LabelShadowEffect
in entrambe le piattaforme ne simula uno tramite l'aggiunta di un secondo elemento Label
sfalsato dietro all'elemento Label
primario. Il metodo OnAttached
crea il nuovo elemento Label
e imposta alcune proprietà di layout per Label
. Quindi chiama i metodi che recuperano i valori della proprietà associata utilizzando i getter ShadowEffect
e crea l'ombreggiatura impostando le proprietà TextColor
, TranslationX
e TranslationY
per controllare il colore e la posizione dell'elemento Label
. shadowLabel
viene quindi inserito sfalsato dietro l'elemento Label
primario. Questa funzionalità è incapsulata in un blocco try
/catch
, nell'evenienza in cui il controllo a cui l'effetto è associato non avesse le proprietà Control.Layer
. Il metodo OnDetached
non fornisce alcuna implementazione perché non sono necessarie operazioni di pulizia.
Rispondere alle modifiche delle proprietà
Se uno qualsiasi dei valori della proprietà associata ShadowEffect
cambia in fase di esecuzione, l'effetto dovrà rispondere visualizzando le modifiche. La risposta alle modifiche delle proprietà associabili avviene in una versione sottoposta a override del metodo OnElementPropertyChanged
, nella classe dell'effetto specifico della piattaforma, come illustrato nell'esempio di codice seguente:
public class LabelShadowEffect : PlatformEffect
{
...
protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
{
if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
UpdateColor ();
} else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
UpdateOffset ();
}
}
...
}
Il metodo OnElementPropertyChanged
aggiorna il colore o l'offset dell'ombreggiatura, a condizione che valore della proprietà associata ShadowEffect
appropriata sia stato modificato. Deve sempre essere eseguito un controllo della proprietà modificata, dal momento che l'override può essere chiamato più volte.
Riepilogo
Questo articolo ha spiegato come usare le proprietà associate per passare i parametri a un effetto e come modificare un parametro in fase di esecuzione. Le proprietà associate possono essere usate per definire i parametri di effetto che rispondono a modifiche delle proprietà in fase di esecuzione.