locked
Detecting from inside a behavior when StrokeThickness of the AssociatedObject is changed at design time RRS feed

  • Question

  • How do I achieve to detect from inside a behavior, when the value of StrokeThickness of the AssociatedObject is changed at design time.

    The behavior is a Behavior(Of Microsoft.Expression.Shapes.RegularPolygon).

     

    Saturday, March 5, 2011 9:52 AM

Answers

  • I don't think that behaviors are actually run at design time. The design surface is a rendition of the page, in a given state it's not actually the running application. So this is useful in a lot of cases but not as much in others. One thing you might have more luck with is subclassing the shape rather than making it a behavior? In the case of an actual control, it will get actually instantiated and executed on the design surface and you should be able to react to changes even at design time.

    Also, I have a blog post about a PathBuilder class you might find useful. 


    -- justin
    Monday, March 7, 2011 5:02 PM

All replies

  • At design time? Do you mean runtime? What are you trying to do exactly? This is an unusual request. I think you can attach to the SizeChanged event and look to see if the stroke thickness is different when that fires.
    -- justin
    Sunday, March 6, 2011 2:24 PM
  • Hello Justin,

    thank you for your reply. I give some words to the background of my question:

    I am building a behavior for a 3 point RegularPolygon (Triangle) which is nested as child inside a square canvas. The task of the behavior is to make the Triangle into a half square, also reffered to as isosceles right triangle.

    The reason because I want to build a behavior for this task is that the mathematic formulas required to calculate the required values for a half square, that has a StrokeThicknes which is greater than 0, are a little bit unhandy for a designer. I recently published an article on my blog about how to create a half square and constributed a sample here at the Expression Gallery which provides help for the required calculations.

    Because of the fact, that a value greater than 0 or any change of the value assigned to the StrokeThickness property of a RegularPolygon affects the calculations for the half square I would like to be able to recalculate the values for Width, Height, TranslateX and TranslateY properties of the RegularPolygon, as soon as the user (designer) changes the value of the StrokeThickness, even at design time but of course also at run time.

    I already tried to listen to SizeChanged event, but unfortunately it doesn't help. Because SizeChanged event does not fire when the value assigned to StrokeThickness changes, but after the recalculation I have to do in the behavior. Only if I have already recalculated Height, Width, TranslateX and TranslateY and did assign the new values to those properties the SizeeChanged event is going to fire.

    Best regards,

    Martin

    Sunday, March 6, 2011 5:41 PM
  • I found a solution for a DependencyPropertyChangedListener, originally written in C#, translated it into VB.NET (because I use VB.NET) and implemented it to listen to changes of StrokeThickness property from inside the behavior.

    The DependencyPropertyChangedListener works fine at runtime. When the StrokeThickness property of the AssociatedObject changes, I get a notification and can make my property modification.

    But nothing happens at design time, when I change StrokeThickness property.

    I do not know why. Any ideas?

    Monday, March 7, 2011 1:41 PM
  • I don't think that behaviors are actually run at design time. The design surface is a rendition of the page, in a given state it's not actually the running application. So this is useful in a lot of cases but not as much in others. One thing you might have more luck with is subclassing the shape rather than making it a behavior? In the case of an actual control, it will get actually instantiated and executed on the design surface and you should be able to react to changes even at design time.

    Also, I have a blog post about a PathBuilder class you might find useful. 


    -- justin
    Monday, March 7, 2011 5:02 PM
  • Your last reply was very helpful. It confirms my explorations.

    I databound a dependency property of the behavior to StrokeThickness, implemented my recalculation on the notification of change of StrokeThickness and at design time nothing happened. But when I run the project the triangle is displayed as a correct calculated perfect half square.

    I will try your suggestion for subclassing but before this I will create a solution with a UserControl, maybe a  custom control.

    Once again, you were a great help to me.

    Monday, March 7, 2011 10:00 PM
  • Hello Justin,

    I have problems with building a sub class of path, following the sample provided under this link, which I converted to VB.NET. I want to use this code as a starting point for creating my own subclass in VB.

    Could you provide a simple basic sample for a sublass of Path and Shape, respectively, in VB:NET?

    Any help is very much appreciated. Here is my VB.NET conversion of the subclassing sample which was originally written in C#:

    Triangle.vb:

    Imports System
    Imports System.Net
    Imports System.Windows
    Imports System.Windows.Controls
    Imports System.Windows.Documents
    Imports System.Windows.Ink
    Imports System.Windows.Input
    Imports System.Windows.Media
    Imports System.Windows.Media.Animation
    Imports System.Windows.Shapes
    
    Namespace HalfSquareSubClass
    
     Public Class Triangle
      Inherits Path
    
      Private lastWidth As Double = 0
    
      Private lastHeight As Double = 0
    
      Private figure As PathFigure
    
      Public Sub New()
       MyBase.New()
       CreatePathData(0, 0)
      End Sub
    
      Private Sub AddPoint(ByVal x As Double, ByVal y As Double)
       Dim segment As LineSegment = New LineSegment
       segment.Point = New Point((x + (0.5 * StrokeThickness)), (y + (0.5 * StrokeThickness)))
       figure.Segments.Add(segment)
      End Sub
    
      Private Sub CreatePathData(ByVal width As Double, ByVal height As Double)
       ' in order to fit in our layout slot we need to reduce the size of the stroke
       height = (height - Me.StrokeThickness)
       width = (width - Me.StrokeThickness)
       ' needed to avoid a layout loop
       If ((lastWidth = width) _
             AndAlso (lastHeight = height)) Then
        Return
       End If
       lastWidth = width
       lastHeight = height
       Dim geometry As PathGeometry = New PathGeometry
       figure = New PathFigure
       figure.StartPoint = New Point((0 + (0.5 * StrokeThickness)), (height + (0.5 * StrokeThickness)))
       AddPoint(width, height)
       AddPoint((width / 2), 0)
       AddPoint(0, height)
       figure.IsClosed = True
       geometry.Figures.Add(figure)
       Me.Data = geometry
      End Sub
    
      Protected Overrides Function MeasureOverride(ByVal availableSize As Size) As Size
       Return availableSize
      End Function
    
      Protected Overrides Function ArrangeOverride(ByVal finalSize As Size) As Size
       CreatePathData(finalSize.Width, finalSize.Height)
       Return finalSize
      End Function
     End Class
    
    End Namespace
    

    MainPage.xaml:

    <UserControl 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:ed="http://schemas.microsoft.com/expression/2010/drawing"
           x:Class="TriangleSubClassTestApp.MainPage"
           mc:Ignorable="d"
           xmlns:silverlaw="clr-namespace:HalfSquareSubClass;assembly=HalfSquareSubClass"
           d:DesignHeight="300"
           d:DesignWidth="400">
    
     <Grid x:Name="LayoutRoot"
        Background="White">
      <Path Data="M36.5,0.5 L72.5,70.5 L0.50000012,70.5 z"
         Fill="#FFF4F4F5"
         HorizontalAlignment="Left"
         Margin="122.25,110.25,0,119.25"
         Stretch="Fill"
         Stroke="Black"
         UseLayoutRounding="False"
         Width="72.5" />
    
      <silverlaw:Triangle Width="100"
                Height="40"
                Stroke="Black"
                StrokeThickness="2"
                Fill="red" />
    
     </Grid>
    </UserControl>
    
    

    When I run the VB project from VS2010 I get the following error:

    System.Windows.Markup.XamlParseException ist aufgetreten.
      LineNumber=23
      LinePosition=31
      Message=Für Typ 'HalfSquareSubClass.Triangle' wurde kein passender Konstruktor gefunden. [Line: 23 Position: 31]
      StackTrace:
           bei System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
           bei TriangleSubClassTestApp.MainPage.InitializeComponent()
           bei TriangleSubClassTestApp.MainPage..ctor()
      InnerException: System.MissingMethodException
           Message=Es kann keine abstrakte Klasse erstellt werden.
           StackTrace:
                bei System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
                bei System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
                bei System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
                bei System.Activator.CreateInstance(Type type, Boolean nonPublic)
                bei MS.Internal.XamlManagedRuntimeRPInvokes.CreateInstance(XamlTypeToken inXamlType, XamlQualifiedObject& newObject)
           InnerException:

    I guess the main point of this error message is:

    "Für Typ 'HalfSquareSubClass.Triangle' wurde kein passender Konstruktor gefunden. [Line: 23 Position: 31]"

    In english: "For type ' HalfSquareSubClass.Triangle' no matching constructor was found. [Line: 23 position: 31]"

    What have I been doing wrong in my translation of the C# code to VB.NET?

    Regards,

    Martin

    Wednesday, March 9, 2011 10:51 AM
  • I think the problem is that you're inheriting from PathFigure, which is sealed as I look in reflector. Also Path is sealed. I think what you need to do is to inherit from Shape instead. I made a blog post with an example of how to create shapes using my PathBuilder with your cunundrum in mind:

    http://justinmchase.wordpress.com/2011/03/09/creating-shapes-with-pathbuilder/

    Hopefully that helps but the summary is to inherit from Shape and generate a Geometry object based on values of DependencyProperties and pass that in the DefiningGeometry property.

    Wednesday, March 9, 2011 7:49 PM
  • I am inheriting from Path. MSDN documentation (see under "Remarks: Deriving from Path") says that Path class is no longer sealed in Silverlight 4. So, I guess that can't be the problem. In addition, the c# source code works perfectly without any problems.

    Is it possible that the error is caused, because I have made the class in a Silverlight class library project?

     

    Thursday, March 10, 2011 8:45 AM
  • Ok Sorry my VB-fu is weak. I totally mistook the private PathFigure field as the base type declaration for some reason. But I can see now that you're inheriting from Path, which is not sealed and has a parameterless constructor.

    I don't know details about VB and if I assume that the C# equivalent works as expected then the only conclusion that I can come up with is that this is a compiler error. I am thinking though that you should try to remove the explicit call to MyBase.New() in the constructor. In C# at least, if you have a parameterless constructor it will implicitly call the base parameterless constructor, so maybe it's not needed in VB or maybe it's actually problematic? That's the best I got.

    Thursday, March 10, 2011 4:10 PM
  • Also, double check that it's a Silverlight 4 project, because it was sealed in SL3.
    Thursday, March 10, 2011 4:27 PM