在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;
}
}
}
答案 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的答案更直接,但是没有他的帮助,我不会来的