Confrontare un'interfaccia utente basata su eventi con un'interfaccia utente associata a dati
Un'interfaccia utente basata su eventi è progettata in base agli eventi esposti da un controllo. Questi eventi possono essere associati al codice del gestore eventi richiamato quando viene attivato l'evento. Si supponga, ad esempio, di avere un pulsante che quando viene selezionato esegue un'operazione a esecuzione prolungata. Il gestore eventi assegnato all'evento Clicked
potrebbe avviare l'operazione e quindi impostare la proprietà IsEnabled
del pulsante su false
, impedendo che il pulsante venga nuovamente selezionato mentre l'operazione è in esecuzione.
Un'interfaccia utente associata a dati usa il data binding per presentare e interagire con i dati. Le proprietà dei controlli sono associate alle proprietà dell'oggetto dati e tali associazioni possono rilevare le modifiche nelle proprietà. Usando l'esempio precedente, prendere in considerazione il pulsante che esegue un'operazione a esecuzione prolungata. Anziché disabilitare il pulsante nel code-behind, la proprietà IsEnabled
è associata alla proprietà IsBusy
dell'oggetto dati. Ogni volta che l'oggetto dati diventa "occupato", lo stato abilitato del pulsante viene modificato automaticamente in modo corrispondente.
Vantaggi e svantaggi dell'uso di eventi e code-behind
L'uso del gestore eventi del controllo con code-behind è un modo rapido e pratico per progettare la logica dell'app per l'interfaccia utente. Si usa il codice per chiamare i servizi per ottenere dati, eseguire operazioni su tali dati e interagire con i controlli nella pagina. Il codice viene usato per mantenere sincronizzati l'interfaccia utente e i dati.
Si consideri l'esempio di un'app del servizio meteo. Il frammento di XAML seguente contiene un semplice pulsante dell'interfaccia utente selezionato dall'utente per ottenere i dati più recenti e aggiornare l'interfaccia utente con informazioni sull'umidità.
<VerticalStackLayout Margin="10">
<HorizontalStackLayout Spacing="20">
<Label Text="Postal Code:" VerticalOptions="Center" />
<Entry x:Name="PostalCode" WidthRequest="100" />
<Button x:Name="RefreshWeatherButton" Text="Refresh" WidthRequest="200" Clicked="RefreshWeatherButton_Clicked" />
</HorizontalStackLayout>
<Label x:Name="Humidity" Text="Humidity: ?" />
</VerticalStackLayout>
In questo esempio sono presenti tre controlli denominati:
- Controllo
Entry
denominato PostalCode. - Controllo
Button
denominato RefreshWeatherButton. - Controllo
Label
denominato Humidity.
RefreshWeatherButton
ha un gestore eventi dichiarato per l'evento Clicked
. Quando si fa clic sul pulsante, il gestore eventi esegue una query su un servizio meteo per le previsioni meteo più recenti, usando i dati immessi nel controllo di immissione PostalCode
e imposta il testo dell'etichetta Humidity
sull'umidità corrente.
private void RefreshWeatherButton_Clicked(object sender, EventArgs e)
{
WeatherService.Location = PostalCode.Text;
WeatherService.Refresh();
Humidity.Text = $"Humidity: {WeatherService.Humidity}";
}
In questo gestore eventi tre controlli sono strettamente associati tra loro e con i dati tramite il code-behind.
Questa progettazione è ideale per le interfacce utente di piccole dimensioni, ma non appena l'interfaccia utente diventa complessa, la gestione di un code-behind strettamente associato può diventare problematica. Se si elimina o si modifica un controllo, è necessario pulire tutto il codice che usa tali controlli dell'interfaccia utente, che potrebbero includere il gestore eventi. Se si decide di riprogettare l'interfaccia utente, sarà necessario anche effettuare il refactoring di una quantità elevata di codice. E quando cambia la struttura dei dati di supporto, è necessario esaminare in modo approfondito il codice di ogni interfaccia utente per rimanere sincronizzati.
Il data binding aiuta
I data binding possono essere implementati in XAML o nel codice, ma sono molto più comuni in XAML, in cui consentono di ridurre le dimensioni del file code-behind. Grazie alla sostituzione del codice procedurale nei gestori eventi con codice dichiarativo o markup, l'app viene semplificata e chiarita. Poiché i binding non richiedono code-behind, è possibile creare, modificare o riprogettare facilmente l'interfaccia utente per adattarsi alla modalità di presentazione dei dati.
Di seguito viene esaminato lo stesso esempio della sezione precedente, ma aggiornato per usare il data binding:
<VerticalStackLayout Margin="10">
<HorizontalStackLayout Spacing="20">
<Label Text="Postal Code:" VerticalOptions="Center" />
<Entry Text="{Binding Location, Mode=OneWayToSource}" WidthRequest="100" />
<Button Text="Refresh" Command="{Binding RefreshWeather}" WidthRequest="200" />
</HorizontalStackLayout>
<Label Text="{Binding Humidity}" />
</VerticalStackLayout>
È possibile individuare le proprietà associate ai dati, perché usano la sintassi dell'estensione XAML {Binding ...}
per il valore della proprietà. Le specifiche, descritte più avanti in questo modulo, non sono al momento rilevanti.
Gli stessi tre controlli vengono dichiarati nel codice XAML, ma nessuno di essi è denominato, perché non è necessario un nome:
Controllo
Entry
:La proprietà
Text
di questo controllo è associata a una proprietà denominataLocation
.Controllo
Button
:La proprietà
Command
di questo pulsante è associata a una proprietà denominataRefreshWeather
.Command
è una proprietà sul pulsante che richiama il codice quando viene premuto il pulsante. È un'alternativa all'eventoClicked
usato nel data binding.Controllo
Label
:Questa proprietà
Text
è associata a una proprietà denominataHumidity
.
In questa semplice interfaccia utente, tutto il code-behind viene eliminato. La rimozione di tutto il code-behind non è la funzione specifica del data binding, anche se in genere è possibile. Code-behind ha ancora un ruolo importante. L'utente determina la quantità di data binding da implementare.
Ora l'interfaccia utente è associata in modo libero a un oggetto dati. Perché è ad accoppiamento debole invece di strettamente accoppiata? A causa del modo in cui vengono valutati i binding. Ogni controllo ha una proprietà BindingContext
. Se il contesto non è impostato, viene usato il contesto del controllo padre e così via finché non viene valutata la radice del codice XAML. Quando vengono valutati i binding, l'istanza dell'oggetto del contesto viene verificata per individuare le proprietà necessarie, ad esempio il binding Text
del controllo etichetta alla proprietà Humidity
del contesto. Se Humidity
non esiste nel contesto, non accade nulla.
Poiché l'interfaccia utente è ad accoppiamento debole, è possibile riprogettare l'interfaccia utente senza preoccuparsi di interrompere il codice. È tuttavia possibile interrompere la funzionalità. È ad esempio possibile eliminare il pulsante senza interrompere la compilazione e l'esecuzione dell'app, ma non sarà possibile aggiornare il meteo. D'altra parte, è possibile sostituire i controlli Entry
e Button
con il singolo controllo SearchBar
. Questo controllo consente di immettere testo e richiamare un comando.
<SearchBar Text="{Binding Location, Mode=OneWayToSource}" SearchCommand="{Binding RefreshWeather}" />
Come si può notare, l'uso del data binding nella progettazione dell'interfaccia utente consente di evolvere e modificare l'interfaccia utente senza molto lavoro. Mantiene l'interfaccia utente sincronizzata automaticamente con i dati e la logica dell'app è separata dall'interfaccia utente.