Создание производных классов WPF

326
09 августа 2017, 17:14

При изучение системы компоновки WPF у меня возникла куча вопросов, мне казалось что я разобрался с ними, до того момента пока не попытался сам сделать пару классов,поэкспериментировать. Вот тут я и понял, что абсолютно ничего не понимаю.

Вот маленький простой класс, я даже не могу понять почему он не работает:

    class Block : FrameworkElement
    {
        Rectangle block;
        public Brush Fill
        {
            get => block.Fill;
            set => block.Fill = value;
        }
        public Block()
        {
            block = new Rectangle();
            AddVisualChild(block);
            AddLogicalChild(block);
        }
        protected override Size MeasureOverride(Size availableSize)
        {
            block.Measure(new Size(Width,Height));
            return block.DesiredSize;
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            block.Arrange(new Rect(new Point(0,0),finalSize));
            return finalSize;
        }
    }
    class A : Window
    {
        [STAThread]
        static void Main()
        {
            new Application().Run(new A());
        }
        public A()
        {
            Block b = new Block();
            b.Fill = Brushes.Black;
            b.Height = b.Width = 10;
            b.HorizontalAlignment = HorizontalAlignment.Center;
            b.VerticalAlignment = VerticalAlignment.Center;
            Content = b;
        }
    }

DesiredSize = (0;0)

Зачем нужно два метода Measure и MeasureOverride, если Measure вызывает MeasureOverride? (аналогично для Arrange)

Вызовы Add(Logical|Visual)Child дают поддержку маршрутизации и наследования значений зависимых свойств? Связь между RenderSize и availableSize? Когда наследоваться от FrameworkElement, и когда от UIElement? Вызывается ли RenderSize при создании объекта? Зачем вызывать AddLogicalChild для элемента который не будет обрабатывать событию и влиять на "логику"? Как вообще элемент без OnRender отрисовывает дочерние элементы?

Если HorizontalAlignment и VerticalAlignment = Center, то availablesize равен нулю, значит какое значение sizedesired не верни, оно все равно урежется?

Пока не попытался все казалось таким простым и ясным... Есть какие-то общие, четкие правила когда и что нужно переопределять?

Answer 1

Вы понимаете вообще зачем вам нужно наследовать от FrameworkElement?

А нужно это для создания собственных (кастомных) элементов управления. Наследуемый класс зависит от ваших задач:

  • Наследуйте от FrameworkElement когда нужно нарисовать его содержимое "с нуля" (с использованием DrawingContext через переопределение OnRender).
  • Наследуйте от UIElement если желаете составить элемент из других контролов

Вообще, ответ на все вопросы есть тут: WPF layout: Measure и Arrange; а так же здесь: Understanding MeasureOverride and ArrangeOverride

А по повожу вашего нерабочего FrameworkElement могу сказать следующее:

  1. Гуглили плохо (Google: wpf inherit frameworkelement).
  2. Как я уже сказал, FrameworkElement рисуется "с нуля". Ну например:

     public partial class MainWindow : Window
     {
         public MainWindow()
         {
             InitializeComponent();
             this.Content = new MyFrameWorkElement();
         }
     }
    public class MyFrameWorkElement : FrameworkElement
    {
        Size _size;
        public MyFrameWorkElement()
        {
            this.SizeChanged += (o, e) =>
            {
                _size = e.NewSize;
                this.InvalidateVisual(); // cause a render
            };
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            drawingContext.DrawRectangle(
                Brushes.Blue, 
                new Pen(Brushes.Red, 2), 
                new Rect(1, 1, _size.Width / 2, _size.Height / 2));
            drawingContext.DrawEllipse(
                Brushes.Green, 
                new Pen(Brushes.Yellow, 2), 
                center: new Point(_size.Width / 2, _size.Height / 2), 
                radiusX: _size.Width / 4, 
                radiusY: _size.Height / 4);
            var txt = new FormattedText(
                "Foobar",
                System.Globalization.CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface("Courier new"),
                21,
                Brushes.Red);
            drawingContext.DrawText(txt, 
                new Point(_size.Width / 2, _size.Height / 2)
                );
        }
    }
    
  3. Для создания собственных элементов управления я вообще использую UIElement (и вам советую): UserControl.

READ ALSO
Разметка изображений в WPF C#

Разметка изображений в WPF C#

Имеется 10 изображений в окне без рамки WPF формыКак мне их разместить так, чтобы можно было из другой формы растягивать сразу все изображения...

385
c# результат быстрейшего потока

c# результат быстрейшего потока

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

268
Awesomium WebView.HTML остается пустым после указания Source

Awesomium WebView.HTML остается пустым после указания Source

С WebControl было то же самое, пока я не вставил его на формуЯ так понимаю, что он отключен, пока не находится на форме

349
C#: Вызов функции основного потока, изменяющей содержимое контролов из другого потока

C#: Вызов функции основного потока, изменяющей содержимое контролов из другого потока

andreycha почему то пометил предыдущую мою тему как дубликат, хотя решения в "ориджинал" теме для своей проблемы я не нашёл, вроде бы другая ситуация,...

354