0

So the problem is that I created a custom Control which holds 3 other controls deriving from FrameworkElement:

class TextArea : Control {
    private List<LocalViewBase> views;
    private Views.TextView.View textView;
    private Views.CaretView.View caretView;
    private Views.SelectionView.View selectionView;

    protected override int VisualChildrenCount => views.Count;

    protected override Visual GetVisualChild(int index) => views[index];

    public TextArea() {
        views = new List<LocalViewBase>();

        SetViews();
    }

    private void SetViews() {
        textView = new Views.TextView.View() { Margin = new Thickness(EditorConfiguration.GetTextAreaLeftMargin(), 0, 0, 0) };
        textInfo = new LocalTextInfo(textView);
        selectionView = new Views.SelectionView.View(textInfo) { Margin = new Thickness(EditorConfiguration.GetTextAreaLeftMargin(), 0, 0, 0) };
        caretView = new Views.CaretView.View(textInfo) { Margin = new Thickness(EditorConfiguration.GetTextAreaLeftMargin(), 0, 0, 0) };

        foreach (var view in new LocalViewBase[] { selectionView, textView, caretView }) {
            views.Add(view);

            AddVisualChild(view);
            AddLogicalChild(view);
        }
    }
}

public abstract class LocalViewBase : FrameworkElement { }

LocalViewBase is currently an empty class deriving from FrameworkElement. The problem I'm dealing with right now is that only the OnRender of the earliest added child is being called - in this case selectionView - and only it is drawn with a proper margin. I tried @devhedgehog's answer from here: WPF - Visual tree not used for drawing? but it doesn't do the trick for me. Even if I extend the Panel class instead of Control and use its Children collection instead of calls to AddVisualChild() and AddLogicalChild(), still the views aren't draw with a proper margin. I also tried every method like InvalidateWhatever() but it also didn't help.

Probably the other views are not drawn with a correct margin, because all of them are stacked on top of each other and WPF "thinks" that only the selectionView is visible, so the actual question is - how do I convince it to think otherwise? :)

Community
  • 1
  • 1
Marek M.
  • 3,799
  • 9
  • 43
  • 93
  • 1
    Aaaand why the -1 and close vote? – Marek M. Feb 24 '16 at 18:36
  • So you did try to override `VisualChildrenCount` and `GetVisualChild()`? – Clemens Feb 24 '16 at 18:49
  • Sorry, I didn't post it in my question but yes - both are overriden. I'll edit my question. – Marek M. Feb 24 '16 at 18:51
  • We should take a step back and establish what you're actually trying to accomplish here. Is there something WPF's TextBox doesn't do that you want it to do? Composing controls in C# like this is nasty. – Guttsy Feb 24 '16 at 18:53
  • 1
    Wpf's `TextBox` is not suitable for my needs. I'm writing a custom code editor control - something like AvalonEdit. I was not happy with some lacking functionalities in it, and I thought it would be fun to create something like that myself. I was not mistaken, however this margin problem is really annoying. – Marek M. Feb 24 '16 at 18:55
  • I'm not sure where all the hate comes in when trying to build custom controls in WPF. Are most developers treating it as a WYSIWYG application builder? You should be able to create, and render, completely user drawn controls to be treated as a serious application framework otherwise we might as well stick with Forms. – Michael Brown May 27 '20 at 21:01

1 Answers1

1

The solution for this problem was really simple. I had to extend StackPanel class and override the ArrangeOverride method (for each child to be put on x, y = 0, 0).

public abstract class StackablePanel : StackPanel {
    protected override Size ArrangeOverride(Size arrangeSize) {
        foreach (var child in Children) {
            var uiElement = (UIElement)child;
            var rcChild = new Rect(0, 0, Width, Height);

            uiElement.Arrange(rcChild);
        }

        return arrangeSize;
    }
}

In one of my previous approaches to this problem I extended the Panel class, but it was not enough - the default implementation for ArrangeOverride doesn't put child UIElement's as I would desire and it wasn't passing a correct Size instance to the child's Arrange method.

Now when my TextArea class extends this one, each of its child views is drawn correctly and its OnRender method is called.

Marek M.
  • 3,799
  • 9
  • 43
  • 93