2013-05-04 16 views
21

Ho questo flipview:Come accedere a un controllo all'interno di un DataTemplate XAML?

<FlipView x:Name="models_list" SelectionChanged="selectionChanged"> 
<FlipView.ItemTemplate> 
      <DataTemplate> 
       <Grid x:Name="cv"> 
         <Image x:Name="img1" Source = "{Binding ModelImage}" Stretch="Fill" Tag="{Binding ModelTag}"/> 
       </Grid> 
      </DataTemplate> 
    </FlipView.ItemTemplate> 

voglio trovare img1 dell'indice attualmente selezionato. Durante la ricerca di essa ho trovato questo metodo su alcuni post qui:

private DependencyObject FindChildControl<T>(DependencyObject control, string ctrlName) 
    { 
     int childNumber = VisualTreeHelper.GetChildrenCount(control); 
     for (int i = 0; i < childNumber; i++) 
     { 
      DependencyObject child = VisualTreeHelper.GetChild(control, i); 
      FrameworkElement fe = child as FrameworkElement; 
      // Not a framework element or is null 
      if (fe == null) return null; 

      if (child is T && fe.Name== ctrlName) 
      { 
       // Found the control so return 
       return child; 
      } 
      else 
      { 
       // Not found it - search children 
       DependencyObject nextLevel = FindChildControl<T>(child, ctrlName); 
       if (nextLevel != null) 
        return nextLevel; 
      } 
     } 
     return null; 
    } 

Mi restituisce l'immagine sul primo indice di flipview ma ho bisogno di quello presente sull'indice selezionato .. Ho provato a modificare questo metodo ma non riesco a trovare il controllo richiesto. Qualcuno può aiutarmi?

+1

Questo di solito è l'approccio sbagliato. Che cosa vuoi specificamente fare con quell'immagine? Nota che FlipView virtualizza i suoi figli in modo che l'immagine non possa nemmeno esistere. –

+0

@FilipSkakun Voglio ottenere le coordinate dello schermo di quell'immagine. Posso farlo in altro modo? – Tehreem

+0

Per cosa ti servono le coordinate dello schermo? –

risposta

49

Il problema riscontrato è che il DataTemplate si sta ripetendo e il contenuto viene generato dallo FlipView. Il Name non è esposto perché sarebbe in conflitto con il fratello precedente che è stato generato (o il prossimo che sarà).

Quindi, per ottenere un elemento denominato nello DataTemplate è necessario prima ottenere l'elemento generato e quindi cercare all'interno di quell'elemento generato per l'elemento desiderato. Ricorda, l'albero logico in XAML è il modo in cui accedi alle cose per nome. Gli oggetti generati non si trovano nell'albero logico. Invece, sono nella struttura visiva (tutti i controlli sono nella struttura visuale). Ciò significa che è nella struttura visiva che devi cercare il controllo a cui vuoi fare riferimento. Lo VisualTreeHelper ti consente di farlo.

Ora, come si fa?

Ho scritto un articolo su questo perché è una domanda così ricorrente: http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html ma la carne della soluzione è un metodo ricorsivo che sembra qualcosa di simile:

public void TestFirstName() 
{ 
    foreach (var item in MyFlipView.Items) 
    { 
     var _Container = MyFlipView.ItemContainerGenerator 
      .ContainerFromItem(item); 
     var _Children = AllChildren(_Container); 

     var _FirstName = _Children 
      // only interested in TextBoxes 
      .OfType<TextBox>() 
      // only interested in FirstName 
      .First(x => x.Name.Equals("FirstName")); 

     // test & set color 
     _FirstName.Background = 
      (string.IsNullOrWhiteSpace(_FirstName.Text)) 
      ? new SolidColorBrush(Colors.Red) 
      : new SolidColorBrush(Colors.White); 
    } 
} 

public List<Control> AllChildren(DependencyObject parent) 
{ 
    var _List = new List<Control>(); 
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) 
    { 
     var _Child = VisualTreeHelper.GetChild(parent, i); 
     if (_Child is Control) 
      _List.Add(_Child as Control); 
     _List.AddRange(AllChildren(_Child)); 
    } 
    return _List; 
} 

La questione chiave qui è che un il metodo come questo ottiene tutti i bambini, quindi nell'elenco risultante dei controlli figlio è possibile cercare il controllo specifico desiderato. Ha senso?

E ora per rispondere alla tua domanda!

Poiché si desidera specificamente l'elemento selezionato, è possibile semplicemente aggiornare il codice come questo:

if (MyFlipView.SelectedItem == null) 
    return; 
var _Container = MyFlipView.ItemContainerGenerator 
    .ContainerFromItem(MyFlipView.SelectedItem); 
// then the same as above... 
+0

Jerry - Sembra che funzioni solo se hai un oggetto selezionato. Come sceglieresti un elemento attraverso un clic del pulsante al di fuori dei limiti dell'elemento basato su modello? Vedi http://stackoverflow.com/questions/23645331/how-to-accessing-elements-in-xaml-datatemplate-listview-without-interacting-with – Rob

+0

No. Funziona sempre. –

+0

@ JerryNixon-MSFT Cerco di utilizzare il codice in un'applicazione Windows Phone 8.1, ma non riesce a trovare un riferimento a VisualTreeHelperClass. Ma quando lo cerco nel browser degli oggetti posso trovarlo in System.Windows.Media e Windows.UI.Xaml.Media. Anche 'OfType ()' dà errore Systems.Generic.Connections.List non contiene la definizione di OfType(). La [struttura del mio FlipView] (http://pastebin.com/9ud7sCyv) – user3263192

2

La soluzione semplice per ottenere l'accesso agli elementi all'interno di un DataTemplate consiste nel racchiudere il contenuto di DataTemplate in un controllo utente, in cui si ottiene l'accesso a tutti gli elementi dell'interfaccia utente in un oggetto ItemsControl. Penso che FlipView di solito virtualizzi i suoi oggetti così anche se hai 100 elementi legati ad esso - solo 2-3 potrebbero effettivamente avere una rappresentazione corrente nell'interfaccia utente (1-2 di essi nascosti), quindi devi ricordare che quando vuoi sostituire qualsiasi cosa e apportare solo modifiche quando un oggetto viene caricato nel controllo.

Se è davvero necessario identificare un contenitore di articoli che rappresenta l'elemento in ItemsSource, è possibile controllare la proprietà ItemContainerGenerator di FlipView e il relativo metodo ContainerFromItem().

Per ottenere le coordinate di un articolo è possibile utilizzare il metodo di estensione GetBoundingRect() in WinRT XAML Toolkit.

Nel complesso tuttavia, in base al tuo commento potrebbe essere che l'approccio migliore è in realtà completamente diverso. Se si associa il proprio FlipView a una fonte, di solito è possibile controllare le immagini visualizzate o sovrapposte modificando le proprietà degli elementi della raccolta di origine associati.

+0

Ho risolto il problema ottenendo tutte le immagini in FlipView in un elenco e quindi ho iterato attraverso quell'elenco per trovare la mia immagine richiesta. Sì, hai ragione, ho potuto accedere solo a 3 elementi del FlipView alla volta, ma poiché avevo bisogno di elementi attualmente focalizzati, è sempre presente in questi tre elementi. Grazie per il tuo aiuto – Tehreem

+0

@Tehreem potresti fornire la soluzione che sto affrontando la stessa situazione. –

+0

È possibile attraversare la struttura visuale usando 'VisualTreeHelper', ma ho sentito che potrebbe non essere il modo più performante per ottenere l'accesso agli elementi, quindi avvolgere il' DataTemplate' in un 'UserControl' potrebbe funzionare meglio. Qualunque cosa funzioni va bene comunque. –

0

Se si desidera solo le coordinate dello schermo sulla voce click ... È possibile registrare un gestore di clic su l'immagine e quindi utilizzare senderimg.transform tovisual (null) questo ti dà un generale trascurato da cui puoi ottenere le coordinate del punto corrente.

9

forse un po 'approccio più generico potrebbe essere qualcosa di simile:

private List<Control> AllChildren(DependencyObject parent) 
{ 
    var _List = new List<Control>(); 
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) 
    { 
     var _Child = VisualTreeHelper.GetChild(parent, i); 
     if (_Child is Control) 
     { 
     _List.Add(_Child as Control); 
     } 
     _List.AddRange(AllChildren(_Child)); 
    } 
    return _List; 
} 


private T FindControl<T> (DependencyObject parentContainer, string controlName) 
{    
     var childControls = AllChildren(parentContainer); 
     var control = childControls.OfType<Control>().Where(x => x.Name.Equals(controlName)).Cast<T>().First();   
     return control; 
} 

Si potrebbe richiamare FindControl in questo modo:

var parentContainer = this.flipView.ItemContainerGenerator.ContainerFromItem(this.flipView.SelectedItem); 
var myImage = FindControl<Image>(parentContainer, "img1"); 

//suppose you want to change the visibility 
myImage.Visibility = Windows.UI.Xaml.Visibility.Collapsed;   
0

Stavo lottando Pentecoste tutta questa giornata, e sembra che il il controllo deve essere caricato (renderizzato) per ottenere i suoi controlli figlio dal DataTemplate. Ciò significa che non puoi usare questo codice o qualsiasi codice (per lo stesso scopo) su finestra caricata, inizializzata ... Puoi sempre usarlo dopo l'inizializzazione del controllo, ad esempio su SelectedItem.

Suggerisco di utilizzare i convertitori e definire l'azione richiesta nel convertitore, invece dell'accesso diretto al controllo.

0

Quindi, se avete assegnato Fonte via vincolante perché non dovrebbe fare lo stesso con il riposo: <FlipView SelectedIndex="{Binding SIndex}" SelectedItem="{Binding SItem}" SelectedValue="{Binding SValue}"/>

È necessario innanzitutto preparare posto per gli annunci di questo. Potrebbe trattarsi di una classe derivata da INotifyPropertyChanged.

Noleggio MVVM per lavorare per Te, fare di più in XAML. Ovviamente all'inizio sembra che ci sia più lavoro, ma con un progetto più sofisticato con MVVM sarà coerente e flessibile.

Problemi correlati