Как получить доступ к элементу управления внутри XAML DataTemplate?

у меня есть этот flipview:

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

Я хочу найти img1 текущего выбранного индекса. При поиске я нашел этот метод на каком-то посте здесь:

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

он возвращает мне изображение на первом индексе flipview, но мне нужен тот, который присутствует в текущем выбранном индексе.. Я попытался отредактировать этот метод, но я не могу найти необходимый элемент управления. Кто-нибудь может мне помочь?

6 ответов


проблема, которую вы испытываете, заключается в том, что DataTemplate повторяется, и содержимое генерируется FlipView. The Name не подвергается, потому что он будет конфликтовать с предыдущим родным братом, который был сгенерирован (или следующий, который будет).

Итак, чтобы получить именованный элемент в DataTemplate вы должны сначала получить сгенерированный элемент, а затем искать внутри этого сгенерированного элемента для элемента, который вы хотите. Помните, что логическое дерево в XAML-это способ доступа к вещам имя. Сгенерированные элементы не находятся в логическом дереве. Вместо этого они находятся в визуальном дереве (все элементы управления находятся в визуальном дереве). Это означает, что в визуальном дереве вы должны искать элемент управления, на который хотите ссылаться. The VisualTreeHelper позволяет сделать это.

теперь, как это сделать?

Я написал статью об этом, потому что это такой повторяющийся вопрос: http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html но мясо решение представляет собой рекурсивный метод, который выглядит примерно так:

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

ключевой проблемой здесь является то, что такой метод получает всех детей, а затем в результирующем списке дочерних элементов управления вы можете искать конкретный элемент управления, который вы хотите. Смысл?

а теперь, чтобы ответить на ваш вопрос!

потому что вы специально хотите выбранный элемент, вы можете просто обновить код следующим образом:

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

возможно, немного более общий подход может быть чем-то вроде этого:

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

вы вызовете FindControl следующим образом:

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;         

простое решение для получения доступа к элементам в DataTemplate-это обернуть содержимое DataTemplate в UserControl, где вы получаете доступ ко всем элементам пользовательского интерфейса в элементе ItemsControl. Я думаю, что FlipView обычно виртуализирует свои элементы, поэтому, даже если у вас есть 100 элементов, связанных с ним, только 2-3 могут фактически иметь текущее представление в пользовательском интерфейсе (1-2 из них скрыты), поэтому вы должны помнить, что когда вы хотите заменить что-либо и только на самом деле вносить изменения, когда элемент загружено в пульт управления.

Если вам действительно нужно определить контейнер элемента, который представляет элемент в ItemsSource-вы можете проверить ItemContainerGenerator собственность и ее ContainerFromItem() метод.

чтобы получить координаты элемента, вы можете использовать GetBoundingRect() метод расширения в WinRT XAML Toolkit.

в целом, однако, на основе вашего комментария может быть, что лучший подход на самом деле совершенно разный. При привязке FlipView к источнику обычно можно управлять отображаемыми или накладываемыми изображениями, изменяя свойства связанных элементов коллекции источников.


Если вы просто хотите экранные координаты щелчка... Вы можете зарегистрировать обработчик кликов на изображении, а затем использовать senderimg.transform tovisual (null) это дает вам generaltransforn, из которого вы можете получить текущие координаты точки.


Я боролся с этим весь день, и кажется, что элемент управления должен загружаться (визуализироваться), чтобы получить дочерние элементы управления из DataTemplate. Это означает, что вы не можете использовать этот код или любой код (для той же цели) при загрузке окна, инициализации... Вы можете использовать его после инициализации элемента управления, например, в SelectedItem.

Я предлагаю использовать преобразователи и определить запрошенное действие в преобразователе вместо прямого доступа управления.


поэтому, если вы назначили Источник через привязку, почему бы вам не сделать то же самое с rest: <FlipView SelectedIndex="{Binding SIndex}" SelectedItem="{Binding SItem}" SelectedValue="{Binding SValue}"/>

вы должны сначала подготовить место для этого свойства. Это может быть класс, производный от INotifyPropertyChanged.

прокат MVVM чтобы работать на вас, сделайте большинство в XAML. Конечно, в начале кажется, что это больше работы, но с более сложным проектом с MVVM будет последовательным и гибким.