Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Il cuore digitale di F1 a Biggin Hill: l'infrastruttura Lenovo dietro la produzione media
Il cuore digitale di F1 a Biggin Hill: l'infrastruttura Lenovo dietro la produzione media
Nel Formula 1 Technology and Media Centre di Biggin Hill, la velocità delle monoposto si trasforma in dati, immagini e decisioni in tempo reale grazie all’infrastruttura Lenovo che gestisce centinaia di terabyte ogni weekend di gara e collega 820 milioni di spettatori nel mondo
DJI Osmo Mobile 8: lo stabilizzatore per smartphone con tracking multiplo e asta telescopica
DJI Osmo Mobile 8: lo stabilizzatore per smartphone con tracking multiplo e asta telescopica
Il nuovo gimbal mobile DJI evolve il concetto di tracciamento automatico con tre modalità diverse, un modulo multifunzionale con illuminazione integrata e controlli gestuali avanzati. Nel gimbal è anche presente un'asta telescopica da 215 mm con treppiede integrato, per un prodotto completo per content creator di ogni livello
Recensione Pura 80 Pro: HUAWEI torna a stupire con foto spettacolari e ricarica superveloce
Recensione Pura 80 Pro: HUAWEI torna a stupire con foto spettacolari e ricarica superveloce
Abbiamo provato il nuovo HUAWEI Pura 80 Pro. Parliamo di uno smartphone che è un vero capolavoro di fotografia mobile, grazie ad un comparto completo in tutto e per tutto, In questa colorazione ci è piaciuto molto, ma i limiti hardware e software, seppur in netto miglioramento, ci sono ancora. Ma HUAWEI ha fatto davvero passi da gigante per questa nuova serie Pura 80. Buona anche l'autonomia e soprattutto la ricarica rapida sia cablata che wireless, velocissima.
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 25-09-2010, 10:31   #1
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
[C# - WPF]File Explorer: metodo più corretto per il pattern Model-View-ViewModel?

Salve a tutti, per necessità mi sono messo a studiare questo pattern e sto provando a realizzare la conversione di un'applicazione che avevo in WinForm.
L'applicazione è un semplice File Explorer, a sinistra l'elenco delle cartelle, a destra la lista dei files in modalità dettagli.

Ho cominciato per gradi, ho quindi da una parte un'applicazione wpf completa che mi carica i nodi dell'albero on demand impostando un path qualsiasi e dall'altra un programmino che mi carica tutti i file di una cartella preimpostata.

Ora devo fondere il tutto in un singolo programma e ho qualche dubbio su come seguire correttamente il pattern MVVM.

Ho creato 3 ViewModel: un ViewModel per ogni elemento del treeview (TreeItemViewModel), uno per ogni elemento della ListView (ListItemViewModel) e un ViewModel (MainViewModel) che è quello che associo come Datacontext alla Vista.

Quest'ultimo nel suo costruttore crea il nodo di root dell'albero e lo aggiunge alla lista dei TreeItemViewModel bindata con la vista (e fin qui nulla da dire) e inizializza la lista dei ListItemViewModel, anch'essa bindata con la vista, sempre al nodo di root.

Ora il problema è che quando clicco su un nuovo nodo, il rispettivo TreeItemViewModel viene richiamato ed eseguito, ma ovviamente il cambiamento non viene invocato poichè il MainViewModel non sa qual'è il nodo selezionato e allo stesso modo il TreeItemViewModel non ha conoscenza del MainViewModel...

Qual'è il metodo corretto? Devo aggiungere un membro alla MainViewModel che indica qual'è la cartella selezionata, aggiungendo al costruttore del TreeViewItemModel un riferimento al MainViewModel ad esso associato?
O esiste un metodo più elegante?

spero si capisca quel che voglio
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 26-09-2010, 00:52   #2
gugoXX
Senior Member
 
L'Avatar di gugoXX
 
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
Quote:
Originariamente inviato da Donbabbeo Guarda i messaggi
e allo stesso modo il TreeItemViewModel non ha conoscenza del MainViewModel...
Puoi farglielo conoscere. Non rompi il pattern.
Puoi fareglielo conoscere nel costruttore, come dici tu.

Oppure esponi un evento da parte dei TreeItemViewModel, intercettato dal MainViewModel.
Se fai cosi' ricordati di de-registrare tutte le sottoscrizioni.

Quote:
Ora il problema è che quando clicco su un nuovo nodo, il rispettivo TreeItemViewModel viene richiamato ed eseguito,
Oppure... cosa usi per eseguire quel metodo nel TreeItemViewModel?

PS: Il file explorer c'e' gia' come controllo gia fatto
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto.
E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test.
gugoXX è offline   Rispondi citando il messaggio o parte di esso
Old 26-09-2010, 12:45   #3
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
Quote:
Originariamente inviato da gugoXX Guarda i messaggi
Oppure... cosa usi per eseguire quel metodo nel TreeItemViewModel?
Ho un presentation member booleano chiamato IsSelezionato, associato in two-way binding con il parametro IsSelected del TreeViewItem datatemplate.

Proverò così quindi: nel set di IsSelezionato aggiungerò un'istruzione per associare il nodo al MainViewModel.

Quote:
Originariamente inviato da gugoXX Guarda i messaggi
PS: Il file explorer c'e' gia' come controllo gia fatto
Davvero? Mica lo sapevo...
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 01-10-2010, 10:36   #4
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
E' insorto un nuovo problema...
In pratica alla pressione di un tasto viene eseguita un'azione lunga e qundi volevo avviare una progress bar fino al termine dell'operazione.

Precisamente è una progress bar circolare sempre attiva, quindi agisco semplicemente sulla proprietà visibility del suo viewbox, così:

Codice:
Visibility="{Binding ProgressBarShow, Converter={StaticResource BoolToVisibility}, UpdateSourceTrigger=PropertyChanged}"
La proprietà ProgressBarShow è quindi presente sul ViewModel nel modo più banale possibile:

Codice:
public bool ProgressBarShow
        {
            get { return _progressbarshow; }
            set
            {
                if (value != _progressbarshow)
                {
                    _progressbarshow = value;
                    OnPropertyChanged("ProgressBarShow");
                }
            }
        }
E quindi, alla pressione del tasto è associato un DelegateCommand di questo tipo:

Codice:
public DelegateCommand RicercaCommand { get; private set; }
this.RicercaCommand = new DelegateCommand((o) => this.Ricerca(),null);
Ed il metodo ricerca è così definito:

Codice:
public void Ricerca()
        {
            ProgressBarShow = true;

            //Do stuff
            Thread.Sleep(10000);
        }
Io mi sarei aspettato che la progressbar venisse mostrata appena premuto il pulsante, invece viene mostrata correttamente al termine del metodo Ricerca.
Se invece inserisco un Debug.Print prima di ProgressBarShow=true, esso viene mostrato immediatamente. Avete qualche suggerimento?
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 01-10-2010, 12:19   #5
gugoXX
Senior Member
 
L'Avatar di gugoXX
 
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
Il metodo Ricerca() deve essere lanciato da un altro Thread.
Anche perche' senno' bloccheresti la GUI per tutta la durata della ricerca.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto.
E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test.
gugoXX è offline   Rispondi citando il messaggio o parte di esso
Old 01-10-2010, 15:21   #6
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
Quote:
Originariamente inviato da gugoXX Guarda i messaggi
Il metodo Ricerca() deve essere lanciato da un altro Thread.
Anche perche' senno' bloccheresti la GUI per tutta la durata della ricerca.
Praticamente quello che facevo con le WindowsForm

Chissà che mi aspettavo con le WPF
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 05-10-2010, 11:59   #7
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
Ok, ora non so veramente che pesci pigliare e non capisco cosa succede...

In pratica ho aggiunto un File System Watcher per monitorare la cartella corrente: quando un file viene eliminato, controllo la lista delle cartelle (è una proprietà del modelview stesso) e faccio ListaCartelle.Remove(file).

Ho fatto un pò di prove: il file esiste nella observable collection, viene trovato correttamente, ma quando cerco di rimuoverlo dalla lista, il programma crasha, stessa cosa se provo ad aggiungerne uno nuovo.

Il bello è che questo succede esclusivamente negli eventi del file system watcher, se provo a fare la stessa cosa tramite un comando associato ad un bottone funziona tranquillamente.

Da che può dipendere?

L'evento del filesystemwatcher è questo:

Codice:
void watcher_Deleted(object sender, FileSystemEventArgs e)
        {
            foreach (FileViewModel f in ListaFiles)
            {
                if (f.Nome == e.Name && f.Percorso == e.FullPath)
                {
                    ListaFiles.Remove(f);
                    break;
                }
            }
        }
Mentre ListaFiles è una semplice proprietà che espone una variabile privata della classe ed è una ObservableCollection di classi FileViewModel.
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 05-10-2010, 12:14   #8
gugoXX
Senior Member
 
L'Avatar di gugoXX
 
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
metti un try-catch e dicci l'eccezione, che con qualche probabilita' sara' la solita dove stai cercando di cambiare una property di un DependencyObject dal di fuori del Thread che ha creato il dependencyObject stesso.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto.
E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test.

Ultima modifica di gugoXX : 05-10-2010 alle 12:31.
gugoXX è offline   Rispondi citando il messaggio o parte di esso
Old 05-10-2010, 19:14   #9
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
Quote:
Originariamente inviato da gugoXX Guarda i messaggi
metti un try-catch e dicci l'eccezione, che con qualche probabilita' sara' la solita dove stai cercando di cambiare una property di un DependencyObject dal di fuori del Thread che ha creato il dependencyObject stesso.
C'ho pensato appena salito in macchina... Domattina try-catcho e vi faccio sapere, oggi pomeriggio non ho avuto tempo.
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 06-10-2010, 10:31   #10
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
Il messaggio è quello che ci si aspettava:
"This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread".

Ho letto un pò di documentazione sull'argomento, ma molte soluzioni mi sembrano fin troppo per il mio progetto. Ho provato quindi a farmi un delegato di questo tipo:

Codice:
private delegate void RemoveFileDelegate(FileViewModel f);
        private void RemoveFile(FileViewModel f)
        {
            ListaFiles.Remove(f);
        }
        void watcher_Deleted(object sender, FileSystemEventArgs e)
        {
            foreach (FileViewModel f in ListaFiles)
            {
                if (f.Nome == e.Name && f.Percorso == e.FullPath)
                {
                    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new RemoveFileDelegate(RemoveFile), f);
                }
            }
        }
Ed ora funziona, ma sarei curioso di sapere a quali problemi potrei andare in contro con questo metodo...

EDIT: così facendo, se elimino più di un file per volta crasha...
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐

Ultima modifica di Donbabbeo : 06-10-2010 alle 11:05.
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 06-10-2010, 14:52   #11
gugoXX
Senior Member
 
L'Avatar di gugoXX
 
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
Il problema e' che la ObservableCollection e' un oggetto WPF, e non si dovrebbe usare nei ViewModel, altrimenti corri il rischio di sbattere di nuovo contro il problema della sincronizzazione, il cui dimenticarsi e' invece uno degli scopi di MVVM
Ma non puoi fare a meno di implementare cio' che WPF usa della ObervableCollection, altrimenti la collezione non viene rinfrescata sulla GUI.

In pratica la tua collezione deve essere una normale List (O qualunque ICollection del tipo che ti interessa) ma anche implementare INotifyCollectionChanged

Esistono online diverse implementazioni di una tale ObservableCollection lookalike che se userai, e se hai implementato MVVM correttamente, ti risolveranno il problema nel modo migliore.

Per iniziare e per vedere se stai andando nella direzione giusta, prima di cercare e usare una di queste nuove ObservableCollection, usa una List<> normale.
Semplicemente lancia NotifyPropertyChanged ogni volta che modifichi il contenuto della lista.
L'intera lista verra' quindi rinfrescata sulla GUI, il che non e' ottimale, ma almeno non dovresti piu' ricevere l'errore.
Potrai quindi proseguire sostituendo la List<> con il nuovo tipo corretto per beneficiare al meglio della messaggistica.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto.
E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test.

Ultima modifica di gugoXX : 06-10-2010 alle 14:55.
gugoXX è offline   Rispondi citando il messaggio o parte di esso
Old 06-10-2010, 15:13   #12
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
Quote:
Originariamente inviato da gugoXX Guarda i messaggi
Il problema e' che la ObservableCollection e' un oggetto WPF, e non si dovrebbe usare nei ViewModel, altrimenti corri il rischio di sbattere di nuovo contro il problema della sincronizzazione, il cui dimenticarsi e' invece uno degli scopi di MVVM
Ma non puoi fare a meno di implementare cio' che WPF usa della ObervableCollection, altrimenti la collezione non viene rinfrescata sulla GUI.

In pratica la tua collezione deve essere una normale List (O qualunque ICollection del tipo che ti interessa) ma anche implementare INotifyCollectionChanged

Esistono online diverse implementazioni di una tale ObservableCollection lookalike che se userai, e se hai implementato MVVM correttamente, ti risolveranno il problema nel modo migliore.

Per iniziare e per vedere se stai andando nella direzione giusta, prima di cercare e usare una di queste nuove ObservableCollection, usa una List<> normale.
Semplicemente lancia NotifyPropertyChanged ogni volta che modifichi il contenuto della lista.
L'intera lista verra' quindi rinfrescata sulla GUI, il che non e' ottimale, ma almeno non dovresti piu' ricevere l'errore.
Potrai quindi proseguire sostituendo la List<> con il nuovo tipo corretto per beneficiare al meglio della messaggistica.
Grazie mille, vedrò come correggerla seguendo il tuo consiglio

PS: usando una comune List di FileViewModel e invocando OnPropertyChanged ad ogni suo cambiamento, il programma funziona normalmente
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 06-10-2010, 15:51   #13
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
RETTIFICA: L'inserimento e la rimozione, sebbene funzionino (non essendo una ObservableCollection posso invocare semplicemente Remove ed Add senza problemi), non vengono propagati alla View.

Provo a dire quello che mi sembra, correggimi se dico una cazzata: la OnpropertyChanged della List, controlla la lista come proprietà nella sua interezza, quindi scatena il suo metodo se e solo se la lista cambia nella sua interezza, cioè l'intero contenuto viene modificato, difatti il popolamento e la ricerca funzionano perchè io azzero e poi ricarico l'intera lista. Al contrario, la rimozione e l'inserimento non modificano la lista intera, ma un suo elemento interno, perciò senza la OnCollectionChanged non ho possibilità di aggiornare la lista. Sbaglio?

Ora implemento anche INotifyCollectionChanged nella mia ViewModelBase, se ho ben capito, inserendo OnCollectionChanged dopo l'inserimento o la rimozione dalla lista, dovrei vedere l'azione propagata alla UI. E' corretto il mio ragionamento?
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
Old 06-10-2010, 16:06   #14
gugoXX
Senior Member
 
L'Avatar di gugoXX
 
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
Quote:
Originariamente inviato da Donbabbeo Guarda i messaggi
RETTIFICA: L'inserimento e la rimozione, sebbene funzionino (non essendo una ObservableCollection posso invocare semplicemente Remove ed Add senza problemi), non vengono propagati alla View.

Provo a dire quello che mi sembra, correggimi se dico una cazzata: la OnpropertyChanged della List, controlla la lista come proprietà nella sua interezza, quindi scatena il suo metodo se e solo se la lista cambia nella sua interezza, cioè l'intero contenuto viene modificato, difatti il popolamento e la ricerca funzionano perchè io azzero e poi ricarico l'intera lista. Al contrario, la rimozione e l'inserimento non modificano la lista intera, ma un suo elemento interno, perciò senza la OnCollectionChanged non ho possibilità di aggiornare la lista. Sbaglio?
Rimozione o inserimento non cambiano la lista.
Sei tu che devi a mano lanciare OnPropertyChanged("NomedellaLista") ogni volta che aggiungi o rimuovi qualcosa dalla lista.
Con tutti i contro del caso sulla gui, ovvero rinfrescare completamente da zero tutto l'elenco.

Quote:

Ora implemento anche INotifyCollectionChanged nella mia ViewModelBase, se ho ben capito, inserendo OnCollectionChanged dopo l'inserimento o la rimozione dalla lista, dovrei vedere l'azione propagata alla UI. E' corretto il mio ragionamento?
No, devi costruire un nuovo oggetto, chiamato MiaLista, che implementi sia IList<> che INotifyCollectionChanged<>
E poi dovrai usarlo nel tuo viewmodel originale al posto della ObservableCollection.

Poiche' sia IList<> che INotifyCollectionChanged<> sono abbastanza lunghi, ti consiglio di usare qualcosa di gia' fatto.
Ma ti consiglio questo passo solo dopo essere riuscito ad usare la semplice List<> di cui sopra.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto.
E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test.

Ultima modifica di gugoXX : 06-10-2010 alle 16:09.
gugoXX è offline   Rispondi citando il messaggio o parte di esso
Old 06-10-2010, 16:28   #15
Donbabbeo
Senior Member
 
L'Avatar di Donbabbeo
 
Iscritto dal: Jul 2006
Messaggi: 8152
Prima di tutto: grazie per l'aiuto e scusa se ti rompo, ma voglio capire bene come funziona la cosa

Quote:
Originariamente inviato da gugoXX Guarda i messaggi
Rimozione o inserimento non cambiano la lista.
Sei tu che devi a mano lanciare OnPropertyChanged("NomedellaLista") ogni volta che aggiungi o rimuovi qualcosa dalla lista.
Con tutti i contro del caso sulla gui, ovvero rinfrescare completamente da zero tutto l'elenco.
Allora, io ho provato a sostituire la ObservableCollection con una List di files.
Quando io la popolo faccio:
Codice:
NomeLista.Clear();
NomeLista = caricaLista();
OnPropertyChanged("NomeLista");
dove caricaLista è un metodo che mi ritorna una List. Questo funziona correttamente e la lista sulla UI è di fatto aggiornata.

Quando inserisco un nuovo elemento faccio
Codice:
NomeLista.Add(new Elemento(parametri elemento));
OnPropertyChanged("NomeLista");
ma in questo caso l'aggiornamento, sebbene il nuovo elemento sia presente, non viene visualizzato nell'interfaccia. Stessa cosa ovviamente per la rimozione.

E' normale che faccia così?
__________________
"Quiquid Latine Dictum Sit, Altum Sonatur" 😐
Donbabbeo è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


Il cuore digitale di F1 a Biggin Hill: l'infrastruttura Lenovo dietro la produzione media Il cuore digitale di F1 a Biggin Hill: l'infrast...
DJI Osmo Mobile 8: lo stabilizzatore per smartphone con tracking multiplo e asta telescopica DJI Osmo Mobile 8: lo stabilizzatore per smartph...
Recensione Pura 80 Pro: HUAWEI torna a stupire con foto spettacolari e ricarica superveloce Recensione Pura 80 Pro: HUAWEI torna a stupire c...
Opera Neon: il browser AI agentico di nuova generazione Opera Neon: il browser AI agentico di nuova gene...
Wind Tre 'accende' il 5G Standalone in Italia: si apre una nuova era basata sui servizi Wind Tre 'accende' il 5G Standalone in Italia: s...
Oltre 55.000 dollari solo per il raffred...
Microsoft e tecnologia immorale: i dipen...
Come stanno andando Fastweb e Vodafone d...
Huawei presenta i nuovi eKit: soluzioni ...
Rockstar spiega i licenziamenti degli sv...
Il "concert phone" definitivo ...
Nintendo punta sul cinema: dopo Mario, a...
Insta360 X4 Air in prova: registra a 360...
PlayStation Portal: arriva lo Streaming ...
iPad Air 13'' (M2) in offerta: sconto di...
Schiaffo al copyright: utilizzare materi...
realme GT 8 Pro porta le fotocamere inte...
JBL Clip 5 a soli 39€ su Amazon: lo spea...
Apple pronta a pagare 1 miliardo l'anno ...
Apple non dimentica gli iPhone più...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 14:05.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Served by www3v