How to databind ViewModels objects as 3DModels in WPF - c#

I am working on an application that is generating a bunch of collections of ViewModel Instances representing wooden planks (has all needed attriuttes-x,y,z,posx,posy,posz). This works fine.
Now I would like to visualize these Planks inside of the app in a 3d environments:
I have found plenty of examples how to creates boxes with viewport3d Frameworkelement, but my problem is that all of them show how to statically define a single 3dobject.
I was trying and experimenting, but I did not manage to find a single example of how to databind a whole collection, transform single boxes, rotate, and resize them.
Does anyone know how to databind 3D viewmodel collections in WPF?

You can
Put to your Window a dependent property ViewModels and on changing it create your geometry models with bindings in code behind
or
Create a UserControl based ex. on Viewport3D and do 1. for it
The bindings you can create such way(pseudo code):
var geo = new MeshGeometry3D { Positions = new Point3DCollection(pointsLists), TriangleIndices = new Int32Collection(indexes) };
geo.Freeze();
var mat = new DiffuseMaterial(Brushes.Gray); mat.Freeze();
var bMat = new DiffuseMaterial(Brushes.Red); bMat.Freeze();
var geomod = new GeometryModel3D(geo, mat);
geomod.BackMaterial = bMat;
geomod.Transform = new ScaleTransform3D();
var bndng = new Binding("ScaleValue");
bndng.Source = SomeViewModel;//Here put the propriate viewmodel
BindingOperations.SetBinding(geomod.Transform, ScaleTransform3D.ScaleXProperty, bndng);
BindingOperations.SetBinding(geomod.Transform, ScaleTransform3D.ScaleYProperty, bndng);
BindingOperations.SetBinding(geomod.Transform, ScaleTransform3D.ScaleZProperty, bndng);
geomod.Geometry = geo;
Model3DGroup.Children.Add(geomod);//Here you have to find reference to you Model3DGroup
You can use an example that you can copy/paste from:
Same ScaleTransform3D for different geometries

Related

Subview in Scrollview Fluent layout

I'm new at using Fluent layout for a Xamarin.iOS project and I now stumbled on a problem that I don't know how to solve.
The situation is like this:
I have a scrollview as the mainView and some texts inside that, and I now want to add another view (let's call it "subView") containing other texts.
Why I want the "subView" is because I have a "Hide/Show" button that are suppose to Hide or Show the "subView".
Where'm I suppose to add the constraints for positioning the stuff inside "subView"? Inside subView.AddConstraints(), or in mainView.AddConstraints?
I don't know how to do this, can somebody please help me?
Example of what im doing
Try to use this code:
CGRect rect = new CGRect(0, UIApplication.SharedApplication.StatusBarFrame.Height, UIScreen.MainScreen.ApplicationFrame.Width , UIScreen.MainScreen.ApplicationFrame.Height);
UIScrollView Mainscrollview = new UIScrollView(rect) { BackgroundColor = UIColor.Gray };
View.AddSubview(Mainscrollview);
this.View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
var someLablel = new UILabel();
var button = new UIButton { BackgroundColor = UIColor.Gray };
var blueView = new UIView { BackgroundColor = UIColor.Blue };
Mainscrollview.AddSubviews( someLablel,button, blueView);
Mainscrollview.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
Mainscrollview.AddConstraints(
someLablel.AtTopOf(Mainscrollview, 50),
someLablel.AtLeftOf(Mainscrollview, 10),
someLablel.AtRightOf(Mainscrollview, 10),
someLablel.Height().EqualTo(50),
button.AtBottomOf(someLablel, 10),
button.WithSameLeft(someLablel),
button.WithSameRight(someLablel),
button.WithSameHeight(button),
blueView.AtBottomOf(button, 10),
blueView.WithSameLeft(someLablel),
blueView.WithSameRight(someLablel),
blueView.AtBottomOf(Mainscrollview, 10)
);
//*do as above*
//blueView.AddSubviews(SubViewLabel1, Another , more);
//blueView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
//blueView.AddConstraints( );
PS:
As what i said above, FluentLayout is used to describe the relationship between parenet view and subview or subview and subview (with the same parent view). We can't relate subview to it's super super view (e,g ,subviewLabel and Mainscrollvew in the pic). And What's the most important is the Constraints we add must be sufficient, which means the view must get its position and size with those Constraints.
Where'm I suppose to add the constraints for positioning the stuff inside "subView"? Inside subView.AddConstraints(), or in mainView.AddConstraints?
Views are assigned the constraints which lay out their direct subviews, so the controls inside subView should be laid out with subView.AddConstraints(). If subView is a subview of mainView, then it should be laid out with mainView.AddConstraints().
EDIT: An example:
mainView.Add(subView);
subView.Add(someOtherView);
var myPadding = 12f;
mainView.AddConstraints(new FluentLayout[]
{
subView.AtTopOf(mainView, myPadding),
subView.AtLeftOf(mainView, myPadding),
subView.AtRightOf(mainView, myPadding),
subView.AtBottomOf(mainView, myPadding)
});
subView.AddConstraints(new FluentLayout[]
{
someOtherView.AtTopOf(subView, myPadding),
someOtherView.AtLeftOf(subView, myPadding),
someOtherView.AtRightOf(subView, myPadding),
someOtherView.AtBottomOf(subView, myPadding)
});
Hiding and showing views is a separate issue - you would need to destroy and recreate separate constraints when an action is taken that shows or hides subView, or use MvvmCross to bind certain constraints to ViewModel properties, which you can find instructions on here.

C# Interacting with ModelVisual3D objects in WPF

Hey I am using a WPF 3D scene and successfully loaded a few .stl models into it.
I am basicall using a method to select and deselect these objects, based on where my mouse in the windows is:
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var viewport = (HelixViewport3D)sender;
var firstHit = viewport.Viewport.FindHits(e.GetPosition(viewport)).FirstOrDefault();
if (firstHit != null)
{
this.viewModel.Select(firstHit.Visual);
}
else
{
this.viewModel.Select(null);
}
}
So I have the selected ModelVisual3D and could store it. However here is the main problem:
My models are generated based on certain data and an associated .stl model.
Basicall I import the .stl model and show it in my 3D scene, but the problem is I have no idea how connect my other data to the model.
For example when I select the visual, I want to show another windows with information like: material, dimensions, company.
But I can't figure out how to determine, which unique ModelVisual3D object is selected at the moment. There seem to be no properties I could use to my advantage doing something like:
ModelImporter tr = new ModelImporter();
var model = tr.Load("C:\\Users\\...\\Pictures\\a.stl");
ModelVisual3D test = new ModelVisual3D();
test.Content = model;
//Here I would like to save the id of my visual model to identify and
//associate it with my other data later
int myUniqueModelID=test.Properties.UNIQUEID
What about using a dictionary to keep the mapping between your data reference and ModelVisual3D reference.
Like
Dictionary<ModelVisual3D, StlDataObject> modelDataMap = new Dictionary<ModelVisual3D, StlDataObject>();
public void LoadModelWithData(string dataFilePath, string stlModelPath)
{
ModelImporter tr = new ModelImporter();
var model = tr.Load("C:\\Users\\...\\Pictures\\a.stl");
ModelVisual3D test = new ModelVisual3D();
test.Content = model;
//Load the datafile from file (or pass it this method)
StlDataObject IdForOurStlModel = GetStlDataFromFile(dataFilePath);
modelDataMap.Add(test, IdForOurStlModel);
}
Then when you need to find your data from the hit test in your viewport, you just check if this dictionary contains a key for the pressed ModelVisual3d and return the value related to that key if it does.

Element is already the child of another element, Windows Phone 8

Hey embarrassingly enough I ran into the error stated in the title. As many others have sought to find a solution online, but I was not able to do so.
Okay so my issue, is that I have an ObservableCollection of my elements which is used in an Itemscontrol. I want to create a new element of one of the elements in the ObservableCollection. Because of the trouble with working with ObservableCollection I created a serializing and Deserializing of the specific object. like this>
IsolatedStorage.SerializeSElement("saveString", saveElement);
IsolatedStorage.DeSerializeSElement("saveString", loadElement);
Which I than hoped gave me a completely new element. But I still have the same element. How do I solve the issue if say my class looks like>
public class myElement(){
int posx;
int posy;
double id;
bool isMoveable;
}
But there is still the error, is there way to solve this error?
Code
private Geometry createGeometry(SViewModel sModelRec)
{//TEST
sModel = sModelRec.Gear;
sModelRec = null;
//new Path.Combine(DecorationOnShield[i].Gear.Path,"");
PathGeometry pathGeometry = new PathGeometry();
PathFigure testPathFigure = new PathFigure();
System.Windows.Shapes.Path pathTesting = new System.Windows.Shapes.Path();
var b = new System.Windows.Data.Binding
{
Source = sModel.Path
};
System.Windows.Data.BindingOperations.SetBinding(pathTesting, System.Windows.Shapes.Path.DataProperty, b);
pathTesting.Width = sModel.Width;
pathTesting.Height = sModel.Height;
Geometry geometry = pathTesting.Data;
return geometry;
}
So when I return the element and add it to the GeometryGroup I get the error.
You are trying to Add a Control and that Control already exists in your VisualTree.
The solution is simple
You have Source Parent Container = The control in witch resides the element you want to add to the other container
Target Parent Container = The control in witch you want to add your element.
To solve your problem just remove the element from the Source Parent Container and then add it to your Target Parent Container
Note: to have an answer more specific to your situation you must post your view or the lines where the error is getting thrown

Is this the way and appeal of data binding?

Okay, let's see if I'm thinking straight:
If I simply want to read some data from a source and then apply it to some control, I may as well just do that directly rather than go to the trouble of data binding. IOW, I may as well do this:
foreach (var quad in listQH)
{
...
tb.Text = quad.Ph1;
...as opposed to:
tb.DataBindings.Add(new Binding("Text", quad, "Ph1"));
However, if I want updates within the underlying class instance to update the controls (the "tb" textBox in this case), and user updates of the controls to update those class instance members ("two-way binding"), I need to implement INotifyPropertyChanged. But then, I would have to change this code:
List<QHQuad> listQH = GetForPlatypus(PlatypusId, dow); // listQH locally declared
foreach (var quad in listQH)
{
int QHCell = quad.QH;
if ((QHCell >= 1) || (QHCell <= QUARTER_HOUR_COUNT))
{
string PH1CellToPopulate = string.Format("textBoxA_{0}", QHCell);
string PH2CellToPopulate = string.Format("textBoxB_{0}", QHCell);
string PH3CellToPopulate = string.Format("textBoxC_{0}", QHCell);
var tb = (TextBox)this.Contro.Find(PH1CellToPopulate, true).First();
tb.DataBindings.Add(new Binding("Text", quad, "Ph1"));
. . .
...to:
List<QHQuad> listQH; //global to the form
. . .
listQH = GetInfoForPlatypus(PlatypusId, dow);
foreach (var quad in listQH)
{
// the same as above
And then I'd be able to ultimately save those potentially changed class instance values in this way:
foreach (var quad in listQH)
{
UpdateQH(quad); // quad contains members QH, Ph1, Ph2, and Ph3 ("UPDATE BLA SET PH1 = :Ph1, PH2 = :Ph2, PH3 = :Ph3 WHERE QH = :QH")
}
You have the right idea. Here are a few pointers, though.
INotifyPropertyChanged is only required if you want changes from within the ViewModel to bubble up to the view. It is not required for two-way binding. If you want two way binding and only need to read once when view loads, a plain property is fine.
You may want to check out the MVVM (Model - View - ViewModel) pattern. It is the recommended design pattern for WPF, Silverlight, Metro, etc. because it is great for data-binding heavy implementations.

TreeListControl within each NavbarControl Group

I'm very new to WPF and I'm attempting to create a treelist navigation within each navbar group. Because the number of navbar groups and treelists are dynamic I have to make them in code rather than them be pre-defined in XAML.
I have tested the following so far which is meant to define the navbar group's content rather than use the default item
private void CreateGroup2(NavBarControl navBar)
{
NavBarGroup group2 = new NavBarGroup();
group2.Header = "Custom Content";
//Specify that the group's content should be defined via the Content property
group2.DisplaySource = DisplaySource.Content;
TreeListControl tree = new TreeListControl();
tree.ItemsSource = TreeList_DataBinding.Stuff.GetStuff();
group2.Content = tree;
navBar.Groups.Add(group2);
}
This gives an Exception: Grid.InfiniteGridSizeException: By default, an infinite grid height is not allowed since all grid rows will be rendered and hence the grid will work very slowly. To fix this issue, you should place the grid into a container that will give a finite height to the grid, or you should manually specify the grid's Height or MaxHeight. Note that you can also avoid this exception by setting the TreeListControl.AllowInfiniteGridSize static property to True, but in that case the grid will run slowly.
I'm a little confused as I'm not using a grid? Can anyone give any pointers what's wrong and how I can add a treview under each navbar group?
Thank You
It feels a bit wrong answering my own question but I managed to get it working using the following
private void CreateGroup2(NavBarGroup navBarGroup)
{
System.Windows.Controls.TreeView treeview = new System.Windows.Controls.TreeView();
TreeViewItem nod = new TreeViewItem();
nod.Header = "Tree Node1";
treeview.Items.Add(nod);
TreeViewItem nod1 = new TreeViewItem();
nod1.Header = "Tree Node2";
treeview.Items.Add(nod1);
TreeViewItem nod2 = new TreeViewItem();
nod2.Header = "Tree Node3";
nod1.Items.Add(nod2);
//StackPanel stcPnl = new StackPanel(); /optiona
//stcPnl.Children.Add(treeview);
//navBarGroup.Content = stcPnl;
navBarGroup.Content = treeview;
navBarGroup.DisplaySource = DisplaySource.Content;
}

Categories

Resources