如何从代码隐藏中引用我的项目控件中的另一个控件?
How to reference another control in my Items Control from Code Behind?
这是我的 XAML:
<ItemsControl Margin="0 0 0 0" Name="CtrlOrderDetails" AlternationCount="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="Foobar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="0 5 0 5" FontSize="16" Text="{Binding DrugName}"></TextBlock>
<TextBox Grid.Column="2" FontSize="10" Width="100" Height="20" HorizontalContentAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Center" VerticalAlignment="Center" Tag="{Binding BarCode}"
Text="Enter Barcode.." LostFocus="BarcodeTextBox_OnLostFocus"></TextBox>
<CheckBox x:Name="IsGoodCheckBox" Grid.Column="4" IsEnabled="False" VerticalAlignment="Center"></CheckBox>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="LightGray" TargetName="Foobar"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
我想要发生的是,当用户在文本框中输入条形码时,如果它与标记中的值匹配 属性,则应将复选框设置为选中。
下面是我的代码:
private void BarcodeTextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
var tb = sender as TextBox;
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(CtrlOrderDetails);
DataTemplate yourDataTemplate = contentPresenter.ContentTemplate;
CheckBox thisCheckBox = yourDataTemplate.FindName("IsGoodCheckBox", contentPresenter) as CheckBox;
if (tb.Text == tb.Tag.ToString())
{
thisCheckBox.IsChecked = true;
}
else
{
thisCheckBox.IsChecked = false;
}
}
public TChildItem FindVisualChild<TChildItem>(DependencyObject obj) where TChildItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is TChildItem)
return (TChildItem)child;
var childOfChild = FindVisualChild<TChildItem>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}
如果我在列表的第一行尝试它,效果很好。但是,如果我在第二行尝试它,它会再次影响第一行中的复选框。
很明显,我的代码并不知道我在哪一行。我怎么能那样做?
谢谢
您的代码每次都找到相同的 CheckBox,因为您是按名称定义它的。相反,您应该按控件类型查看。
根据 another question 的回答,您可以尝试这样的操作:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
试试这个:
private void BarcodeTextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
var tb = sender as TextBox;
Panel grid = tb.Parent as Panel;
if (grid != null)
{
CheckBox checkBox = grid.Children.OfType<CheckBox>().FirstOrDefault(x => x.Name == "IsGoodCheckBox");
if (checkBox != null)
{
//set the IsChecked property based on your logic here...
checkBox.IsChecked = tb.Text == tb.Tag.ToString();
}
}
}
这将是一种更传统的 XAML 方法。没有代码隐藏,只有绑定和转换器。
<ItemsControl Margin="0 0 0 0" Name="CtrlOrderDetails" AlternationCount="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<local:EqualityMultiConverter
x:Key="EqualityConverter"
/>
</DataTemplate.Resources>
<Grid x:Name="Foobar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="0 5 0 5" FontSize="16" Text="{Binding DrugName}"></TextBlock>
<TextBox
x:Name="BarCodeTextBox"
Grid.Column="2"
FontSize="10"
Width="100"
Height="20"
HorizontalContentAlignment="Center"
HorizontalAlignment="Center"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Tag="{Binding BarCode}"
Text="Enter Barcode.."
LostFocus="BarcodeTextBox_OnLostFocus"
/>
<CheckBox
x:Name="IsGoodCheckBox"
Grid.Column="4"
IsEnabled="False"
VerticalAlignment="Center"
>
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource EqualityConverter}">
<Binding Path="BarCode" />
<Binding Path="Text" ElementName="BarCodeTextBox"/>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="LightGray" TargetName="Foobar"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
多转换器class:
public class EqualityMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// Return true if we have fewer than two values,
// or if we have at least two, and they're all equal.
return values.Length < 2 || values.Skip(1).All(v => values[0] == v);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
这是我的 XAML:
<ItemsControl Margin="0 0 0 0" Name="CtrlOrderDetails" AlternationCount="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="Foobar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="0 5 0 5" FontSize="16" Text="{Binding DrugName}"></TextBlock>
<TextBox Grid.Column="2" FontSize="10" Width="100" Height="20" HorizontalContentAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Center" VerticalAlignment="Center" Tag="{Binding BarCode}"
Text="Enter Barcode.." LostFocus="BarcodeTextBox_OnLostFocus"></TextBox>
<CheckBox x:Name="IsGoodCheckBox" Grid.Column="4" IsEnabled="False" VerticalAlignment="Center"></CheckBox>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="LightGray" TargetName="Foobar"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
我想要发生的是,当用户在文本框中输入条形码时,如果它与标记中的值匹配 属性,则应将复选框设置为选中。
下面是我的代码:
private void BarcodeTextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
var tb = sender as TextBox;
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(CtrlOrderDetails);
DataTemplate yourDataTemplate = contentPresenter.ContentTemplate;
CheckBox thisCheckBox = yourDataTemplate.FindName("IsGoodCheckBox", contentPresenter) as CheckBox;
if (tb.Text == tb.Tag.ToString())
{
thisCheckBox.IsChecked = true;
}
else
{
thisCheckBox.IsChecked = false;
}
}
public TChildItem FindVisualChild<TChildItem>(DependencyObject obj) where TChildItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is TChildItem)
return (TChildItem)child;
var childOfChild = FindVisualChild<TChildItem>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}
如果我在列表的第一行尝试它,效果很好。但是,如果我在第二行尝试它,它会再次影响第一行中的复选框。
很明显,我的代码并不知道我在哪一行。我怎么能那样做?
谢谢
您的代码每次都找到相同的 CheckBox,因为您是按名称定义它的。相反,您应该按控件类型查看。
根据 another question 的回答,您可以尝试这样的操作:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
试试这个:
private void BarcodeTextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
var tb = sender as TextBox;
Panel grid = tb.Parent as Panel;
if (grid != null)
{
CheckBox checkBox = grid.Children.OfType<CheckBox>().FirstOrDefault(x => x.Name == "IsGoodCheckBox");
if (checkBox != null)
{
//set the IsChecked property based on your logic here...
checkBox.IsChecked = tb.Text == tb.Tag.ToString();
}
}
}
这将是一种更传统的 XAML 方法。没有代码隐藏,只有绑定和转换器。
<ItemsControl Margin="0 0 0 0" Name="CtrlOrderDetails" AlternationCount="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<local:EqualityMultiConverter
x:Key="EqualityConverter"
/>
</DataTemplate.Resources>
<Grid x:Name="Foobar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="0 5 0 5" FontSize="16" Text="{Binding DrugName}"></TextBlock>
<TextBox
x:Name="BarCodeTextBox"
Grid.Column="2"
FontSize="10"
Width="100"
Height="20"
HorizontalContentAlignment="Center"
HorizontalAlignment="Center"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Tag="{Binding BarCode}"
Text="Enter Barcode.."
LostFocus="BarcodeTextBox_OnLostFocus"
/>
<CheckBox
x:Name="IsGoodCheckBox"
Grid.Column="4"
IsEnabled="False"
VerticalAlignment="Center"
>
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource EqualityConverter}">
<Binding Path="BarCode" />
<Binding Path="Text" ElementName="BarCodeTextBox"/>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="LightGray" TargetName="Foobar"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
多转换器class:
public class EqualityMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// Return true if we have fewer than two values,
// or if we have at least two, and they're all equal.
return values.Length < 2 || values.Skip(1).All(v => values[0] == v);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}