如何从代码隐藏访问 UserControl 中 Setter 中 ControlTemplate 中定义的 Control 实例?
How to access from code-behind an instance of a Control defined in a ControlTemplate in a Setter in a Style in an UserControl?
以前我直接设置了 UserControl 的模板,而不是通过样式,并且一切正常:可以使用 this.Template.LoadContent() 或通过 this.Template.FindName( 访问内容的根目录"MyControlName", this),这两个调用都在 base.OnApplyTemplate() 调用之后在 OnApplyTemplate 中完成。现在我需要一种样式,因为我使用两个 DataTriggers 来显示一种类型的 Control 或另一种 Binding 值的函数。
通过使用下面的 XAML 进行调试,我看到在 base.OnApplyTemplate 调用 this.Template.LoadContent() returns 之后,一个 Border 的 Child 设置为一个空的 ContentPresenter。我希望获得 wpf:TimeSpanPicker 元素。
我已阅读this answer and it does not help me because of the result of debugging presented above. The same with this answer。
之前,我的 UserControl 直接在里面(在它的 XAML 文件中):
<UserControl.Template>
<ControlTemplate>
<wpf:TimeSpanPicker
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Name="MyTimeSpanPicker"
Margin="0,0,7,0"/>
</ControlTemplate>
</UserControl.Template>
现在我有了这个:
<UserControl.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Mode=OneWay, Converter={StaticResource ClockToType}}"
Value="TimerData">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="UserControl">
<wpf:TimeSpanPicker
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Name="MyTimeSpanPicker"
Margin="0,0,7,0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Mode=OneWay, Converter={StaticResource ClockToType}}"
Value="AlarmData">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="UserControl">
<Button>Not yet implemented</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
隐藏代码包括:
internal wpf_timespanpicker.TimeSpanPicker GetMyTimeSpanPicker()
{
return (wpf_timespanpicker.TimeSpanPicker)Template.LoadContent();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
GetMyTimeSpanPicker().TimeSpanValueChanged += MyTimeSpanPicker_TimeSpanValueChanged;
}
private void MyTimeSpanPicker_TimeSpanValueChanged(object sender, EventArgs e)
{
CurrentValue = GetMyTimeSpanPicker().TimeSpan;
}
ClockToType 值转换器只是将我的 Clock classes 实例之一转换为其类型名称。
更新
现在它部分工作因为答案但是我需要设置 TimeSpanPicker 的 TimeSpan 依赖性 属性 当 UserControl 的 CurrentValue 依赖性 属性 改变时,并且 CurrentValue 依赖性 属性 可以在时间跨度选择器尚未加载时更改。推迟此设置的最佳方法是什么?在设置之前放置 ApplyTemplate 调用似乎不起作用,因为我保留对 TimeSpanPicker 的引用的 class 变量为空:
private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var o = d as ClockValueScreen;
o.ApplyTemplate();
o.MyTimeSpanPicker.TimeSpan = (TimeSpan)e.NewValue; // but here o.MyTimeSpanPicker is null
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate(); // execution reaches this point from the ApplyTemplate call above
}
private void MyTimeSpanPicker_Loaded(object sender, RoutedEventArgs e)
{
MyTimeSpanPicker = (wpf_timespanpicker.TimeSpanPicker)sender;
MyTimeSpanPicker.TimeSpanValueChanged += MyTimeSpanPicker_TimeSpanValueChanged;
}
您不能使用 OnApplyTemplate()
,因为在解析绑定并且转换器返回值 "TimerData" 之前没有可用的 TimeSpanPicker
元素。
您可以改为在 XAML:
中连接事件处理程序
<ControlTemplate TargetType="UserControl">
<wpf:TimeSpanPicker
Loaded="OnLoaded"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Name="MyTimeSpanPicker"
Margin="0,0,7,0"/>
</ControlTemplate>
...然后在您的代码隐藏中处理它;
private void OnLoaded(object sender, RoutedEventArgs e)
{
wpf_timespanpicker.TimeSpanPicker timeSpanPicker = ( wpf_timespanpicker.TimeSpanPicker)sender;
}
编辑: 如果您想在 CurrentValue
属性 更改时对 TimeSpanPicker
做一些事情,您可以使用 VisualTreeHelper
class 在可视化树中找到它:
private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var o = (ClockValueScreen)d;
var timeSpanPicker = FindVisualChild<wpf_timespanpicker.TimeSpanPicker>(o);
//...
}
private static T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
T descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
}
return null;
以前我直接设置了 UserControl 的模板,而不是通过样式,并且一切正常:可以使用 this.Template.LoadContent() 或通过 this.Template.FindName( 访问内容的根目录"MyControlName", this),这两个调用都在 base.OnApplyTemplate() 调用之后在 OnApplyTemplate 中完成。现在我需要一种样式,因为我使用两个 DataTriggers 来显示一种类型的 Control 或另一种 Binding 值的函数。
通过使用下面的 XAML 进行调试,我看到在 base.OnApplyTemplate 调用 this.Template.LoadContent() returns 之后,一个 Border 的 Child 设置为一个空的 ContentPresenter。我希望获得 wpf:TimeSpanPicker 元素。
我已阅读this answer and it does not help me because of the result of debugging presented above. The same with this answer。
之前,我的 UserControl 直接在里面(在它的 XAML 文件中):
<UserControl.Template>
<ControlTemplate>
<wpf:TimeSpanPicker
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Name="MyTimeSpanPicker"
Margin="0,0,7,0"/>
</ControlTemplate>
</UserControl.Template>
现在我有了这个:
<UserControl.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Mode=OneWay, Converter={StaticResource ClockToType}}"
Value="TimerData">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="UserControl">
<wpf:TimeSpanPicker
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Name="MyTimeSpanPicker"
Margin="0,0,7,0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Mode=OneWay, Converter={StaticResource ClockToType}}"
Value="AlarmData">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="UserControl">
<Button>Not yet implemented</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
隐藏代码包括:
internal wpf_timespanpicker.TimeSpanPicker GetMyTimeSpanPicker()
{
return (wpf_timespanpicker.TimeSpanPicker)Template.LoadContent();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
GetMyTimeSpanPicker().TimeSpanValueChanged += MyTimeSpanPicker_TimeSpanValueChanged;
}
private void MyTimeSpanPicker_TimeSpanValueChanged(object sender, EventArgs e)
{
CurrentValue = GetMyTimeSpanPicker().TimeSpan;
}
ClockToType 值转换器只是将我的 Clock classes 实例之一转换为其类型名称。
更新
现在它部分工作因为答案但是我需要设置 TimeSpanPicker 的 TimeSpan 依赖性 属性 当 UserControl 的 CurrentValue 依赖性 属性 改变时,并且 CurrentValue 依赖性 属性 可以在时间跨度选择器尚未加载时更改。推迟此设置的最佳方法是什么?在设置之前放置 ApplyTemplate 调用似乎不起作用,因为我保留对 TimeSpanPicker 的引用的 class 变量为空:
private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var o = d as ClockValueScreen;
o.ApplyTemplate();
o.MyTimeSpanPicker.TimeSpan = (TimeSpan)e.NewValue; // but here o.MyTimeSpanPicker is null
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate(); // execution reaches this point from the ApplyTemplate call above
}
private void MyTimeSpanPicker_Loaded(object sender, RoutedEventArgs e)
{
MyTimeSpanPicker = (wpf_timespanpicker.TimeSpanPicker)sender;
MyTimeSpanPicker.TimeSpanValueChanged += MyTimeSpanPicker_TimeSpanValueChanged;
}
您不能使用 OnApplyTemplate()
,因为在解析绑定并且转换器返回值 "TimerData" 之前没有可用的 TimeSpanPicker
元素。
您可以改为在 XAML:
中连接事件处理程序<ControlTemplate TargetType="UserControl">
<wpf:TimeSpanPicker
Loaded="OnLoaded"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Name="MyTimeSpanPicker"
Margin="0,0,7,0"/>
</ControlTemplate>
...然后在您的代码隐藏中处理它;
private void OnLoaded(object sender, RoutedEventArgs e)
{
wpf_timespanpicker.TimeSpanPicker timeSpanPicker = ( wpf_timespanpicker.TimeSpanPicker)sender;
}
编辑: 如果您想在 CurrentValue
属性 更改时对 TimeSpanPicker
做一些事情,您可以使用 VisualTreeHelper
class 在可视化树中找到它:
private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var o = (ClockValueScreen)d;
var timeSpanPicker = FindVisualChild<wpf_timespanpicker.TimeSpanPicker>(o);
//...
}
private static T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
T descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
}
return null;