web-dev-qa-db-fra.com

Comment accéder à un contrôle à l'intérieur d'un DataTemplate XAML?

J'ai cette photo:

<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>

Je veux trouver img1 de l'index actuellement sélectionné. En cherchant, j'ai trouvé cette méthode sur un post ici:

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;
    }

Il me renvoie l'image sur le premier index de la vue à feuilles mobiles, mais j'ai besoin de celle présente sur l'index actuellement sélectionné. J'ai essayé de modifier cette méthode, mais je suis incapable de trouver le contrôle requis. Quelqu'un peut-il m'aider?

22
Tehreem

Le problème que vous rencontrez est que la DataTemplate se répète et que le contenu est généré par la FlipView. La Name n'est pas exposée car elle entrerait en conflit avec le frère précédent généré (ou le prochain qui sera généré). 

Ainsi, pour obtenir un élément nommé dans la variable DataTemplate, vous devez d'abord obtenir l'élément généré, puis rechercher dans l'élément généré l'élément recherché. N'oubliez pas que l'arborescence logique en XAML permet d'accéder aux éléments par leur nom. Les éléments générés ne sont pas dans l'arborescence logique. Au lieu de cela, ils sont dans l'arborescence visuelle (tous les contrôles sont dans l'arborescence visuelle). Cela signifie que c'est dans l'arborescence visuelle que vous devez rechercher le contrôle que vous souhaitez référencer. La VisualTreeHelper vous permet de le faire.

Maintenant, comment le faire?

J'ai écrit un article à ce sujet parce que c'est une question récurrente: http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html but de la solution est une méthode récursive qui ressemble à ceci: 

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;
}

Le problème clé ici est qu’une méthode comme celle-ci récupère tous les enfants, puis dans la liste résultante des contrôles enfants, vous pouvez rechercher le contrôle spécifique souhaité. Avoir un sens?

Et maintenant, répondez à votre question!

Parce que vous voulez spécifiquement l'élément actuellement sélectionné, vous pouvez simplement mettre à jour le code comme ceci: 

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

peut-être une approche un peu plus générique pourrait-elle ressembler à ceci:

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;
}

Vous invoqueriez FindControl comme ceci:

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;         
11
user1466108

La solution simple pour obtenir l'accès aux éléments d'un DataTemplate consiste à envelopper le contenu du DataTemplate dans un UserControl, où vous aurez accès à tous les éléments d'interface utilisateur d'un élément de ItemsControl. Je pense que FlipView virtualise généralement ses éléments. Ainsi, même si vous avez 100 éléments qui y sont liés, seuls 2 ou 3 d'entre eux peuvent avoir une représentation actuelle dans l'interface utilisateur (1 ou 2 d'entre eux sont masqués). ne remplace rien et n'effectue réellement des modifications que lorsqu'un élément est chargé dans le contrôle.

Si vous avez vraiment besoin d'identifier un conteneur d'élément qui représente l'élément dans ItemsSource, vous pouvez vérifier la propriété ItemContainerGenerator de votre FlipView et sa méthode ContainerFromItem () .

Pour obtenir les coordonnées d'un élément, vous pouvez utiliser la méthode GetBoundingRect() extension dans WinRT XAML Toolkit.

Dans l’ensemble, toutefois, en fonction de votre commentaire, il se peut que la meilleure approche soit en réalité complètement différente. Si vous liez votre FlipView à une source, vous pouvez généralement contrôler les images affichées ou superposées en modifiant les propriétés des éléments de la collection source liée.

2
Filip Skakun

Je me débattais toute la journée et il semble que le contrôle doit être chargé pour obtenir ses contrôles enfants à partir du DataTemplate. Cela signifie que vous ne pouvez pas utiliser ce code ni aucun code (dans le même but) sur une fenêtre chargée, initialisée ........ Vous pouvez néanmoins l'utiliser après que le contrôle soit initialisé, par exemple sur SelectedItem.

Je suggère d'utiliser des convertisseurs et de définir l'action demandée dans le convertisseur, au lieu d'un accès à contrôle direct. 

0
autodev101

Si vous souhaitez simplement que les coordonnées de l'écran soient cliquées sur un élément ... Vous pouvez enregistrer un gestionnaire de clics sur l'image, puis utiliser senderimg.transform tovisual (null), qui vous donne un generaltransforn à partir duquel vous pouvez obtenir les coordonnées du point actuel.

0
gaurav5430

Donc, si vous avez assigné Source via une liaison, pourquoi ne feriez-vous pas la même chose avec reste: <FlipView SelectedIndex="{Binding SIndex}" SelectedItem="{Binding SItem}" SelectedValue="{Binding SValue}"/>

Vous devez d'abord préparer la place pour ces propriétés. Ce peut être une classe dérivée de INotifyPropertyChanged.

EmbaucherMVVMpour travailler pour vous, faites le plus en XAML. Bien sûr au début cela semble être plus de travail mais avec un projet plus sophistiqué avec MVVM sera cohérent et flexible.

0
Slav Siwek