Search code examples
xamldebugginguwpuwp-xamluielement

UWP Custom UI Element Render Bug


I've created a custom class for an Element that inherits from Path class (An Arrow Head drawing) in UWP now it seems to be working fine but when I try to put 2 of those elements on the screen one doesn't render. The one I initialized first is the one that is going to be rendered I though it might have something to do with the class instances referencing some pointers that are shared between them or something of that sort but I yet to have found a solution.

Black Arrow Initalized First

Blue Arrow Initalized First

Now here is the code for the custom class I wrote:

public sealed partial class TimePickerArrow : Path
{ 
    #region Arrow Direction XAML Property
    public enum Directions
    {
        Up = 0,
        Down = 180,
        Left = -90,
        Right = 90
    };

    public static readonly DependencyProperty DirectionProperty =
    DependencyProperty.Register("Direction", typeof(Directions), typeof(TimePickerArrow), new PropertyMetadata(Directions.Up, OnArrowDirectionChanged));

    public Directions Direction
    {
        get { return (Directions)GetValue(DirectionProperty); }
        set { SetValue(DirectionProperty, value); }
    }

    #endregion

    #region Arrow Hover Effect Fill Xaml Property

    public static readonly DependencyProperty HoverFillProperty =
    DependencyProperty.Register("HoverFill", typeof(Brush), typeof(TimePickerArrow), new PropertyMetadata(new SolidColorBrush(Colors.Gray)));

    public Brush HoverFill
    {
        get { return (Brush)GetValue(HoverFillProperty); }
        set { SetValue(HoverFillProperty, value); }
    }

    #endregion

    // static arrow color set by the Fill property of the path, 
    // used to save the original arrow fill color that gets changed on hover
    Brush StaticFill; 

    public TimePickerArrow()
    {
        #region PATH PROPERTIES INIT
        this.Direction = Direction;
        this.HoverFill = HoverFill;
        this.Fill = new SolidColorBrush(Colors.Black);
        this.StaticFill = Fill;
        #endregion

        InitArrowPath();

        this.PointerEntered += TimePickerArrow_HoverEntered;
        this.PointerExited += TimePickerArrow_HoverExited;
    }
    private void InitArrowPath()
    {
        this.Style = Application.Current.Resources["TimePickerArrowPath"] as Style;
    }
    private static void OnArrowDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TimePickerArrow arrow = (TimePickerArrow)d;
        Directions newArrowDirection = (Directions)e.NewValue;

        // Update the RenderTransform based on the new Direction value

        CompositeTransform arrowDirectionRotation = new CompositeTransform();
        arrowDirectionRotation.Rotation = (double)newArrowDirection;

        arrow.RenderTransform = arrowDirectionRotation;
    }
    private void TimePickerArrow_HoverEntered(object sender, PointerRoutedEventArgs e)
    {
        this.Fill = HoverFill;
    }
    private void TimePickerArrow_HoverExited(object sender, PointerRoutedEventArgs e)
    {
        this.Fill = StaticFill;
    }
}

And here is the style referenced in InitPath() method:

<Style x:Key="TimePickerArrowPath" TargetType="Path">
    <Setter Property="Data" Value="M32 6 L58 32 L48.07142857142857 41.92857142857143 L32 25.857142857142854 L16 41.92857142857143 L6 31.92857142857143 Z"/>
    <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
    <Setter Property="Stretch" Value="Fill"/>
</Style>

Thanks to anybody who chooses to contribute.


Solution

  • I can't believe I wasted so much time on this, the problem layed down in this line: this.Style = Application.Current.Resources["TimePickerArrowPath"] as Style; Since it's a static global resource for the whole Application it's shared amongst all instances and its the only thign they share, expalining only the first initalized being drawn to the screen since its the first one to "get hold" of the style resource, I just tried to change the shape and removed the style and that's where I saw and it hit me.

    I was able to solve the problem by creating a new Style each instance in the code like that

    Style arrowStyle = new Style(typeof(Path));
    arrowStyle.Setters.Add(new Setter(Path.DataProperty, "M32 6 L58 32 L48.07142857142857 41.92857142857143 L32 25.857142857142854 L16 41.92857142857143 L6 31.92857142857143 Z"));
    arrowStyle.Setters.Add(new Setter(Path.RenderTransformOriginProperty, new Point(0.5, 0.5)));
    arrowStyle.Setters.Add(new Setter(Path.StretchProperty, Stretch.Fill));
    
    this.Style = arrowStyle;
    

    Here is the result both render!!

    P.S: Funny but I needed the style only for the purpose of initalizing the Path.Data property with a string and not messing with XAML Geometry