在DataTemplate中查找TextBox

时间:2014-02-05 13:17:20

标签: .net wpf datatemplate findname

在DataTemplate中有一些我需要在事件处理程序后面的代码中访问的元素 如果按钮和文本框具有相同的父级,则下面有效 我如何处理更复杂的布局? 如果有一般方法来访问DataTemplate中的元素吗?

XAML

<DataTemplate x:Key="fieldDateTemplate">
    <StackPanel>
        <DatePicker SelectedDate="{Binding Path=FieldValue}" />
        <TextBox x:Name="tbFindMe" Text="findME"/>
        <Button Content="FindTB" Click="FindTB_click" Width="60" HorizontalAlignment="Left"/>
    </StackPanel>
</DataTemplate>

C#

private void FindTB_click(object sender, RoutedEventArgs e)
{
    Button btn = (Button)sender;
    TextBox tb = ((StackPanel)btn.Parent).FindName("tbFindMe") as TextBox;
}

必须更新dkozl提供的优秀答案 如果模板中有ListBox或ListView就会失败,因为它会停在那里 这是目前正在使用的修复程序

private DataTemplate FieldTemplateDetail2(object sender, out ContentPresenter cp)
{
    cp = null;
    if (sender == null) return null;
    var d = sender as DependencyObject;
    DependencyObject dNext = null;
    DataTemplate template = null;
    while (d != null)
    {
        if (d is ContentPresenter)
        {
            Debug.WriteLine("FieldTemplateDetail2 d is ContentPresenter" + d.ToString());
            cp = d as ContentPresenter;
        }

        dNext = VisualTreeHelper.GetParent(d);
        if (dNext != null &&  dNext is ListBoxItem)
        {
            Debug.WriteLine("FieldTemplateDetail2 dNext is ListBoxItem " + d.ToString());
            if (cp != null)
            {
                Debug.WriteLine("FieldTemplateDetail2 cp != null" + cp.ToString());
                cp = cp.ContentTemplate.FindName("fieldTemplateDetail", cp) as ContentPresenter;
                if (cp != null)
                {
                    Debug.WriteLine("FieldTemplateDetail2 cp fieldTemplateDetail != null" + cp.ToString());
                    template = cp.ContentTemplate;
                    if (template == null && cp.ContentTemplateSelector != null)
                        template = cp.ContentTemplateSelector.SelectTemplate(cp.Content, cp);
                    break;
                }
                cp = null;
            }
        }
        //d = VisualTreeHelper.GetParent(d);
        d = dNext;
    }
    return template;
}

根据以下所有代码,这似乎是基于dkozl的答案的修复 将tb埋在Expandar中的网格中比上面的简单示例更难

var d = sender as DependencyObject;
ContentPresenter cp = null;
while (d != null && !(d is ListBoxItem))
{
    if (d is ContentPresenter) cp = d as ContentPresenter;
    d = VisualTreeHelper.GetParent(d);
}
if (cp != null) cp = cp.ContentTemplate.FindName("fieldTemplateDetail", cp) as ContentPresenter;
if (cp != null)
{               
    var template = cp.ContentTemplate;
    if (template == null && cp.ContentTemplateSelector != null)
        template = cp.ContentTemplateSelector.SelectTemplate(cp.Content, cp);
    if (template != null)
    {
        var tb = template.FindName("tbFindMe", cp) as TextBox;
        if (tb == null) MessageBox.Show("null", "ContentTemplateSelector");
        else  MessageBox.Show(tb.Text, "ContentTemplateSelector");
    }
}

所有代码

<Window x:Class="ListViewTemplateSelectorWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ListViewTemplateSelectorWPF"
        DataContext="{Binding RelativeSource={RelativeSource self}}"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="bvc" />
        <local:FieldTemplateSelector x:Key="fieldTemplateSelector"/>
        <DataTemplate x:Key="windowTemplate">
            <TextBox x:Name="windowTemplateTB" Text="windowTemplate" />
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0" x:Name="lbFields"
                 ItemsSource="{Binding Path=Fields}"
                 HorizontalContentAlignment="Stretch">
            <ListBox.Resources>
                <DataTemplate x:Key="fieldStringTemplate">
                    <StackPanel x:Name="fieldString" Visibility="Visible">
                        <TextBox Text="{Binding Path=FieldValue}" />
                    </StackPanel>
                </DataTemplate>
                <DataTemplate x:Key="fieldDateTemplate">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <DatePicker Grid.Row="0" SelectedDate="{Binding Path=FieldValue}" />
                        <!--<TextBox Grid.Row="1" x:Name="tbFindMe" Text="findME"/>
                        <Button Grid.Row="2" Content="FindTB" Click="FindTB_click" Width="60" HorizontalAlignment="Left"/>-->
                        <Expander Grid.Row="1" Header="Find">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="auto"/>
                                    <RowDefinition Height="Auto"/>
                                </Grid.RowDefinitions>
                                <TextBox Grid.Row="0" x:Name="tbFindMe" Text="findME"/>
                                <Button Grid.Row="1" Content="FindTB" Click="FindTB_click" Width="60" HorizontalAlignment="Left"/>
                            </Grid>
                        </Expander>
                    </Grid>
                </DataTemplate>
            </ListBox.Resources>
            <ListBox.ItemTemplate>
                <DataTemplate DataType="local:Field">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="60"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name}" />
                        <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=DisplayValue}" />                   
                        <ContentPresenter Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
                                    Visibility="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem},
                                                         Path=IsSelected, Converter={StaticResource bvc}}"
                                    x:Name="fieldTemplateDetail"
                                    Content="{Binding}"
                                    ContentTemplateSelector="{StaticResource fieldTemplateSelector}"/>                   
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Grid.Row="1" x:Name="findButton" Content="Waste Button" Width="100" HorizontalAlignment="Left" Click="click_Unselect"/>
    </Grid>
</Window>

using System.ComponentModel;
namespace ListViewTemplateSelectorWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    /// 

    public partial class MainWindow : Window
    {
        private List<Field> fields = new List<Field>();
        public MainWindow()
        {
            fields.Add(new FieldString("String1"));
            fields.Add(new FieldString("String2"));
            fields.Add(new FieldDate("Date1"));
            fields.Add(new FieldDate("Date2"));

            InitializeComponent();          
        }
        public Field CurField { get; set; }
        public List<Field> Fields { get { return fields; } }

        private void click_Unselect(object sender, RoutedEventArgs e)
        {            
            try
            {
                Button tb = this.FindName("findButton") as Button;
                if (tb == null) MessageBox.Show("null");
                else MessageBox.Show(tb.Name);
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.Message, "exception findButton");
            }
            try
            {
                DataTemplate dt = this.FindResource("fieldDateTemplate") as DataTemplate;
                if (dt == null) MessageBox.Show("dt not found");
                else MessageBox.Show("dt found");
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.Message, "exception dt");
            }

            lbFields.SelectedIndex = -1;
        }

        private void FindTB_click(object sender, RoutedEventArgs e)
        {
            var d = sender as DependencyObject;
            while (d != null && !(d is ContentPresenter)) d = VisualTreeHelper.GetParent(d);
            var cp = d as ContentPresenter;
            if (cp != null)
            {
                var template = cp.ContentTemplate;
                if (template == null && cp.ContentTemplateSelector != null)
                    template = cp.ContentTemplateSelector.SelectTemplate(cp.Content, cp);
                if (template != null)
                {
                    var tb = template.FindName("tbFindMe", cp) as TextBox;
                    MessageBox.Show(tb.Text, "ContentTemplateSelector");
                }
            }

            Button btn = (Button)sender;
            //MessageBox.Show("button name = " + btn.Name);
            try
            {
                TextBox tb = ((Grid)btn.Parent).FindName("tbFindMe") as TextBox;
                if (tb == null) MessageBox.Show("null","manual");
                else MessageBox.Show(tb.Text, "manual");
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.Message, "exception manual");
            }
        }
    }
    public abstract class Field : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        private string name;
        public string Name { get { return name; } }
        public abstract string DisplayValue { get; }
        public Field(string Name) { name = Name; }
    }
    public class FieldString : Field
    {
        private string fieldValue;
        public string FieldValue
        {
            get { return fieldValue; }
            set
            {
                if (fieldValue == value) return;
                fieldValue = value;
                NotifyPropertyChanged("FieldValue");
                NotifyPropertyChanged("DisplayValue");
            }
        }
        public override string DisplayValue
        {
            get { return FieldValue; }
        }
        public FieldString(string Name) : base(Name) { }
        public FieldString(string Name, string FieldValue) : base(Name)
        {   fieldValue = FieldValue; } 
    }
    public class FieldDate : Field
    {
        private  DateTime? fieldValue = null;
        public DateTime? FieldValue
        {
            get { return fieldValue; }
            set
            {
                if (fieldValue == value) return;
                fieldValue = value;
                NotifyPropertyChanged("FieldValue");
                NotifyPropertyChanged("DisplayValue");
            }
        }
        public override string DisplayValue
        {
            get { return FieldValue.ToString(); }
        }
        public FieldDate(string Name) 
            : base(Name) { }
        public FieldDate(string Name, DateTime FieldValue)
            : base(Name)
        { fieldValue = FieldValue; }
    }
    public class FieldTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate
            SelectTemplate(object item, DependencyObject container)
        {
            FrameworkElement element = container as FrameworkElement;
            if (item != null && item is Field)
            {
                System.Diagnostics.Debug.WriteLine("Field");
                if (item is FieldString)
                {
                    System.Diagnostics.Debug.WriteLine("FieldString");
                    return element.FindResource("fieldStringTemplate") as DataTemplate;
                }
                if (item is FieldDate)
                {
                    System.Diagnostics.Debug.WriteLine("FieldDate");
                    return element.FindResource("fieldDateTemplate") as DataTemplate;                }

                return element.FindResource("fieldTemplate") as DataTemplate;
            }
            else
                return element.FindResource("fieldTemplate") as DataTemplate;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

要在DataTemplate内按姓名查找控件,您需要找到使用此模板的ContentPresenter,并在找到ContentPresenter的该模板上致电FindName

private void Button_Click(object sender, RoutedEventArgs e)
{
   var d = sender as DependencyObject;
   ContentPresenter cp = null;
   while (d != null && !(d is ListBoxItem))
   {
       if (d is ContentPresenter) cp = d as ContentPresenter;
       d = VisualTreeHelper.GetParent(d);
   }
   if (cp != null)
   {
      cp = cp.ContentTemplate.FindName("fieldTemplateDetail", cp) as ContentPresenter;
      if (cp != null)
      {
         var template = cp.ContentTemplate;
         if (template == null && cp.ContentTemplateSelector != null)
            template = cp.ContentTemplateSelector.SelectTemplate(cp.Content, cp);
         if (template != null)
         {
            var tb = template.FindName("tbFindMe", cp) as TextBox;
         }
      }
   }
}

答案 1 :(得分:0)

这个较早的帖子是救生员。在过去的几个小时中,我一直在解决这个问题,dkozl提供了一些很好的见解。希望我的解释对其他人有所帮助。

我有一个数据网格,其中有一些列(准确地说是7列),其中5列显示项目上的静态数据,1列是文本框条目/数据显示,最后一个是布尔项目的打开和关闭指示符。

我目前正在改善使用数据网格(使用F2进行编辑,使用空格切换布尔值等)的用户体验

我下面的XAML在datagrid中只有一个带有文本框的列:

<DataGrid x:Name="my_DG" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" AutoGenerateColumns="False" KeyDown="my_DG_KeyDown">
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.Header>
                <TextBlock Text="Value" FontSize="18"/>
            </DataGridTemplateColumn.Header>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox x:Name="txtBoxValue" Text="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" KeyDown="EventTrigger_KeyDown"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellStyle>
                <Style TargetType="DataGridCell">
                    <Setter Property="IsTabStop" Value="False" />
                </Style>
            </DataGridTemplateColumn.CellStyle>
        </DataGridTemplateColumn>
    <DataGrid.Columns>
</DataGrid>

我想要的效果是,当我按F2焦点时,在数据网格上突出显示了一行,这将转到文本框,并且用户可以开始编辑该值。这是通过KeyDown =“ my_DG_KeyDown”事件完成的。该事件的代码是:

private void my_DG_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key.Equals(Key.F2))
    {
        var currRow = (sender as DataGrid).CurrentItem;
        //Columns[0] is the column the text box is in for the given row.
        var currCell_CP = (sender as DataGrid).Columns[0].GetCellContent(currRow);
        var itm = (currCell_CP as ContentPresenter).ContentTemplate.FindName("txtBoxValue", currCell_CP) as TextBox;
        itm.Focus();
    }
}

重要的收获是我能够获得给定Cell的ContentPresenter。然后从那里我可以获取模板并使用这两个项目搜索文本框名称(“ txtBoxValue”)。

我觉得这比dkozl的答案更直接,但是没有他的帮助,我不会来的