如何从代码的后面访问User样式中的Setter的ControlTemplate中定义的Control实例?

时间:2019-04-09 13:12:35

标签: c# wpf user-controls controltemplate

以前,我直接设置了UserControl的模板,而不是通过样式设置,并且一切运行良好:可以使用this.Template.LoadContent()或通过this.Template.FindName(“ MyControlName”访问内容的根“,this),两个调用都在base.OnApplyTemplate()调用之后在OnApplyTemplate中完成。现在,我需要一种样式,因为我使用两个DataTriggers来显示一种Control类型或一种在Binding值函数中的其他类型。

通过下面的XAML调试,我看到在base.OnApplyTemplate调用this.Template.LoadContent()之后,返回一个Border,其Child设置为空的ContentPresenter。我希望获得wpf:TimeSpanPicker元素。

我已阅读this的答案,由于上述调试的结果,它对我没有帮助。与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类的实例之一转换为其类型名称。

更新

现在由于答案而部分起作用,但是当UserControl的CurrentValue依赖项属性更改时,我需要设置TimeSpanPicker的TimeSpan依赖项属性,而当时间跨度选择器尚未使用时,可以更改CurrentValue依赖项属性已加载。推迟此设置的最佳方法是什么?在设置之前放置ApplyTemplate调用似乎无效,因为我在其中保留对TimeSpanPicker的引用的类变量为null:

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

1 个答案:

答案 0 :(得分:1)

您无法使用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;
}

编辑:如果您想在TimeSpanPicker属性更改时对CurrentValue进行操作,则可以使用VisualTreeHelper类在视觉树:

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;