How to detect a view as parent - xamarin

Consider a textView, how to detect the parent of textView using condition or some logic?
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
TextView text=new TextView(Context);
}

Answer the question as per my understanding:
If you are adding and removing a control a lot of times to get the View's parent you can use the parent property in your case:
text.Parent;
Note that this will be null if there is no parent.
So before using this parent in any way, I would suggest you null check it.
if(text.Parent!=null)

ViewGroup parent = (ViewGroup) textView.getParent();
If parent is null, it is not added to the paren.

You may detect a view as parent using text.Parent, so the code is like this:
TextView text = new TextView(this);
text.Text = "Text";
IViewParent layout = text.Parent;
if (layout == layout2) //To detect whether it is added to parent or not.
{
text.Text = "I am added to layout2";
Console.WriteLine("layout2 is the parent of text");
}
else {
layout2.AddView(text);
}

Related

Will it be a problem to have a TapGesture added inside of a data template in Xamarin? Should I somehow remove the event I added?

Within my DataTemplate (written in C#), I have this code:
var plusMinusGrid = new Grid
{
Children =
{
_minusFrame.Column(0).Bind(IsVisibleProperty, nameof(DeckRow.FRMIsVisible), source: this),
_plusFrame.Column(0).Bind(IsVisibleProperty, nameof(DeckRow.FRPIsVisible), source: this)
},
};
var plusMinusTapGesture = new TapGestureRecognizer();
plusMinusTapGesture.Tapped += PlusMinusTap;
plusMinusGrid.GestureRecognizers.Add(plusMinusTapGesture);
So I am adding the tap event to a part of each row.
My question is, will this be an issue as a memory leak and if that's the case is there a way that I can deal with this.
Here is what I have. Override the Disappearing method add a call to a CleanUp() that you write yourself. As I am doing the += on in the view model I add the -= there also. This is annoying as I also found that I needed to set itemsource to null, but itemsource is in the view. Since Disappearing is part of the view, I call the VM's CleanUp() method!
As my page is used modally this works fine. If your page is not modal, and you refresh the grid, make sure you do the -= on existing items before you repopulate.
View page :
public void CleanUp()
{
this.athleteDetailVM.CleanUp();
this.collectionV.ItemsSource = null;
}
VM Implementation:
public void CleanUp()
{
foreach (CheckedItem<EventResult> item in eventResults)
{
item.PropertyChanged -= null;
}
}

Add custom view to AndroidTV's Leanback RowsSupportFragment?

Is it possible to add some Views above and between the rows in the RowsSupportFragment?
It is possible to add any view above the rows by adding view to parent FrameLayout of VerticalGridView in onViewCreated method. To give space for this view, set proper value for verticalGridView.windowAlignmentOffsetPercent:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(view.parent as? FrameLayout)?.run {
val myView = LayoutInflater.from(context).inflate(R.layout.my_view_to_add, this, false)
addView(myView)
}
verticalGridView?.let {
it.windowAlignmentOffsetPercent = 30.0f
}
}
This added view is static and won't scroll out when focus goes down.
Between rows it seems there is no way to add anything but ItemDecorator, because VerticalGridView is a descendant of RecyclerView

How to hide navigation Toolbar icon in xamarin?

I want to hide navigation bar button in xamarin. how can i do that using binding. Toolbar item doesn't have "IsVisible" property.
Following is my xaml code
please help me to sort out this issue.
I would suggest to build a bindable ToolBoxItem. That way you can control the visibility through a view model property.
An implementation could look like that:
public class BindableToolbarItem : ToolbarItem
{
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(BindableToolbarItem), true, BindingMode.TwoWay, propertyChanged: OnIsVisibleChanged);
public bool IsVisible
{
get => (bool)GetValue(IsVisibleProperty);
set => SetValue(IsVisibleProperty, value);
}
private static void OnIsVisibleChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var item = bindable as BindableToolbarItem;
if (item == null || item.Parent == null)
return;
var toolbarItems = ((ContentPage)item.Parent).ToolbarItems;
if ((bool)newvalue && !toolbarItems.Contains(item))
{
Device.BeginInvokeOnMainThread(() => { toolbarItems.Add(item); });
}
else if (!(bool)newvalue && toolbarItems.Contains(item))
{
Device.BeginInvokeOnMainThread(() => { toolbarItems.Remove(item); });
}
}
}
As you have discovered yourself there is not IsVisible. So you will have to implement functionality like that yourself if you still want it.
Another way would be to handle it in the pages' code-behind and remove or add the toolbar item whenever needed.
Adding and removing is simple, just add and remove items to the ToolbarItems collection: ToolbarItems.RemoveAt(0); for instance will remove the first toolbar item.
Putting #Gerald answer in action, it would be done this way:
void Done_Clicked(System.Object sender, System.EventArgs e)
{
//Do somthing and hide the done item
ShowDoneToolbarItem(false, (ToolbarItem)sender);
}
void Entry_Focused(System.Object sender, Xamarin.Forms.FocusEventArgs e)
{
//Show the done item
ShowDoneToolbarItem(true);
}
void ShowDoneToolbarItem(bool show, ToolbarItem item = null)
{
if(show)
{
ToolbarItem done = new ToolbarItem();
done.Text = "Done";
done.Clicked += Done_Clicked;
ToolbarItems.Add(done);
}
else if(item != null)
{
ToolbarItems.Remove(item);
}
}
This is cleaner and works from the code behind.
Well we need the IsVisible property for the front end, as xamarin doesn't have it, you can use Device.RuntimePlatform to check in real time which device the application is running. Since my code is in .cs of the XAML file, we can use xaml .cs to insert items into the screen.I put if () to do the logic and check if my device is on which platform, because I don't want it to display in UWP a toolbar.
The code is in .cs of the XAML file:
public kingTest()
{
InitializeComponent();
if((Device.RuntimePlatform == "Android")||(Device.RuntimePlatform == "iOS"))
{
ToolbarItem toolbar = new ToolbarItem();
toolbar.IconImageSource = "ic_ToolBar.png";
this.ToolbarItems.Add(toolbar);
}
};
I've achieved this easily using overloaded constructors. Here's an example:
View (add the name property):
<ContentPage x:Name="ContentPage"
<!-- rest of the tag -->
/>
Code-behind (add the toolbar items):
public partial class ExamplePage : ContentPage
{
public ExamplePage()
{
InitializeComponent();
BindingContext = this;
var saveToolbarItem = new ToolbarItem { Text = "Save" };
saveToolbarItem.Clicked += YourMethodToBeRan;
ContentPage.ToolbarItems.Add(saveToolbarItem);
}
public ExamplePage(Object object)
{
InitializeComponent();
BindingContext = this;
var updateToolbarItem = new ToolbarItem { Text = "Update" };
updateToolbarItem.Clicked += YourMethodToBeRan;
var deleteToolbarItem = new ToolbarItem { Text = "Delete" };
deleteToolbarItem.Clicked += YourMethodToBeRan;
ContentPage.ToolbarItems.Add(updateToolbarItem);
ContentPage.ToolbarItems.Add(deleteToolbarItem);
}
// rest of the class
}
The above pseudocode will add the "Save" toolbar item when the class is instantiated with no parameter, or the "Update" and "Delete" when a parameter is provided.
This isn't as elegant as IsEnabled / IsVisible booleans but it's a step in the right direction. Following this train of thought, you could modify the children of your toolbar during runtime to "show" and "hide" by adding and removing them as children.
Good luck!
I don't know if #tequila slammer's solution fully worked on Xamarin, but for us it only kind of works in .Net Maui (the evolution of Xamarin) and binding the IsVisible property to a variable.
Once the BindableToolbarItem is removed from the ContentPage's list of ToolbarItems, it is disconnected from the object that IsVisible is bound to forever.
For example: We want to use this control to hide or show a ToolbarItem that navigates to the admin screen, if I log in as the administrator on app launch, the item is there...great. If I then log out and log in as a non-admin, the item is not there...perfect. If I then log out and log in as an admin, the item is not there (the propertyChanged: OnIsVisibleChanged never fired)...:-(.
Not a big deal for us, if you want admin access then stopping the app and starting the app to log in as the admin is not a big ask.
In the newest release with .Net 7 the workaround works never more !
The reason is because the toolbar item which revomed will destoyed !

Event handler ordering in Xamarin

I have a list view that I am popping up in Xamarin forms, that I want to hide if someone taps outside of the box. I have a tap gesture recognizer on the parent layout for the list view that handles that. In Android, it all works good. If I click off, it closes, but if I click on an element in the list view, it properly selects it. In iOS, the opposite happens. The gesture handler on the layout fires first and closes the list view without properly selecting the item.
So my question, is there a way to change the order on how the events are fired? If not, is there a better alternative to how I'm trying to accomplish this? Thanks!
If you are using ListView.ItemSelected or ListView.ItemTapped then I ran into the exact same issue the other day. The fix for me was to not use either of those and instead attach a TapGestureRecognizer to the ViewCell that is within the ListView. I also added an IsSelected property to the object that the ViewCell is being bound to so that I could change the background color of the item once it has been clicked.
public class SomePage : ContentPage {
private SomeModel _selectedModel; //It would be best to put this into your ViewModel
...
public SomePage() {
ListView list = new ListView {
ItemTemplate = new DataTemplate(() => {
ViewCell cell = new ViewCell {
View = new ContentView()
};
cell.View.GestureRecognizers.Add(new TapGestureRecognizer {
Command = new Command(() => {
if(_selectedModel != null) { _selectedModel.IsSelected = false; }
SomeModel model = (SomeModel)cell.BindingContext;
model.IsSelected = true;
_selectedModel = model;
})
}
return cell;
}
}
}
}

Javafx ComboBox disappearing items after select

I have created a (JavaFX) combobox, which I am populating with an observable list made from HBoxes, so that I can display an image with some text in each list cell.
This displays well, other than the fact that whenever you select one of the items in the list, it will disappear. Once you have selected every item, it will not render any items at all. (You can still select them by clicking in the space where they previously were.
Do you know how I might correct this, please?
Part of my code is displayed below:
public class IconListComboBox {
Group listRoot = new Group();
VBox mainVBox = new VBox();
ComboBox selectionBox = new ComboBox();
List<HBox> list = new ArrayList<HBox>();
ListView<HBox> listView = new ListView<HBox>();
ObservableList<HBox> observableList;
public IconListComboBox(int dimensionX, int dimensionY, ArrayList<String> names, ArrayList<ImageView> icons)
{
//VBox.setVgrow(list, Priority.ALWAYS);
selectionBox.setPrefWidth(dimensionY);
selectionBox.setPrefHeight(40);
for(int i = 0; i < names.size(); i++)
{
HBox cell = new HBox();
Label name = new Label(names.get(i));
Label icon = new Label();
icon.setGraphic(icons.get(i));
name.setAlignment(Pos.CENTER_RIGHT);
icon.setAlignment(Pos.CENTER_LEFT);
icon.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(icon, Priority.ALWAYS);
cell.getChildren().add(icon);
cell.getChildren().add(name);
list.add(cell);
}
observableList = FXCollections.observableList(list);
listView.setItems(observableList);
listView.setPrefWidth(dimensionX);
selectionBox.setMaxWidth(dimensionX);
listView.setMaxWidth(dimensionX);
selectionBox.setItems(observableList);
mainVBox.getChildren().add(selectionBox);
mainVBox.getChildren().add(listRoot);
//mainVBox.getChildren().add(listView);
//listRoot.getChildren().add(listView);
}
Thanks in advance for your assistance!
Ok, so I've managed to work this out, thanks to #James_D 's very kind help!
This is for anyone who, like me, was slightly daunted by the example that was given in the Java documentation. (Although, my description below is probably worse!!)
So, I started by adding an HBox which was in the layout I wanted straight into the ComboBox... which is a bad idea!
So, before you go deleting everything you've done, save the HBox somewhere, and do the following:
1. Create a new class to hold your date (Image, and String) which will go into each cell. Make getters/setters to do this. I called mine IconTextCell.
2. Add the following code to the class where your ComboBox is located:
yourComboBox.setCellFactory(new Callback<ListView<T>, ListCell<T>>() {
#Override public ListCell<T> call(ListView<T> p) {
return new ListCell<T>() {
Label name = new Label();
Label icon = new Label();
private final HBox cell;
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
cell = new HBox();
//HERE, ADD YOUR PRE-MADE HBOX CODE
name.setAlignment(Pos.CENTER_RIGHT);
icon.setAlignment(Pos.CENTER_LEFT);
icon.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(icon, Priority.ALWAYS);
cell.getChildren().add(icon);
cell.getChildren().add(name);
}
#Override protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
name.setText(item.getLabel());
icon.setGraphic(item.getIcon());
setGraphic(cell);
//HERE IS WHERE YOU GET THE LABEL AND NAME
}
}
};
}
});
You'll see that the main content is very similar to what I had already produced, so no code is lost.
Just replace "T" with your own class for representing a cell.
3. This will display your icon and string in the list, but you need to to also be displayed in the button (the grey top selector part of the combobox, aka the button). Do do this, we need to add the following code:
class IconTextCellClass extends ListCell<T> {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getLabel());
}
}
};
selectionBox.setButtonCell(new IconTextCellClass());
...and that's how I did it. I hope this helps - please compare this to my original post. The actual content (where I create the HBox etc) is obviously not generalised. You can make this as simple or as complex as you want.
Once again, thanks for your help! I hope this post helps others!
This is exactly the example cited in the documentation under "A warning about inserting Nodes into the ComboBox items list".
The list of items in the combo box should represent data - not the UI component used to display the data. The issue is that the HBox cannot appear twice in the scene graph: so it cannot appear both in the "selected cell" and as a cell in the drop-down list.
Instead, create a class that represents the data you are displaying in the ComboBox, and use a cell factory to instruct the ComboBox as to how to display those data. Be sure to set a button cell as well (the cell used for the selected item).

Resources