title: HelixToolkit 3D WPF custom image for each cube side face
body: <p>I'm using HelixToolkit3D for a small school project which requires to generate 3D objects(Cubes) based on a existing list with objects(Custom create objects) and each cube face should be able to have its own image which will be as an string attribute(path to the image) to the object on the list that I mentioned earlier (for example front_image, back_image etc). I'm using Wpf and I wanted to use binding for generating the 3D elements. After searching I found this link <a href="https://github.com/helix-toolkit/helix-toolkit/tree/develop/Source/Examples/WPF/ExampleBrowser/Examples/DataTemplate" rel="nofollow noreferrer">https://github.com/helix-toolkit/helix-toolkit/tree/develop/Source/Examples/WPF/ExampleBrowser/Examples/DataTemplate</a> which was exactly what I needed, and I was able to make it work but only cubes with a solid color. I tried to set the material from image but it's not working. Also I wanted to add edge lines like a wireframe for each cube.</p>
  
  <p>Here is my code so far:</p>
  
  <p>3D view xaml file</p>
  
  <pre><code>&lt;Controls:MetroWindow x:Class="Project1._3DView"
      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:local="clr-namespace:Planom"
      xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
      xmlns:h="http://helix-toolkit.org/wpf"
      Closing="Window_Closing"
      Icon="Images/meIcon.ico"
      mc:Ignorable="d" 
      PreviewKeyDown="MetroWindow_PreviewKeyDown"
      WindowStartupLocation="CenterScreen" WindowState="Maximized"
      Title="Pamja 3-Dimensionale" Height="768" Width="1024"&gt;
  &lt;Window.Resources&gt;
      &lt;local:ColorConverter3D x:Key="colorConverter"/&gt;
      &lt;local:DataTemplate3D x:Key="{x:Type local:CubeElement}"&gt;
          &lt;local:GenericUIElement3D widthX="{Binding Depth}" Material="{Binding Material}" heightZ="{Binding Height}" depthY="{Binding Width}" Color="{Binding color}"&gt;
              &lt;local:GenericUIElement3D.Transform&gt;
                  &lt;TranslateTransform3D OffsetX="{Binding Position.X}" OffsetY="{Binding Position.Y}" OffsetZ="{Binding Position.Z}" /&gt;
              &lt;/local:GenericUIElement3D.Transform&gt;
          &lt;/local:GenericUIElement3D&gt;
      &lt;/local:DataTemplate3D&gt;
  &lt;/Window.Resources&gt;
  &lt;Grid&gt;
      &lt;Grid.RowDefinitions&gt;
          &lt;RowDefinition Height="71*"/&gt;
          &lt;RowDefinition Height="666*"/&gt;
      &lt;/Grid.RowDefinitions&gt;
      &lt;h:HelixViewport3D ShowCoordinateSystem="True" ZoomExtentsWhenLoaded="True" ShowFrameRate="True" ShowCameraTarget="True" Grid.RowSpan="2"&gt;
          &lt;h:SunLight&gt;
              &lt;h:SunLight.Transform&gt;
                  &lt;TranslateTransform3D OffsetX="200" OffsetY="-200" OffsetZ="200" /&gt;
              &lt;/h:SunLight.Transform&gt;
          &lt;/h:SunLight&gt;
          &lt;h:GridLinesVisual3D Center="0,0,0" Width="400" Length="400" MinorDistance="10" MajorDistance="10" Thickness="0.1" Fill="Black"/&gt;
          &lt;local:ItemsVisual3D ItemsSource="{Binding ObservableElements}" RefreshChildrenOnChange="True"/&gt;
      &lt;/h:HelixViewport3D&gt;
  &lt;/Grid&gt;
  </code></pre>
  
  <p></p>
  
  <p>3D view .cs file</p>
  
  <pre><code>public partial class _3DView : MahApps.Metro.Controls.MetroWindow
  {
      Shelf currentShelf;
      public ObservableCollection&lt;Article&gt; ObservableElements { get; set; }
      public _3DView(Shelf currentShelf)
      {
          InitializeComponent();
          this.ObservableElements = new ObservableCollection&lt;CubeElement&gt;();
          this.DataContext = this;
          this.currentShelf = currentShelf;
          foreach (CubeElement a in currentShelf.books)
          {
                  a.Position = new Point3D((a.Depth / 2) - (a.Depth * a.depthF) + currentShelf.Depth / 2 + 1, (a.Width / 2) + a.leftPush  - currentShelf.Width / 2,  20);
                  ObservableElements.Add(a);
          }
          currentShelf.items3D = ObservableElements;
      }
  }
  </code></pre>
  
  <p>CubeElement.cs</p>
  
  <pre><code>public partial class CubeElement : INotifyPropertyChanged
  {
      public CubeElement()
      {
          fSize = 12;
          changeTracking = false;
          drawRatio = 1;
          isSelectable = true;
      }
  
      public string Id { get; set; }
      public string Name { get; set; }
      public string left_image { get; set; }
      public string front_image { get; set; }
      public string back_image { get; set; }
      public string right_image { get; set; }
      public string top_image { get; set; }
      public Shelf shelf{ get; set; }
      private double _width { get; set; }
      public virtual double Width
      {
          get { return _width; }
          set
          {
              _width = value;
              widthDraw = _width * mainDraw;
              OnPropertyChanged("Width");
          }
      }
      private double _height { get; set; }
      public virtual double Height
      {
          get { return _height; }
          set
          {
              _height = value;
              heightDraw = _height * mainDraw;
              OnPropertyChanged(nameof(Height));
          }
      }
      public virtual double Depth { get; set; }
      public double Weight { get; set; }
      public string Color
      {
          get { return _Color; }
          set
          {
              _Color = value;
              color = (Color)ColorConverter.ConvertFromString(_Color);
              OnPropertyChanged("Color");
          }
      }  
  
      private string _Color { get; set; }
      public int _depthF { get; set; }
      public int depthF
      {
          get { return _depthF; }
          set
          {
              _depthF = value;
              if (shelf!= null)
              {
                  Position = new Point3D((Depth / 2) - (Depth * _depthF) + shelf.Depth / 2 + 1, (Width / 2) + leftPush  - (shelf.Width / 2), 20);
              }
              OnPropertyChanged("depthF");
          }
      }
      private double _leftPush { get; set; }
      public double leftPush
      {
          get { return _leftPush; }
          set
          {
              _leftPush = value;
              leftPushP = value * drawRatioW;
              OnPropertyChanged("leftPush");
              Position = new Point3D((Depth / 2) - (Depth * depthF) + shelf.Depth / 2 + 1, (Width / 2) + (_leftPush / mainDraw) - (shelf.Width / 2), 20);
          }
      }        
      private string _imagePath { get; set; }
      [System.ComponentModel.DataAnnotations.Schema.NotMapped]
      public string imagePath
      {
          get { return _imagePath; }
          set
          {
              _imagePath = value; OnPropertyChanged("imagePath");
              if(_imagePath != null)
              {
                  Material = MaterialHelper.CreateEmissiveImageMaterial(_imagePath, Brushes.Red, UriKind.Absolute);
              }
              else
              {
                  Material = MaterialHelper.CreateMaterial(color);
              }
          }
      }
  
      public event PropertyChangedEventHandler PropertyChanged;
  
      protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
      {
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
  
      public object this[string propertyName]
      {
          get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
          set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
      }
      public Model3D Model { get; set; }
      public Material Material { get; set; }
      public Point3D _position;
      public Point3D Position
      {
          get { return _position; }
          set
          {
              _position = value;
              OnPropertyChanged("Position");
          }
      }
      public double Radius { get; set; }
      private bool isVisible = true;
      public bool IsVisible
      {
          get { return isVisible; }
          set
          {
              if (IsVisible != value)
              {
                  isVisible = value;
                  OnPropertyChanged("IsVisible");
              }
          }
      }
      private Color _color { get; set; }
      public Color color
      {
          get { return _color; }
          set
          {
              _color = value;
              OnPropertyChanged("color");
          }
      }
  }
  </code></pre>
  
  <p>Shelf.cs</p>
  
  <pre><code>public partial class Shelf
  {
      public string Id { get; set; }
      public string Name { get; set; }
      private double _width { get; set; }
      public virtual double Width
      {
          get { return _width; }
          set
          {
              _width = value;
              OnPropertyChanged("Width");
          }
      }
      private double _height { get; set; }
      public virtual double Height
      {
          get { return _height; }
          set
          {
              _height = value;
              OnPropertyChanged("Height");
          }
      }
      public double Depth { get; set; }
      public double Weight { get; set; }   
      public ObservableCollection&lt;CubeElement&gt; books{ get; set; }
  }
  </code></pre>
  
  <p>GenericUIElement3D.cs</p>
  
  <pre><code>public class GenericUIElement3D : UIElement3D
  {
      protected GeometryModel3D Model { get; set; }
  
      public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
          nameof(Color), typeof(Color), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) =&gt; ((GenericUIElement3D)s).ColorChanged()));
  
      public static readonly DependencyProperty MaterialProperty = DependencyProperty.Register(
          nameof(Material), typeof(Material), typeof(GenericUIElement3D), new PropertyMetadata(null));
  
      public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(
          nameof(widthX), typeof(double), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) =&gt; ((GenericUIElement3D)s).DimensionsChanged()));
  
      public static readonly DependencyProperty HeightProperty = DependencyProperty.Register(
          nameof(heightZ), typeof(double), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) =&gt; ((GenericUIElement3D)s).DimensionsChanged()));
  
      public static readonly DependencyProperty DepthProperty = DependencyProperty.Register(
          nameof(depthY), typeof(double), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) =&gt; ((GenericUIElement3D)s).DimensionsChanged()));
  
      public Color Color
      {
          get { return (Color)GetValue(ColorProperty); }
          set { SetValue(ColorProperty, value); }
      }
  
      public double widthX
      {
          get { return (double)GetValue(WidthProperty); }
          set { SetValue(WidthProperty, value); }
      }
  
      protected override void OnMouseEnter(MouseEventArgs e)
      {
          base.OnMouseEnter(e);
          //MessageBox.Show("OnMouseDown raised. " + e.OriginalSource);
      }
  
      public double heightZ
      {
          get { return (double)GetValue(HeightProperty); }
          set { SetValue(HeightProperty, value); }
      }
  
      public double depthY
      {
          get { return (double)GetValue(DepthProperty); }
          set { SetValue(DepthProperty, value); }
      }
  
      public Material Material
      {
          get { return (Material)GetValue(MaterialProperty); }
          set { SetValue(MaterialProperty, value); }
      }
  
      public GenericUIElement3D()
      {
          Model = new GeometryModel3D();
          BindingOperations.SetBinding(Model, GeometryModel3D.MaterialProperty, new Binding(nameof(Material)) { Source = this });
          Visual3DModel = Model;
      }
      private void SetGeometry()
      {
          MeshBuilder meshBuilder = new MeshBuilder(false, false);
          meshBuilder.AddBox(new Point3D(0, 0, heightZ / 2), widthX, depthY, heightZ);
          Model.Geometry = meshBuilder.ToMesh();
      }
  
      private void ColorChanged()
      {
          Material = MaterialHelper.CreateMaterial(Color);
      }
  
      private void DimensionsChanged()
      {
          SetGeometry();
      }
  
      private void DepthChanged()
      {
          SetGeometry();
      }
  }
  </code></pre>
  
  <p>This project is about a school library so there will be books and a shelf. Code is shorten in order to be more easy if someone tries to experiment(I hope I didn't remove any crucial lines). Basically there will be a 2D view on the beginning which will be used to create the shelf and add books and then if wanted user can switch to a 3D view in parallel windows. I cant really understand the 3D "VisualTree" so good(so far) that's why I need some help.</p>