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.
Related
I'm working through a project where I'm going to have multiple square size instances of the same set of form components.
I can either create 8 instances manually in my form UI or what I'd rather do is create a view (or Item Renderer) and then dynamically add instances of that view to my main view.
How do I add a create and add a custom view dynamically to the main view in my Xamarin form?
Note: Including Swift tag because you might know the answer if you know Swift or Objective C since the API wraps Apple API.
If IIUC:
Create a view in XCode Interface Builder
In ViewDidLoad create an instance of the custom instance views
Add each instance to the main view
I'd read a guide if there was one but I can't find anything specifically on this.
Some what related. I can create a new View in Xcode interface Builder pretty easily. Is there a way to export that as a class to my application?
Update:
I've found a textfield in Interface Builder where I can enter the name of a class. Back in Visual Studio my main View Controller can see the HelloWorld class. I've found a method named AddChildViewController. I try testing it. Nothing happens. Do I need to set the position and size? I can't find any API to do this.
Tomorrow I will scour the ancient texts again for example code. Maybe there is something I missed?
public override void ViewDidLoad()
{
base.ViewDidLoad();
var view = new HelloView(this.Handle);
var handle = view.Handle;
base.AddChildViewController(view);
var view2 = new HelloView(this.Handle);
handle = view.Handle;
base.AddChildViewController(view2);
}
I noticed a note in the console log:
<ViewController: 0x600000a94180> already have child of:
<ViewController: 0x600000a94180>
Update II:
This adds a new NSView and then a NSButton in that view to the main window:
var frame = new CoreGraphics.CGRect(0, 0, 100, 100);
var button = new NSButton(frame) {
Title = "My Button",
};
var view = new NSView(frame) {};
view.AddSubview(button);
View.AddSubview(view);
It doesn't add my custom class yet though.
Update III:
I'm able to add the custom HelloWorldView class but the controls are not visible. If I add a button to the form I see it but it is anchored to the bottom of the screen. I don't see the controls created from Interface Builder.
//var frame = this.View.Frame;
var frame = new CoreGraphics.CGRect(0, 0, 100, 20);
var button = new NSButton(frame) {
Title = "My Button"
};
var frame2 = new CoreGraphics.CGRect(0, 0, 100, 100);
var helloView = new HelloView() {
};
helloView.Frame = frame2;
helloView.AddSubview(button);
mainFrame.AddSubview(helloView);
HelloView.cs:
public partial class HelloView : NSView
{
public HelloView () : base ()
{
}
}
Note about the code above: I removed the handle parameter because it was causing a compiler error.
Setup: Visual Studio for Mac using Xamarin C# and XCode Interface Builder
--
Notes for the bounty.
To receive the bounty you must show how to do either step 1 or step 2 mentioned above in the Bounty Requirements section. I prefer step 1. If you are unsure ask.
I want to create an application where the user can enter shortcuts to files. I know how to create buttons in code at compile time but I have no idea how to create dynamic buttons where the name and the click event are going to be dynamic.
How hard would it be to create something like the image below? Is that even possible in C#/WPF/XAML?
What would be the logic?
FYI - I don't need help with saving the buttons objects, for this I will be using JSON.
You should create an ItemsControl to show what you want, this could be an approach:
<ItemsControl
ItemsSource="{Binding YourListOfLinkObject}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding WhateverYouWantToShow}"
Command="{Binding YourCommand} "
CommandParameter="{Binding YourFileName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You should create a new (if it's not already created) class with the name of the file, the content you want to show in the button and your command. And when initializing the view, create a list of "Link" object.
The command will be the same for all of them, just declare it in a generic way to open the file you put in the CommandParameter
Now that I know you are using MVVM I will try to expand my answer focus on that.
You need a class that I will call FileLink. FileLink will have, at least, 3 properties:
public string WhateverYouWantToShow - This will be the content of your button
public ICommand YourCommand - This will have a DelegateCommand<string> that will be the one who "does" things. This command will be the same for every item you create. You just need one because you will use the parameter to execute/open one file or another.
public string YourFileName - This will be the string you need to execute your command method. I guess it will be a path or a file name.
Now that we have this class created, when initializing the third view, the one with the buttons, you will have an ObservableCollectionproperty, what I called YourListOfLinkObject, of FileLinkobjects. There you will have to add as many FileLink objects as you got from the database and they will be displayed.
If you need to change the way they are shown you just need to modify the DataTemplate.
If there's something I failed to explain again or you want me to go further just let me know :)
It is possible and simple. You add controls to your container and add container to main form. Click event is simply defined in code (which actually you know at dev time - probably you should instead use user controls).
Below is some partial code, doing a similar thing in a real world Silverlight application from years ago to give the idea:
...
sp.Children.Add(p);
foreach (var slot in group)
{
var color = colors[(int)slot.State];
var name = String.Format("W{0}", slot.When.ToString("yyyyMMddHHmm"));
Rectangle rect = new Rectangle
{
Name = name,
Width = rectWidth,
Height = rectWidth,
Margin = new Thickness(rectMargin),
Stroke = new SolidColorBrush(slot.State == Availability.Booked ? Colors.White : Colors.Black),
StrokeThickness = 1,
Fill = new SolidColorBrush(color),
RadiusX = 2,
RadiusY = 2,
Cursor = (slot.State == Availability.Booked ? Cursors.Arrow : Cursors.Hand)
};
if (slot.State != Availability.Booked)
{
rect.Effect = new DropShadowEffect(); //myDropShadowEffect,
}
if (slot.State != Availability.Booked)
{
rect.MouseLeftButtonDown += new MouseButtonEventHandler(rect_MouseLeftButtonDown);
ToolTipService.SetToolTip(rect, slot.When.ToString("MMM dd,yyyy dddd hh:mm tt"));
}
sp.Children.Add(rect);
}
b.Child = sp;
contentStackPanel.Children.Add(b);
}
Say I want to render a Xamarin.Froms control (e.g a Xamarin.Forms.Label) in a native android view.
The following code works. But ignores the width and height of the control (wrap_content).
// create a Xamarin.Froms.Label
Xamarin.Forms.Label view = new Xamarin.Forms.Label()
{
Text = "This is a Xamarin.Forms.Label",
BackgroundColor = Color.Red,
};
// create a Renderer
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
// create an AlertDialog
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
builder.SetView(renderer.ViewGroup); // Use the ViewGroup of the renderer
builder.SetTitle("Dialog");
// create and show the dialog
builder.Create().Show();
Produces the following:
Doing the same with an native label:
var view = new Android.Widget.TextView(Xamarin.Forms.Forms.Context)
{
Text = "This is a Android.Widget.TextView",
Background = new Android.Graphics.Drawables.ColorDrawable(Android.Graphics.Color.Red),
};
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
builder.SetView(view);
builder.SetTitle("Dialog");
builder.Create().Show();
Produces the expected output:
I am looking for any hints or advices regarding width / height measurement in Xamarin.Forms, and/or a solution for this kind of problem.
Update
Also note that this does not depend on the AlertDialog, using a LinearLayout instead of a AlertDialog results to the same behaviour.
The following code works. But ignores the width and height of the control (wrap_content).
Reason:
You passed a ViewGroup to the AlertDialog. But inside the AlertDialog the layout params of ViewGroup won't work at all. Refering to this blog:
Every Rule Has An Exception
...
The issue here is that AlertDialog.Builder supports a custom view, but does not provide an implementation of setView() that takes a layout resource; so you must inflate the XML manually. However, because the result will go into the dialog, which does not expose its root view (in fact, it doesn’t exist yet), we do not have access to the eventual parent of the layout, so we cannot use it for inflation. It turns out, this is irrelevant, because AlertDialog will erase any LayoutParams on the layout anyway and replace them with match_parent.
So instead of passing a ViewGroup, pass a view to AlertDialog like your second try.
Update(a few more research):
if try the following codes to set the height of ViewGroup to 400:
// create a Renderer
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
// create an AlertDialog
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
renderer.ViewGroup.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, 400);
builder.SetView(renderer.ViewGroup); // Use the ViewGroup of the renderer
builder.SetTitle("Dialog");
// create and show the dialog
builder.Create().Show();
It doesn't work, the dialog is still almost covers the whole screen:
But if add the renderered ViewGroup to LinearLayout like this:
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
//The following codes works correctly
renderer.ViewGroup.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, 400);
LinearLayout linearLayout = new LinearLayout(Xamarin.Forms.Forms.Context);
linearLayout.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
linearLayout.AddView(renderer.ViewGroup);
this.AddContentView(linearLayout,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent));
The height was set correctly:
So, on this layer, the problem lies in the AlertDialog as I mentioned above.
But If the height was set to Wrap_content like this:
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
//set the height again to wrap_content
renderer.ViewGroup.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
linearLayout.AddView(renderer.ViewGroup);
this.AddContentView(linearLayout,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent));
the rendererd ViewGroup again start to fill the whole screen:
On this layer I found that the problem lies in the rendered ViewGroup, because after changing the ViewGroup to a native LinearLayout everything is fine.
I checked the Source codes of Xamarin.Forms, and found the following Inherit hierarchy:
Xamarin.Forms.Platform.AndroidLabelRenderer ->
Xamarin.Forms.Platform.Android.ViewRenderer<TView, TNativeView> ->
Xamarin.Forms.Platform.Android.VisualElementRenderer<TElement> ->
Xamarin.Forms.Platform.Android.FormsViewGroup ->
Android.Views.ViewGroup
I found none of these classes has implemented ViewGroup.OnMeasure method. Thus the wrap_content will never work. You can refer to Custom ViewGroup for an detailed implementation example of custom ViewGroup.
I answer my own question since It is probalby the easiest way to solve the problem:
Since Xamarin.Forms 2.3.5 (as time of writing in prerelease) there is a new Property View next to ViewGroup in IVisualElementRenderer.
The solution would be to use Xamarin.Froms 2.5.3 and the property View:
Xamarin.Forms.Label view = new Xamarin.Forms.Label()
{
Text = "This is a Xamarin.Forms.Label",
BackgroundColor = Color.Red,
};
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
builder.SetView(renderer.View); // Use View instead of ViewGroup
builder.SetTitle("Dialog");
builder.Create().Show();
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
I'm using iCarousel by #Nick Lookwood to load a scrollable list of "flashcards".
Each View of the iCarousel control is one flashcard. By design, I require that when a user taps the flashcard (view), the flashcard flips to reveal the behind. Before using iCarousel, I was making two separate controls, one for front and one for back, and then using UIView.Transition (with a nice Flip From Top animation) to go from front to back when a Tap was detected, or the other way round.
Adding a UITapGestureRecognizer to my View is leading to weird artifacts and not functioning as expected (overlapping controls, the next one instead of the current one flipping, no animation, etc.) and I need a different approach. I could conveniently use the Selected event in the iCarousel Delegate instead of a Tap Gesture Recognizer, but what do I do there exactly?
In essence, I would like to replace the specific view which was tapped with another but I feel this is conflicting with the whole reusable views idea. Is there nothing I can do? (Once the view is out of the screen, I am fine with it being "flipped forward" again.)
Thanks!
p
P.S. I'm using C# and Xamarin.iOS, but I can understand Obj-C and Swift code fairly well, so any help will be appreciated.
There are lots of example for iCarausal. I assume you are using this :
iCarausal
and specifically folder name "Basic iOS Example"
How about the below changes in delegate methods:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;
//create new view if no view is available for recycling
if (view == nil)
{
//don't do anything specific to the index within
//this `if (view == nil) {...}` statement because the view will be
//recycled and used with other index values later
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)];
((UIImageView *)view).image = [UIImage imageNamed:#"page.png"];
view.contentMode = UIViewContentModeCenter;
//back view i have just added this apart from it there is no change made by me in this method
UIView *backView = [[UIView alloc] initWithFrame:view.bounds];
backView.tag = 2;
[backView setBackgroundColor:[UIColor redColor]];
[view addSubview:backView];
[backView setHidden:YES];
//back view
label = [[UILabel alloc] initWithFrame:view.bounds];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = [label.font fontWithSize:50];
label.tag = 1;
[view addSubview:label];
}
else
{
//get a reference to the label in the recycled view
label = (UILabel *)[view viewWithTag:1];
}
//set item label
//remember to always set any properties of your carousel item
//views outside of the `if (view == nil) {...}` check otherwise
//you'll get weird issues with carousel item content appearing
//in the wrong place in the carousel
label.text = [_items[index] stringValue];
return view;
}
and implementing didSelect delegate as below:
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index{
UIView *frontview = [carousel itemViewAtIndex:index];
UIView *backView = [frontview viewWithTag:2];
BOOL isInFront = NO;
if (backView.hidden == NO) {
isInFront = YES;
}
[UIView transitionWithView: isInFront ? backView : frontview
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[backView setHidden:isInFront];
}
completion:nil];
}
You should write your code more structured, it was an example to show you that you can acheive what you want with iCarausal.