Il tuo dubbio è legittimo e trova la risposta nei cosiddetti "binding" di XAML.
Il concetto del binding è quello di legare una specifica proprietà di un widget a una di un oggetto qualsiasi (quindi anche non di un elemento grafico. Immagina di legare il testo di un TextBlock a una proprietà, ovviamente di tipo string, di una classe della tua applicazione, in modo da farla visualizzare ogni qual volta essa cambi programmaticamente), e questo anche in maniera bidirezionale (il cambiamento dell'una aggiorna l'altra, e viceversa).
Per rientrare nel tuo discorso, faccio un esempio pratico. Ho due widget, un TextBox che contiene il path corrente, e subito alla sua destra c'è un Button con la lente d'ingrandimento che devono essere visualizzati entrambi assieme oppure nascosti sempre assieme. Per "legarne" la visibilità, si procede in questo modo in XAML:
<TextBox x:Name="CurrentPath" Visibility={Binding ElementName=SearchPath, Path=Visibility, Mode=TwoWay} />
<Button x:Name="SearchPath">
<Image Source="Images/SearchIcon.png" />
</Button>
Come vedi è sufficiente specificare il binding una sola volta, perché il sistema provvederà poi a legare i due widget tramite le rispettive proprietà. Nello specifico, il binding afferma questo: la proprità Visibility di CurrentPath è legata alla proprietà Visibility di SearchPatch bidirezionalmente. Se fosse omesso l'attributo Mode, sarebbe ovviamente monodirezionale (CurrentPath.Visibility riflettere soltanto i cambiamenti di SearchPath.Visibility, e non il viceversa).
Se i tipi delle proprietà da legare non dovessero coincidere, XAML dà la possibilità di utilizzare un "convertitore", cioè una classe che si occupa di eseguire delle operazioni sul valore della proprietà a cui siamo legati, in modo da "trasformarlo" prima di assegnarlo alla nostra proprietà.
Immagina che la tua classe principale a cui è legata la pagina della tua interfaccia grafica (Silverlight organizza la GUI in "pagine" indipendenti; immagina che sia l'equivalente di un "form") esponga la proprietà booleana IsRemote che indica se si sta lavorando in remoto, e che in base a questo valore uno o più widget debbano essere visibili oppure no.
La proprietà Visibility è un enumerativo, per cui non puoi assegnarle un booleano. Si ricorre, quindi, a un comunissimo e strausato convertitore (che ti riporto in C#, ma è semplice da capire):
class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool) value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Il metodo Convert è quello che c'interessa, e che fa un lavoro semplicissimo, come vedi. value è il valore che gli viene passato, e che sicuramente è un booleano nel nostro caso (siamo noi a "passarglielo"), che provvediamo a "castare" in bool. Dopodiché, in base al valore di verità, restituiamo l'enumerativo appropriato.
ConvertBack dev'essere implementato per forza, ma non c'interessa e restituiamo semplicemente null. Si usa per i convertitori bidirezionali, quando dall'enumerativo bisogna restituire il booleano.
Adesso mettiamo che vogliamo visualizzare un widget in base alla proprietà IsRemote di cui sopra. Ecco un estratto dello XAML:
<phone:PhoneApplicationPage
x:Class="FileManagerPage" Name="this" />
<phone:PhoneApplicationPage.Resources>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
</phone:PhoneApplicationPage.Resources>
<Button Content="Refresh" Visibility="{Binding ElementName=this, Path=IsRemote, Converter={StaticResource BooleanToVisibility}}" Click="OnRemoteResourcesRefreshClick" />
</phone:PhoneApplicationPage>
Da notare che fra le risorse della pagina ho dovuto dichiarare il convertitore in oggetto. Per il resto, tutto si risolve nel binding, come abbiamo visto prima, a cui viene semplicemente aggiunta la proprietà Converter che specifica la risorsa da utilizzare per la conversione.
Alla pagina abbiamo assegnato il nome "this", che rappresenta l'istanza della classe della GUI (che è quella che poi espone la proprietà IsRemote). Il nome può essere qualunque, ma si usa this per convenzione. Successivamente vien referenziato in ElementName.
Nel caso in cui dovessimo visualizzare in maniera mutuamente esclusiva widget legati a IsRemote, e altri alla sua negazione, le strade possibili sono due. La prima è che si crea un convertitore come quello di prima, che funziona in logica negata: serve a nascondere un oggetto se il booleano è vero, e viceversa. La seconda, che è quella che scelgo io, è quella di esporre la proprietà complementare di IsRemote, che chiamiamo ad esempio IsLocal, ed eseguire il binding degli altri widget a questa.
Per adesso finisco qui, ma aggiungo qualche considerazione. XAML può sembrare complicato a un primo approccio, perché si tratta di un modello abbastanza diverso da quello a cui magari siamo abituati. Ma una volta cominciato a lavorarci si apprezza la notevole potenza espressiva (nonché comodità!) del pattern Model View ViewModel su cui è basato (che deriva dal pattern Model View Presenter, a sua volta derivato dal più noto Model View Controller).
La cosa intrigante è che puoi definire con XAML tantissima roba che normalmente richiederebbe codice con un altro modello (chi ha sviluppato con Android sa cosa voglio dire!), e al contempo disaccoppiando moltissimo lo sviluppo dell'interfaccia grafica da quello del codice.
In generale io parto da XAML per definire la GUI, e quando poi serve costruisco quel poco di codice funzionale alla GUI stessa. In particolare scrivo molti converter perché mi permettono di realizzare cose molto carine, e faccio uso di "data binding" in modo da "popolare" automaticamente i widget con dei dati in maniera molto semplice ed efficace.
Il mio personalissimo problema è che una volta assaggiato XAML, lavorare con altro mi deprime.