ExtZGholson
Topic Author
Posts: 22
Joined: 12 Mar 2021, 01:24

Passing Rectangle via converter

28 Aug 2021, 01:26

Hello,

I am currently trying to pass a Rectangle in to a convert so that I can return a Rect for a Viewbox. Unfortuantely, I am missing something simple when trying to either cast or Unbox the BaseComponent*.

When attempting to unbox the Rectangle I get the follow error:
'Project.UI.Converters.ElementPositionSizeConverter' binding converter failed to convert value 'Rectangle' (type 'Rectangle')'Project.UI.Converters.ElementPositionSizeConverter' binding converter failed to convert value 'Rectangle' (type 'Rectangle')
What may I be doing wrong here?

Here is the code in the converter:
bool ElementPositionSizeConverter::TryConvert(Noesis::BaseComponent* value, const Noesis::Type* /*targetType*/, Noesis::BaseComponent* /*parameter*/, Noesis::Ptr<Noesis::BaseComponent>& result)
  {
    if (Noesis::Boxing::CanUnbox<Noesis::Rectangle*>(value))
    {
        const Noesis::Rectangle* myRectangle = Noesis::Boxing::Unbox<Noesis::Rectangle*>(value);
        if (myRectangle)
        {
            Noesis::Size contentSize = Noesis::Size(myRectangle->GetActualWidth(), myRectangle->GetActualHeight());
            const Noesis::Point contentPosition = (myRectangle->PointToScreen(Noesis::Point(0, 0)));
            result.Reset(Noesis::Boxing::Box(Noesis::Rect(contentPosition, contentSize)));
            return true;
        }
    }
      
    return false;
  }
 
User avatar
sfernandez
Site Admin
Posts: 2984
Joined: 22 Dec 2011, 19:20

Re: Passing Rectangle via converter

30 Aug 2021, 11:59

Hi, if I understand correctly you are binding to a Rectangle element, something like this:
<Rectangle x:Name="rect" Width="200" Heigth="100">
  <Rectangle.Fill>
    <ImageBrush Viewbox="{Binding ElementName=rect, Converter={StaticResource ElementPositionSizeConverter}}"/>
  </Rectangle.Fill>
</Rectangle>
In that case the value received in the converter will be the reference to the Rectangle element, so you can cast like this:
bool ElementPositionSizeConverter::TryConvert(Noesis::BaseComponent* value, const Noesis::Type* /*targetType*/, Noesis::BaseComponent* /*parameter*/, Noesis::Ptr<Noesis::BaseComponent>& result)
{
  Rectangle* rect = DynamicCast<Rectangle*>(value);
  if (rect != nullptr)
  {
    //...
  }
}
Only basic types (bool, int, float, ...) and non-ref counted classes (String, Size, Point, Rect, Thickness....) are boxed and should use Unbox to obtain the value. For reference count objects you should use casting, static or dynamic, depending if you know the type or no.

Have you already tried that?
 
ExtZGholson
Topic Author
Posts: 22
Joined: 12 Mar 2021, 01:24

Re: Passing Rectangle via converter

30 Aug 2021, 16:59

I haven't. I tried something close but we thinking it would be a Noesis Rectangle. I will give this a try!

Thank you very much!
 
ExtZGholson
Topic Author
Posts: 22
Joined: 12 Mar 2021, 01:24

Re: Passing Rectangle via converter

30 Aug 2021, 17:46

When using a Noesis::Rectangle* with the dynamic cast, we get the following Errors:
ERROR| [Noesis] Visual is not connected to a ViewVisual is not connected to a View
000000.621|  ERROR| [Noesis] Visual is not connected to a ViewVisual is not connected to a View
000000.622|  ERROR| [Noesis] Visual is not connected to a ViewVisual is not connected to a View
000000.625|  ERROR| [Noesis] Visual is not connected to a ViewVisual is not connected to a View
000015.485|  ERROR| Run failed: Error emitted [Noesis] Visual is not connected to a View
The properties of the rectangle don't seem to be initialized or assigned.
 
User avatar
sfernandez
Site Admin
Posts: 2984
Joined: 22 Dec 2011, 19:20

Re: Passing Rectangle via converter

30 Aug 2021, 18:01

Those error messages are raised when calling PointToScreen() on the Rectangle if it is not part of the UI tree (not connected to the View).
You can use rectangle->IsConnectedToView() before calling PointToScreen() to avoid them:
bool ElementPositionSizeConverter::TryConvert(Noesis::BaseComponent* value, const Noesis::Type* /*targetType*/, Noesis::BaseComponent* /*parameter*/, Noesis::Ptr<Noesis::BaseComponent>& result)
{
    Noesis::Rectangle* myRectangle = Noesis::DynamicCast<Noesis::Rectangle*>(value);
    if (myRectangle && myRectangle->IsConnectedToView())
    {
        Noesis::Size contentSize = Noesis::Size(myRectangle->GetActualWidth(), myRectangle->GetActualHeight());
        const Noesis::Point contentPosition = (myRectangle->PointToScreen(Noesis::Point(0, 0)));
        result.Reset(Noesis::Boxing::Box(Noesis::Rect(contentPosition, contentSize)));
        return true;
    }
    return false;
}
 
ExtZGholson
Topic Author
Posts: 22
Joined: 12 Mar 2021, 01:24

Re: Passing Rectangle via converter

30 Aug 2021, 18:32

Thank you this is certainly helpful.

I know this sounds like a silly question, but how would I go about connecting this Rectangle to the view?

The structure exists as the following.

Main Window -> Viewbox -> Noesis::ContentControl (loads User Control XAML via set source NsPRop) -> Grid -> Rectangle -> Button -> Grid -> Rectangle(Target Rectangle) -> Visual Brush (Contains Converter)
 
User avatar
sfernandez
Site Admin
Posts: 2984
Joined: 22 Dec 2011, 19:20

Re: Passing Rectangle via converter

30 Aug 2021, 19:44

Elements are connected to the View when they raise the Loaded event. Instead of binding directly to the rectangle, you can bind to a property that gets updated with the Rectangle when it gets loaded:
<Grid x:Name="target">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
      <ei:ChangePropertyAction PropertyName="Tag" Value="{Binding ElementName=rect}"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
  <Rectangle x:Name="rect" Width="200" Height="100">
    <Rectangle.Fill>
      <ImageBrush Viewbox="{Binding Tag, ElementName=target, Converter={StaticResource ElementPositionSizeConverter}}"/>
    </Rectangle.Fill>
  </Rectangle>
</Grid>
 
ExtZGholson
Topic Author
Posts: 22
Joined: 12 Mar 2021, 01:24

Re: Passing Rectangle via converter

30 Aug 2021, 22:53

Thank you, I am now able to read the properties on the Rectangle for my converter! I just need to tweak some values on my end and I am all clear. I appreciate your time and assistance.
 
ExtZGholson
Topic Author
Posts: 22
Joined: 12 Mar 2021, 01:24

Re: Passing Rectangle via converter

31 Aug 2021, 21:09

As we are reading the values for the rectangles that we passed in, we noticed that we are getting inconsistent values when calling `contentPosition = myRectangle->PointToScreen(Noesis::Point(0, 0));`. The values we are expecting in one case are (93,216). However, one one machine we are getting (85.308, 136.199), while the other machine is closer but not accurate.

Is there some other way of determining the position for an element on screen?

Thank you,
Zane
 
ExtZGholson
Topic Author
Posts: 22
Joined: 12 Mar 2021, 01:24

Re: Passing Rectangle via converter

02 Sep 2021, 18:35

We ended up walking up the visual tree and examining the values of the offsets of the elements as went. Based on this, we discovered that we could pick a target parent by name (Noesis::String), get the TransformToAncestor and then calculate a position from the transformation matrix.
bool ElementPositionSizeConverter::TryConvert(Noesis::BaseComponent* value, const Noesis::Type* /*targetType*/, Noesis::BaseComponent* parameter, Noesis::Ptr<Noesis::BaseComponent>& result)
{
    const Noesis::String targetName = Noesis::Boxing::CanUnbox<Noesis::String>(parameter) ? Noesis::Boxing::Unbox<Noesis::String>(parameter) : Noesis::String("NameNotFound");    
    const Noesis::Rectangle* myRectangle = DynamicCast<Noesis::Rectangle*>(value);

    if (myRectangle && myRectangle->IsConnectedToView())
    {
        Noesis::FrameworkElement* rootElement = myRectangle->GetParent();
        while (rootElement->GetParent() != nullptr)
        {
            if (strcmp(rootElement->GetName(), targetName.Str()) == 0)
            {
                break;
            }
            rootElement = rootElement->GetParent();
        }

        const Noesis::Size contentSize = Noesis::Size(myRectangle->GetActualWidth(), myRectangle->GetActualHeight());
        const Noesis::Matrix4 theMatrix = myRectangle->TransformToAncestor(rootElement);
        const Noesis::Vector4 myTransform = Noesis::Vector4(0, 0, 0, 1.0f) * theMatrix;
        const Noesis::Point contentPosition = Noesis::Point(myTransform.x / myTransform.w, myTransform.y / myTransform.w);
        result.Reset(Noesis::Boxing::Box(Noesis::Rect(contentPosition, contentSize)));
        return true;
    }
    //Return true always to prevent log spam.  Is there a better way of doing this?
    result.Reset(Noesis::Boxing::Box(Noesis::Rect(Noesis::Point(), Noesis::Size())));
    return true;
}

Who is online

Users browsing this forum: Ahrefs [Bot] and 65 guests