Iām building a user control that has a ViewPort3D. I want to be able to update the view port with a bound property of ModelVisual3D (through an exposed method accepting data that is used to create the visual model). To try the user control I am also trying to use it in a window. I believe I am missing something regarding the binding, and wonder if someone could help me by pointing out what I am missing.
This is my code:
The user control
<UserControl x:Class="TheProject.TheUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="ThisUserControl"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Viewport3D Name="mainViewPort" ClipToBounds="True">
<Viewport3D.Camera>
<PerspectiveCamera
FarPlaneDistance="100"
LookDirection="-11,-10,-9"
UpDirection="0,1,0"
NearPlaneDistance="2"
Position="11,10,9"
FieldOfView="70" />
</Viewport3D.Camera>
<Viewport3D.Children>
<ModelVisual3D Content="{Binding Path=Model, ElementName=ThisUserControl}" />
<ModelVisual3D>
<ModelVisual3D.Content>
<DirectionalLight
Color="White"
Direction="-2,-3,-1">
</DirectionalLight>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D.Children>
</Viewport3D>
</Grid>
</UserControl>
Code behind
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.ComponentModel;
namespace TheProject
{
public partial class TheUserControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
private ModelVisual3D _model;
protected ModelVisual3D Model
{
get { return this._model; }
set
{
this._model = value;
OnPropertyChanged("Model");
}
}
public void SetTheData(ITheData theData)
{
if (theData != null)
{
this.Model = SimpleTriangleModel(theData);
}
}
private static ModelVisual3D SimpleTriangleModel(ITheData theData)
{
var point0 = new Point3D(theData.X, 0, 0);
var point1 = new Point3D(0, theData.Y, 0);
var point2 = new Point3D(0, 0, theData.Z);
return new ModelVisual3D
{
Content = CreateTriangleModel(point0, point1, point2, new SolidColorBrush(Colors.Tomato))
};
}
public static Model3DGroup CreateTriangleModel(Point3D p0, Point3D p1, Point3D p2, Brush brush)
{
var mesh = new MeshGeometry3D();
mesh.Positions.Add(p0);
mesh.Positions.Add(p1);
mesh.Positions.Add(p2);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
Vector3D normal = CalculateNormal(p0, p1, p2);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
Material material = new DiffuseMaterial(brush);
var model = new GeometryModel3D(mesh, material);
var group = new Model3DGroup();
group.Children.Add(model);
return group;
}
public static Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
{
var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
return Vector3D.CrossProduct(v0, v1);
}
}
}
The data interface
namespace TheProject
{
public interface ITheData
{
double X { set; get; }
double Y { set; get; }
double Z { set; get; }
}
}
The user control using window
<Window x:Class="TheProject.TheWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TheProject"
Title="The Window" Height="416" Width="649">
<Grid>
<local:TheUserControl x:Name="TheUserControl1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</Grid>
</Window>
Code behind
using System.Windows;
namespace TheProject
{
public partial class TheWindow : Window
{
private class DataClass : ITheData
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
public TheWindow()
{
InitializeComponent();
ITheData newData = new DataClass { X = 3, Y = 2, Z = 1 };
this.TheUserControl1.SetTheData(newData);
this.DataContext = this;
}
}
}
The event handler is null in OnPropertyChanged so I suppose there is some binding mechanism that I'm missing. I've tried a bunch of different approaches. I've tried reading about data binding in WPF and about DataContext but can't seem to figure out how it works or how to use it. I've also read about dependency properties but am not sure that is what I need.
(Also any guidance into making this best practice is appreciated. Should I try to MVVM:ify the user control, and in that case how? Or would you consider this being needless complexity?)
None of these helped me get it to work:
WPF - Binding in simple User Control WPF Simple Binding to an objects property Custom UserControl Property used by child element How can I bind a property of a user control to a properties of that same control in WPF? WPF User control binding issue
Thanks /Ulf ā currently a WPF newbie... :-)