it-swarm.com.ru

Как получить доступ к элементу управления внутри шаблона данных 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>

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

22
Tehreem

Проблема, с которой вы столкнулись, заключается в том, что 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...
49
Jerry Nixon - MSFT

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

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

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

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

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

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

2
Filip Skakun

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

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

0
autodev101

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

0
gaurav5430

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

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

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

0
Slav Siwek