Tech Off Thread

5 posts

Problem drawing visuals and controls on canvas

Back to Forum: Tech Off
  • anaximander

    Hello,


    I am working on  a program to allow users to draw objects on a screen. It needs to do this through code, not XAML. I have a class inheriting Canvas that implements protected functions like, AddVisualChild, AddLogicalChild, RemoveVisualChild, RemoveLogicalChild. These methods can only be accessed by an inheriting class - if I use the Canvas object instead of the subclass, these methods cannot be accessed. My class will draw lines, curves and other Visuals. But I want to also add a TextBox into which the user can type directly, which I haven't been able to do. If I create an instance of TextBox and add it the children of the my subclass, the textbox does not appear. I've verified that it's there in debug mode and using Snoop. Is there a property I need to set. Setting Visibility and ZIndex do nothing.

    If i create a Canvas (not an instance of my DrawingCanvas class) in XAML and put a TextBox under it, it appears fine. But if I try to add a Visual in code to the Canvas using

    theCanvas.Children.Add(myVisual)


    it says: "cannot be converted to 'System.Windows.UIElement'. I apparently need to subclass Canvas (or Panel or UIElement) to override protected functions, like "AddVisualChild". It's a Catch 22: if I use a Canvas, I can't draw visuals, if I use my derived class, DrawingCanvas, added controls are invisible.

    If I use an actual Canvas in XAML, I can add the textbox in code using

    theCanvas.Children.Add(myTextBox)


    I am looking for either a solution that gets all the functionality of the protected methods of Canvas, or a solution that makes TextBox visible in my derived class.

  • Dr Herbie

    , anaximander wrote

    Hello,


    I am working on  a program to allow users to draw objects on a screen. It needs to do this through code, not XAML. I have a class inheriting Canvas that implements protected functions like, AddVisualChild, AddLogicalChild, RemoveVisualChild, RemoveLogicalChild. These methods can only be accessed by an inheriting class - if I use the Canvas object instead of the subclass, these methods cannot be accessed. My class will draw lines, curves and other Visuals. But I want to also add a TextBox into which the user can type directly, which I haven't been able to do. If I create an instance of TextBox and add it the children of the my subclass, the textbox does not appear. I've verified that it's there in debug mode and using Snoop. Is there a property I need to set. Setting Visibility and ZIndex do nothing.

    If i create a Canvas (not an instance of my DrawingCanvas class) in XAML and put a TextBox under it, it appears fine. But if I try to add a Visual in code to the Canvas using

    1
    theCanvas.Children.Add(myVisual)



    it says: "cannot be converted to 'System.Windows.UIElement'.

    *snip*

    The error suggests to me that 'myVisual' does not inherit from UIElement. Is myVisual a UserControl?

    I have an app that uses a standard canvas, and I allow the user to add elements (images and text) that are implemented as UserControl types (with additional code to handle touch manipulation).

     

    Herbie

  • anaximander

    No, it's not a user control. I am drawing DrawingVisual instances using DrawLine, DrawGeometry and the like. And yes, these do not inherit from UIElement.

  • figuerres

    you may have a problem with needing to get the rendering system to just draw the new item.

    I can't give you an exact example but I have run into problems in the past with trying to change things and finding that if I used a story board the idea worked.

    I am not saying to must use a story board.  but I think that there are some things that the rendering system needs to get told that you are done messing with the UI and now it needs to update that part of the visual to show the new element.

     

  • anaximander

    What I have now is just rudimentary to get everthing working. The full functionality will come later. It will be like a drawing or CAD program. (Sorry for not saying exactly what it will do.) This is the XAML and derived class. drawingSurface is my instance of my DrawingCanvas class and is standard boiler plate:

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:CanvasTester"
        Title="MainWindow" Height="350" Width="525">
        <DockPanel HorizontalAlignment="Stretch" Name="DockPanel1" VerticalAlignment="Stretch" >
            <local:DrawingCanvas  x:Name="drawingSurface" DockPanel.Dock="Bottom" Background="AliceBlue"  >
            </local:DrawingCanvas>
        </DockPanel>
    </Window>


    Public Class DrawingCanvas
        Inherits Canvas
    
        Private visuals As New List(Of Visual)()
        Private hits As New List(Of DrawingVisual)()
    
    
        Protected Overrides Function GetVisualChild(ByVal index As Integer) As Visual
            Return visuals(index)
        End Function
    
        Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
            Get
                Return visuals.Count
            End Get
        End Property
    
        Public Sub AddVisual(ByVal visual As Visual)
            visuals.Add(visual)
            MyBase.AddVisualChild(visual)
            MyBase.AddLogicalChild(visual)
        End Sub
    
        Public Sub DeleteVisual(ByVal visual As Visual)
            visuals.Remove(visual)
            MyBase.RemoveVisualChild(visual)
            MyBase.RemoveLogicalChild(visual)
        End Sub
    
        Public Function GetVisual(ByVal point As Point) As DrawingVisual
            Dim hitResult As HitTestResult = VisualTreeHelper.HitTest(Me, point)
            Return TryCast(hitResult.VisualHit, DrawingVisual)
        End Function
    
        Public Function GetVisuals(ByVal region As Geometry) As List(Of DrawingVisual)
            hits.Clear()
            Dim parameters As New GeometryHitTestParameters(region)
            Dim callback As New HitTestResultCallback(AddressOf Me.HitTestCallback)
            VisualTreeHelper.HitTest(Me, Nothing, callback, parameters)
            Return hits
        End Function
    
        Private Function HitTestCallback(ByVal result As HitTestResult) As HitTestResultBehavior
            Dim geometryResult As GeometryHitTestResult = CType(result, GeometryHitTestResult)
            Dim visual As DrawingVisual = TryCast(result.VisualHit, DrawingVisual)
            If visual IsNot Nothing AndAlso geometryResult.IntersectionDetail = IntersectionDetail.FullyInside Then
                hits.Add(visual)
            End If
            Return HitTestResultBehavior.Continue
        End Function
    End Class
    


    Here is the main code that will have the functionality:

    Imports System.Globalization
    Class MainWindow
        Public visRectangle As DrawingVisual
        Private brTomato As Brush = Brushes.Tomato
        Private pen As Pen = New Pen(Brushes.SteelBlue, 3)
    
        Public Sub New()
            InitializeComponent()
            ' Draw a sample visual.
            Using dc As DrawingContext = visRectangle.RenderOpen
                dc.DrawRectangle(brTomato, pen, New Rect(20, 20, 50, 50))
            End Using
            drawingSurface.AddVisual(visRectangle)
    
            ' Draw a textbox.
            Dim tb As TextBox = New TextBox
            Dim brush As Brush = Brushes.Black
            Dim drawingPen As Pen = New Pen(Brushes.Green, 3)
            tb.Text = "Doh!"
            tb.Name = "someBox"
            Canvas.SetTop(tb, 10)
            Canvas.SetLeft(tb, 10)
            tb.Background = System.Windows.Media.Brushes.AliceBlue
            drawingSurface.Children.Add(tb)
        End Sub
    End Class

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.