2013-05-04 3 views
21

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

<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, но мне нужен один подарок на выбранный в данный момент индекс .. Я пытался изменить этот метод но я не могу найти необходимый контроль. Может кто-нибудь мне помочь?

+1

Это, как правило, неправильный подход. Что именно вы хотите сделать с этим изображением? Обратите внимание, что FlipView виртуализирует своих детей, чтобы изображение даже не существовало. –

+0

@FilipSkakun Я хочу получить координаты экрана этого изображения. Могу ли я сделать это по-другому? – Tehreem

+0

Для чего нужны координаты экрана? –

ответ

49

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

Итак, чтобы получить именованный элемент в DataTemplate, вы должны сначала получить сгенерированный элемент, а затем выполнить поиск внутри этого сгенерированного элемента для нужного элемента. Помните, что логическое дерево в XAML - это то, как вы получаете доступ к вещам по имени. Сгенерированные элементы не находятся в Логическом дереве. Вместо этого они находятся в визуальном дереве (все элементы управления находятся в визуальном дереве). Это означает, что в Visual Tree вы должны искать элемент управления, с которым хотите ссылаться. 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... 
+0

Jerry - Кажется, что это работает только если у вас есть SelectedItem. Как вы могли бы настроить таргетинг на элемент нажатием кнопки за пределами шаблона? См. Http://stackoverflow.com/questions/23645331/how-to-accessing-elements-in-xaml-datatemplate-listview-without-interacting-with – Rob

+0

Нет. Он всегда работает. –

+0

@ JerryNixon-MSFT Я пытаюсь использовать ваш код в приложении Windows Phone 8.1, но он не может найти ссылку на VisualTreeHelperClass. Но когда я ищу его в браузере объектов, я могу найти его в System.Windows.Media и Windows.UI.Xaml.Media. Также 'OfType ()' дает ошибку Systems.Generic.Connections.List не содержит определения OfType(). [структура моего FlipView] (http://pastebin.com/9ud7sCyv) – user3263192

2

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

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

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

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

+0

Я решил проблему, получив все изображения в FlipView в списке, а затем перебрал через этот список, чтобы найти требуемое изображение. Да, вы правы. Я мог получить доступ только к 3 элементам FlipView за один раз, но так как мне нужен текущий предмет, он всегда присутствует в этих трех элементах. Спасибо за вашу помощь – Tehreem

+0

@Tehreem вы могли бы предоставить решение, я столкнулся с такой же ситуацией. –

+0

Вы можете пересечь визуальное дерево с помощью 'VisualTreeHelper', но я слышал, что это может быть не самый эффективный способ получить доступ к элементам, поэтому обертка вашего DataTemplate в' UserControl' может работать лучше. Тем не менее, все работает нормально. –

0

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

9

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

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

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

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

0

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

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

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

Смежные вопросы