WPF ViewPort3D 用户控件与 ModelVisual3D 属性绑定?

发布于 2024-11-09 13:09:42 字数 6119 浏览 4 评论 0原文

我正在构建一个具有 ViewPort3D 的用户控件。我希望能够使用 ModelVisual3D 的绑定属性更新视图端口(通过接受用于创建可视模型的数据的公开方法)。为了尝试用户控件,我还尝试在窗口中使用它。 我相信我遗漏了一些有关绑定的内容,并且想知道是否有人可以通过指出我遗漏的内容来帮助我。

这是我的代码:

用户控件

<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>

背后的代码

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);
    }
  }
}

数据接口

namespace TheProject
{
  public interface ITheData
  {
    double X { set; get; }
    double Y { set; get; }
    double Z { set; get; }
  }
}

使用窗口的用户

<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>

控件 背后的

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;
    }
  }
}

代码 事件处理程序在 OnPropertyChanged 中为空,所以我想我缺少一些绑定机制。我尝试了很多不同的方法。 我尝试阅读有关 WPF 中的数据绑定和 DataContext 的内容,但似乎无法弄清楚它是如何工作的或如何使用它。我还阅读了有关依赖属性的信息,但不确定这是否是我所需要的。

(此外,任何有关实现此最佳实践的指导都值得赞赏。我应该尝试 MVVM:ify 用户控件吗?在这种情况下如何?或者您会认为这是不必要的复杂性吗?)

这些都没有帮助我让它工作:

< a href="https://stackoverflow.com/questions/1095527/wpf-binding-in-simple-user-control">WPF - 简单用户控件中的绑定 WPF 简单绑定到对象属性 子元素使用的自定义 UserControl 属性 如何将用户控件的属性绑定到 WPF 中同一控件的属性? WPF 用户控件绑定问题

谢谢 /Ulf – 目前是 WPF 新手...:-)

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... :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文