C# 如何访问 XAML DataTemplate 中的控件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16375375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:53:00  来源:igfitidea点击:

How do I access a control inside a XAML DataTemplate?

c#windows-8windows-store-appswinrt-xamlflipview

提问by Tehreem

I have this 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>

I want to find img1 of currently selected index. While searching for it I found this method on some post here:

我想找到当前选定索引的 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;
    }

It returns me the Image on the first index of flipview but I need the one present on the currently selected index.. I tried to edit this method but I am unable to find the required control. Can anyone help me?

它返回我翻转视图第一个索引上的图像,但我需要当前选定索引上的图像。我尝试编辑此方法,但无法找到所需的控件。谁能帮我?

采纳答案by Jerry Nixon

The problem you are experiencing is that the DataTemplateis repeating and the content is being generated by the FlipView. The Nameis not exposed because it would conflict with the previous sibling that was generated (or the next one that will be).

您遇到的问题是DataTemplate正在重复并且内容是由FlipView. 在Name不被暴露,因为它将与已生成的上一个兄弟(或下一个将要)发生冲突。

So, to get a named element in the DataTemplateyou have to first get the generated item, and then search inside that generated item for the element you want. Remember, the Logical Tree in XAML is how you access things by name. Generated items are not in the Logical Tree. Instead, they are in the Visual Tree (all controls are in the Visual Tree). That means it is in the Visual Tree you must search for the control you want to reference. The VisualTreeHelperlets you do this.

因此,要在 中获取命名元素,DataTemplate您必须首先获取生成的项目,然后在生成的项目中搜索您想要的元素。请记住,XAML 中的逻辑树是您按名称访问事物的方式。生成的项目不在逻辑树中。相反,它们在可视化树中(所有控件都在可视化树中)。这意味着您必须在可视化树中搜索要引用的控件。将VisualTreeHelper让你做到这一点。

Now, how to do it?

现在,该怎么做?

I wrote an article on this because it is such a recurring question: http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.htmlbut the meat of the solution is a recursive method that looks something like this:

我写了一篇关于此的文章,因为这是一个反复出现的问题: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;
}

The key issue here is that a method like this gets all the children, and then in the resulting list of child controls you can search for the specific control you want. Make sense?

这里的关键问题是,像这样的方法获取所有子控件,然后在生成的子控件列表中,您可以搜索所需的特定控件。有道理?

And now to answer your question!

现在回答你的问题!

Because you specifically want the currently selected item, you can simply update the code like this:

因为您特别想要当前选定的项目,所以您可以简单地更新代码,如下所示:

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

回答by Filip Skakun

The simple solution to getting access to elements within a DataTemplate is to wrap the contents of the DataTemplate in a UserControl, where you get access to all UI elements in an ItemsControl's item. I think FlipView usually virtualizes its items so even if you have 100 items bound to it - only 2-3 might actually have a current representation in the UI (1-2 of them hidden), so you have to remember that when you want to replace anything and only actually make changes when an item is loaded into the control.

访问 DataTemplate 中元素的简单解决方案是将 DataTemplate 的内容包装在 UserControl 中,您可以在其中访问 ItemsControl 项中的所有 UI 元素。我认为 FlipView 通常会虚拟化它的项目,所以即使你绑定了 100 个项目——实际上只有 2-3 个可能在 UI 中有当前的表示(其中 1-2 个隐藏),所以你必须记住,当你想要替换任何东西,只有在项目加载到控件中时才真正进行更改。

If you really need to identify an item container that represents the item in ItemsSource - you can check your FlipView's ItemContainerGeneratorproperty and its ContainerFromItem()method.

如果您确实需要在 ItemsSource 中识别代表该项目的项目容器 - 您可以检查 FlipView 的ItemContainerGenerator属性及其ContainerFromItem()方法。

To get coordinates of an item you can use the GetBoundingRect()extension method in WinRT XAML Toolkit.

要获取项目的坐标,您可以使用GetBoundingRect()WinRT XAML 工具包中的扩展方法。

Overall however, based on your comment it might be that the best approach is actually completely different. If you are binding your FlipView to a source - you can usually control images displayed or overlaid by changing the properties of the bound source collection items.

然而,总的来说,根据您的评论,最好的方法实际上可能完全不同。如果您将 FlipView 绑定到源 - 您通常可以通过更改绑定源集合项的属性来控制显示或覆盖的图像。

回答by gaurav5430

If you just want the screen coordinates on item click... You can register a click handler on the image and then use senderimg.transform tovisual(null) this gives you a generaltransforn from which youcan get the current point coordinates.

如果您只想要点击项目时的屏幕坐标...您可以在图像上注册一个点击处理程序,然后使用 senderimg.transform tovisual(null) 这为您提供了一个通用转换,您可以从中获取当前点坐标。

回答by user1466108

maybe a little more generic approach might be something like this:

也许更通用的方法可能是这样的:

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

You would invoke FindControl like this:

您可以像这样调用 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;         

回答by autodev101

I was struggling whit this whole day, and it seems that the control has to loaded (rendered) in order to get it's child controls from the DataTemplate. This means that you cant use this code or any code (for the same purpose) on window loaded, initialized... You can how ever use it after the control is initialized for example on SelectedItem.

我一整天都在挣扎,似乎必须加载(渲染)控件才能从 DataTemplate 中获取它的子控件。这意味着您不能在加载、初始化的窗口上使用此代码或任何代码(出于相同目的)...您可以在控件初始化后使用它,例如在 SelectedItem 上。

I suggest using converters and define the requested action in the converter, instead of direct control access.

我建议使用转换器并在转换器中定义请求的操作,而不是直接控制访问。

回答by Slav Siwek

So if You assigned Source via binding why wouldn't you do the same with rest: <FlipView SelectedIndex="{Binding SIndex}" SelectedItem="{Binding SItem}" SelectedValue="{Binding SValue}"/>

因此,如果您通过绑定分配了 Source,那么您为什么不对 rest 做同样的事情: <FlipView SelectedIndex="{Binding SIndex}" SelectedItem="{Binding SItem}" SelectedValue="{Binding SValue}"/>

You must first prepare place for this properties. It may be a class derived from INotifyPropertyChanged.

你必须先为这个属性准备好位置。它可能是一个派生自INotifyPropertyChanged.

Hire MVVMto work for You, do most in XAML. Of course at the begining it seems to be more work but with more sophisticated project with MVVM will be coherent and flexible.

雇用MVVM为您工作,在 XAML 中完成大部分工作。当然,一开始它似乎需要更多的工作,但是使用 MVVM 的更复杂的项目将变得连贯和灵活。