2

The FrameworkElement.FindName(string name) method returns an object from the XAML namescope. As a reverse to this, is there a way to get the name of the object as a string by passing the object?

Example:

The following ViewPort3D contains a red cone and a blue cone. On hit-testing I can get the cone object that was clicked on. But what I need is information on which cone was hit (preferably "redCone" as a string), so I can use this information to decide, say, which image to popup

    <Viewport3D Name="myViewport" MouseDown="myViewport_MouseDown">
        <Viewport3D.Children>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <Model3DGroup >
                        <Model3DGroup.Children>

                            <!-- Define a red cone -->
                            <GeometryModel3D x:Name="redCone">
                                <GeometryModel3D.Geometry>
                                    <MeshGeometry3D />
                                </GeometryModel3D.Geometry>
                            </GeometryModel3D>

                            <!-- Define a blue cone -->
                            <GeometryModel3D x:Name="redCone">
                                <GeometryModel3D.Geometry>
                                    <MeshGeometry3D />
                                </GeometryModel3D.Geometry>
                            </GeometryModel3D>

                        </Model3DGroup.Children>
                    </Model3DGroup>
                </ModelVisual3D.Content>
            </ModelVisual3D>
        </Viewport3D.Children>
    </Viewport3D>

What I have so far:

I do have a non-dynamic solution where I have to check the object that was clicked on against hard-coded objects:

    private void myViewport_MouseDown(object sender, MouseButtonEventArgs e)
    {
        Point mousePoint = e.GetPosition(this);
        PointHitTestParameters pointparams = new PointHitTestParameters(mousePoint);

        //Test for a result in the Viewport3D
        VisualTreeHelper.HitTest(myViewport, null, HTResult, pointparams);
    }

    public HitTestResultBehavior HTResult(System.Windows.Media.HitTestResult rawresult)
    {
        RayHitTestResult rayResult = rawresult as RayHitTestResult;

        if (rayResult != null)
        {
            RayMeshGeometry3DHitTestResult rayMeshResult = rayResult as RayMeshGeometry3DHitTestResult;

            if (rayMeshResult != null)
            {
                GeometryModel3D hitGeometry = rayMeshResult.ModelHit as GeometryModel3D;

                // Non-dynamic solution:
                if (hitGeometry.Equals(redCone))
                    infoImage.Source = System.IO.Path.Combine(imagesPath, "redCone.png");
                else if (hitGeometry.Equals(blueCone))
                    infoImage.Source = System.IO.Path.Combine(imagesPath, "blueCone.png");

                // Desired solution:
                // string hitName = FindObject(hitGeometry);
                // infoImage.Source = System.IO.Path.Combine(imagesPath, hitName, ".png");
            }
        }

        return HitTestResultBehavior.Stop;
    }

But as you can see, I have to write code for each GeometryModel3D object I want to hit-test against and compare it with hitGeometry. It would be nice to have a method (like FindObject in the code above) that returns the name assigned to 'hitGeometry` in XAML namescope by passing the object.

The question:

Is there a solution where I can get the name of object that was hit so I don't have to go change my code every time the 3D landscape changes?

Things to note:

  • This is for GeometryModel3D objects so there is no Name property.
  • GeometryModel3D is a sealed class
  • The actual XAML for the ViewPort3D is generated from .3ds files and is more complicated than this so I cannot afford to change the structure
Vanlalhriata
  • 559
  • 5
  • 19
  • What if you just created an Attached Property for you object? – JLott Oct 30 '13 at 15:22
  • @JLott It doesn't seem like that's possible according to [this](http://stackoverflow.com/questions/16701467/is-it-possible-to-create-an-attached-property-on-a-sealed-xaml-control-class). Also that would mean editing the generated XAML, which I would **really** like to avoid – Vanlalhriata Oct 30 '13 at 15:27
  • Yeah that is understandable... Just a thought – JLott Oct 30 '13 at 15:30
  • It's one of the failed solutions I tried :) Thanks for the suggestion though – Vanlalhriata Oct 30 '13 at 15:32

2 Answers2

2

We were looking for ways to do it some time ago and find out that you have to store a list of clickable elements anyway... In our project we partly solved this problem by storing a List<string> which contains names of GeometryModel3D objects you actually click.

And the hit-test part looks similar to yours:

...
    if (rayMeshResult != null)
       {
          GeometryModel3D hitgeo = rayMeshResult.ModelHit as GeometryModel3D;
          foreach (string s in List)
          {
           if (hitgeo.Equals(FindName(s)))
           {
               //do something
           }
         }
       }
lena
  • 1,181
  • 12
  • 36
  • `partly solved` is right. This is what I ended up doing; this plus making a custom Viewport3D. That at least brings in a little reusability. It's just that getting the name of the 3D object seems like such a simple task and being able to do it would have made the control so much better. – Vanlalhriata Nov 01 '13 at 07:56
  • I have now made my UserControl (not custom anymore) dynamic by using this approach and reading the 3D content XAML using `XamlReader`, and providing the hit-testable object names as a list. Thanks. I still wish I could directly get the name of the 3D object though. – Vanlalhriata Nov 10 '13 at 16:22
2

For people who want to achieve this,

I used this approach :

  <GeometryModel3D x:Name="cube" AutomationProperties.Name="cube1">

And then in mouse down event :

        // See if we hit a model.
        RayMeshGeometry3DHitTestResult mesh_result =
            result as RayMeshGeometry3DHitTestResult;
        if (mesh_result != null)
        {
            GeometryModel3D model =
                (GeometryModel3D)mesh_result.ModelHit;
            Title = AutomationProperties.GetName(model);

You will get cube1

Actually it seems that x:Name is useless on runtime and only AutomationProperties.Name is working.

This post was helpfult to me : In WPF, what are the differences between the x:Name and Name attributes?

Community
  • 1
  • 1
Spongebob Comrade
  • 1,495
  • 2
  • 17
  • 32