wpfxamlframeworkelement

FrameworkElement method does not draw grid when called from from XAML - but does from Window_loaded


I am rebuilding in wpf my vb6 application that is working perfectly since 2011. My app handles 11 documents. In vb6 I used 11 forms in an MDI.

In wpf I am using a Canvas that I called Hold. This canvas holds 11 instances of a FrameworkElement that I called Doc.

Doc has methods for drawing shape and text for a class that I called Cell. In order to place cells in Doc, In need Doc to draw a grid. For that I have a Boolean field (bool _showGrid;) if true Doc draws the grid.

My problem is that Doc FrameworkElement does not draw the grid when called from xaml. But from Window_Loaded it does.

this is a part of the Doc FrameworkElement:

public class Doc : FrameworkElement
{
    VisualCollection paper;
    DrawingVisual cellMaker;

    bool _showGrid;

    public Doc()
    {
        paper = new VisualCollection(this);
        //SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
    }

    public bool showGrid
    {
        set
        {
            _showGrid = value;
            if (_showGrid)
            {
                drawGrid();
            }
        }
    }

    private void drawGrid()
    {
        DrawingVisual grid = new DrawingVisual();
        using(DrawingContext dc = grid.RenderOpen())
        {
            for(int i = 0; i <= Width; i += 18)
            {
                dc.DrawLine(new Pen(Brushes.OrangeRed, 1), new Point(i, 0), new Point(i, Height));
            }
            for(int j = 0; j <= Height; j += 18)
            {
                dc.DrawLine(new Pen(Brushes.OrangeRed, 1), new Point(0, j), new Point(Width, j));
            }
            dc.Close();
        }
        paper.Add(grid);
    }

And this is xaml where documentsReceipt instance of Doc is created in showGrid is set to true witch is not working:

<ScrollViewer Grid.Row="1" Grid.Column="0">
        <Canvas Name="Hold" Width="21cm" Height="29.7cm" Background="White" Margin="17">
            <dc:Doc Name="documentsReceipt"
                    Width="{Binding Path=ActualWidth,ElementName=Hold}"
                    Height="{Binding Path=ActualHeight,ElementName=Hold}"
                    showGrid="True"
                    Loaded="documentsReceipt_Loaded">
            </dc:Doc>
            <TextBox Name="txt" 
                     TextChanged="txt_TextChanged" 
                     KeyDown="txt_KeyDown"
                     PreviewKeyDown="txt_PreviewKeyDown"/>
        </Canvas>
    </ScrollViewer>

This is the app with when I omit documentReceipt=true from Window_Loaded

private void Window_Loaded(object sender, RoutedEventArgs e)
{
   //documentsReceipt.showGrid = true;
}

Window without grid

And this is the output when I enable showGrid Window with grid


Solution

  • Usually you implement a control like this a little bit different. First of all you need a dependency property for ShowGrid to be bindable. Next is you override OnRender to draw your shapes (or what ever). Here is the full implementation of the control:

    public class Doc : FrameworkElement
    {
        public bool ShowGrid
        {
            get { return (bool)GetValue (ShowGridProperty); }
            set { SetValue (ShowGridProperty, value); }
        }
    
        public static readonly DependencyProperty ShowGridProperty =
            DependencyProperty.Register ("ShowGrid", typeof (bool), typeof (Doc), new FrameworkPropertyMetadata (false, FrameworkPropertyMetadataOptions.AffectsRender));
    
        protected override void OnRender (DrawingContext dc)
        {
            if (ShowGrid)
            {
                for (int i = 0; i <= ActualWidth; i += 18)
                {
                    dc.DrawLine (new Pen (Brushes.OrangeRed, 1), new Point (i, 0), new Point (i, Height));
                }
    
                for (int j = 0; j <= ActualHeight; j += 18)
                {
                    dc.DrawLine (new Pen (Brushes.OrangeRed, 1), new Point (0, j), new Point (Width, j));
                }
            }
        }
    }