WPF: рисование частей пользовательского элемента управления в OnApplyTemplate: как обновить? - PullRequest
0 голосов
/ 23 января 2019

Я могу успешно нарисовать часть пользовательского элемента управления в методе OnApplyTemplate элемента управления, однако этот метод вызывается только один раз и, по-видимому, не вызывается при вызове ApplyTemplate при изменении свойства элемента управления Num.

Вот код пользовательского элемента управления:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Cc1
{
    public class CirclesControl : Control
    {
        static CirclesControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CirclesControl), new FrameworkPropertyMetadata(typeof(CirclesControl)));
        }

        public int Num
        {
            get { return (int)GetValue(NumProperty); }
            set { SetValue(NumProperty, value); }
        }

        public static readonly DependencyProperty NumProperty =
            DependencyProperty.Register("Num", typeof(int), typeof(CirclesControl), new PropertyMetadata(2, new PropertyChangedCallback(OnNumChanged)));

        private static void OnNumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is CirclesControl control)
            {
                System.Diagnostics.Debug.WriteLine($"Num changed - value is now {control.Num}");
                control.ApplyTemplate();
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            var canvasPart = Template.FindName("Part_Canvas", this);
            if (canvasPart is Canvas canvas)
            {
                for (int i = 0; i < Num; i++)
                {
                    var circle = new Ellipse
                    {
                        Height = 50,
                        Width = 50,
                        Fill = Brushes.Red,
                        Stroke = Brushes.Green
                    };
                    canvas.Children.Add(circle);
                    Canvas.SetLeft(circle, i * 55);
                    Canvas.SetTop(circle, 50);
                }
            }
        }
    }
}

Я знаю, что OnNumChanged вызывается, потому что System.Diagnostics.Debug.WriteLine записывает сообщение в окно вывода. OnNumChanged также вызывает ApplyTemplate, но это не вызывает OnApplyTemplate для вызова.

Вот xaml главного окна:

<Window x:Class="Cc1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Cc1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="1024">
<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>
    <StackPanel>
        <TextBlock Text="{Binding NumberOfCircles}" FontSize="36"/>
        <local:CirclesControl Num="{Binding NumberOfCircles}"/>
    </StackPanel>
</Window>

generic.xaml для контроля:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Cc1">


    <Style TargetType="{x:Type local:CirclesControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CirclesControl}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Canvas x:Name="Part_Canvas"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

И модель представления:

using System;
using System.Windows;
using System.Windows.Threading;

namespace Cc1
{
    public class ViewModel : DependencyObject
    {
        public ViewModel()
        {
            var timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(500)
            };
            timer.Tick += (s, e) =>
            {
                NumberOfCircles++;
                if (NumberOfCircles == 10)
                {
                    NumberOfCircles = 2;
                }
            };
            timer.Start();
        }
        public int NumberOfCircles
        {
            get { return (int)GetValue(NumberOfCirclesProperty); }
            set { SetValue(NumberOfCirclesProperty, value); }
        }

        public static readonly DependencyProperty NumberOfCirclesProperty =
            DependencyProperty.Register("NumberOfCircles", typeof(int), typeof(ViewModel), new PropertyMetadata(4));
    }
}

Количество кругов (в модели вида) постоянно меняется (это то, что делает таймер диспетчера). TextBlock в главных окнах точно отражает количество кругов, однако пользовательский элемент управления CirclesControl в главном окне не обновляется.

Благодарен за любые подсказки относительно того, чего не хватает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...