Как получить доступ из выделенного кода к экземпляру Control, определенному в шаблоне ControlTemplate в Setter в стиле в UserControl? - PullRequest
0 голосов
/ 09 апреля 2019

Ранее у меня был шаблон UserControl, настроенный напрямую, а не через стиль, и все работало хорошо: доступ к корню содержимого можно получить с помощью this.Template.LoadContent () или this.Template.FindName ("MyControlName", это), оба вызова выполняются в OnApplyTemplate, после вызова base.OnApplyTemplate ().Теперь мне нужен стиль, потому что я использую два DataTriggers для отображения типа Control или другого в функции значения Binding.

После отладки с помощью XAML ниже, я вижу, что после base.OnApplyTemplate вызывается this.Template.LoadContent () возвращает Border с установленным дочерним элементом для пустого ContentPresenter.Я хочу получить элемент wpf: TimeSpanPicker.

Я прочитал этот ответ, и он не помогает мне из-за результата отладки, представленного выше.То же самое с этим ответом .

Раньше мой 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 в их имя типа.

Update

Теперь это частично работает из-за ответа, но мне нужно установить свойство зависимостей TimeSpan TimeSpanPicker, когда свойство зависимости CurrentValue UserControl изменяется, а свойство зависимости CurrentValue можно изменить, когда средство выбора промежутка времени еще не загружено.,Каков наилучший способ отложить этот параметр?Кажется, что выполнение вызова ApplyTemplate до установки не работает, поскольку переменная класса, в которой я сохраняю ссылку на TimeSpanPicker, равна нулю:

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 Ответ

1 голос
/ 09 апреля 2019

Вы не можете использовать OnApplyTemplate(), потому что нет элемента TimeSpanPicker, доступного до тех пор, пока привязка не будет разрешена и преобразователь не вернет значение "TimerData".

Вместо этого вы можете подключить обработчик событий в 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;
...