Xamarin.Forms: Access layout individually when it has a binding context - xamarin.forms

I have a layout with a few elements that get layout binding:
<Grid Grid.Column="1" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label
HorizontalOptions="Start"
VerticalOptions="End"
FontFamily="arial"
FontSize="18"
TextColor="#151515"
FontAttributes="Bold"
Grid.Row="0"
Text="{Binding title}"
/>
<Label
HorizontalOptions="Start"
VerticalOptions="Start"
FontFamily="arial"
FontSize="13"
TextColor="#151515"
Grid.Row="1"
Text="{Binding subtitle}"
/>
</Grid>
So I am giving it mulitple objects with different contents set on a variable with the same name and the result is that all layout get inflated with different contents. That works fine.
However, there is a little notification icon to the right of each layout:
<Grid Grid.Column="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ImageButton
VerticalOptions="Center"
HorizontalOptions="Center"
Source="img_notify.png"
Grid.Column="1"/>
<!-- BackgroundColor="#00000000"-->
<Label
HorizontalOptions="Center"
VerticalOptions="Center"
FontFamily="arial"
FontSize="18"
TextColor="#ffffff"
FontAttributes="Bold"
Grid.Column="1"
Text="!"
/>
</Grid>
It looks like this:
Now, once the user clicked on one of those elements, the notify button is just supposed to dissapear, since the user has already seen it.
Therefor I set up a database that stores the name and the subtitle of the ad if clicked and the next time the list becomes visible, each object is checked:
private async void ReturnDataBaseToListAndUpdateNotifyButton(List<ListView_Matches> contentOfListView)
{
List<db_MatchesNotify_Type> contentOfDatabase = await Database.GetIDsAlreadyClickedOn();
foreach (var o in contentOfListView) // for each match in our list...
{
for(var i = 0; i < contentOfDatabase.Count; i++) //... check every object in our database if they match
{
if (o.title == contentOfDatabase[i].titleOfAd && o.userID == contentOfDatabase[i].userID) // check if matches then delete icon
{
}
}
}
}
.. by using nested looping.
Now I know that my database is working fine and that my logic is OK, but u see that in within the last "if" statement, nothing is happening since I dont know how to adress the notification button now.
Bascially, in within the IF (if a match between the data from the list and the data from the database is found) i would need to set the notifybutton to invisible.
But 1.) I cannot access the object by name since the property from a list is not transported to the class but most:
2.) even if I just set it to invisible then ALL the notifybuttons in my list would become insivible. Ofc I only want this particular object on which a match was found to become invisible.
I need to access the layout that has been inflated individually and decided for each new button now wheater it is visible or not.
How can I achieve that?
Thank you!

You do it the same way you have set your Labels' bindings. The idea is that this ImageButton will always be in the layout and you will simply toggle its IsVisible property with a binding.
<ImageButton
IsVisible="{Binding IsNotifyIconVisible}"
VerticalOptions="Center"
HorizontalOptions="Center"
Source="img_notify.png"
Grid.Column="1"/>
You haven't specified anywhere what your collection's binding context is, but I assume it is some sort of an IEnumerable<YourItemViewModel>. In YourItemViewModel.cs, besides title & subtitle, you will have a property IsNotifyIconVisible and that way you will be able set it to true/false on demand.

Related

Xamarin CollectionView event and command issue

I work on the front end of our app, configuring the UI. Our app originally had a ListView using Xamarin Community Toolkit's EventToCommandBehavior with custom ViewCells to facilitate the search. We set up custom renderers to change the ViewCell's background color. However, when I moved over to the Mac, we discovered that none of that worked with iOS; the cell background would not change.
At this point, I switched to a CollectionView with a TapGestureRecognizer on the grid using VisualStateManager to change the background color. Since then, I can either get the grid selection to highlight without the command enabling our buttons or the command to fire which enables the buttons, but the grid selection will not highlight. Simply put, I either get the command or the event but have been unable to get them working together. This is also a problem on our store page; the IAP's never highlighted before the purchase confirmation popped up. We have been experiencing this issue from the start and never noticed.
Here is the relevant XAML:
<!--Faction Search Results-->
<Frame Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="6" BackgroundColor="Black" BorderColor="White" CornerRadius="5">
<CollectionView ItemsSource="{Binding FactionItems}"
BackgroundColor="Transparent">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid x:DataType="Data:Faction" BackgroundColor="Black">
<Grid.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:FactionTopPageViewModel}}, Path=ItemTapped}"
CommandParameter="{Binding .}">
</TapGestureRecognizer>
</Grid.GestureRecognizers>
<Grid.RowDefinitions>
<RowDefinition Height="{markups:OnScreenSize DefaultSize='12', iPod='20', iPhoneSE='20', iPhoneXR='20', iPhoneX='20', iPhone13='25', iPhone7p='25', iPhone11pm='25', iPhone13pm='16',
iPadMini='35', iPad9p7='35', iPad='35', iPadAir='35', iPad11='35', iPad12p9='40', Nexus1='20', NexusR='25', Nexus7R='30', Pixel2R='25', Pixel3R='25', GalaxyS8='25', Nexus6P='25', Pixel3XL='25', PixelC='40'}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Padding="10,5" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" Text="{Binding FactionName}" HorizontalOptions="Start" VerticalTextAlignment="End" FontAttributes="Bold"
FontSize="{markups:OnScreenSize DefaultSize='12', iPod='15', iPhoneSE='15', iPhoneXR='18', iPhoneX='19', iPhone13='19', iPhone7p='19', iPhone11pm='19', iPhone13pm='16',
iPadMini='30', iPad9p7='30', iPad='32', iPadAir='32', iPad11='34', iPad12p9='34', Nexus1='14', NexusR='14', Nexus7R='22', Pixel2R='20', Pixel3R='20', GalaxyS8='20', Nexus6P='18', Pixel3XL='18', PixelC='34'}" />
<Label Padding="0,5" Grid.Row="0" Grid.Column="4" Grid.ColumnSpan="2" Text="{Binding NumberMembersString}" HorizontalOptions="Center" VerticalTextAlignment="End" FontAttributes="Bold"
FontSize="{markups:OnScreenSize DefaultSize='12', iPod='15', iPhoneSE='15', iPhoneXR='18', iPhoneX='19', iPhone13='19', iPhone7p='19', iPhone11pm='19', iPhone13pm='16',
iPadMini='30', iPad9p7='30', iPad='32', iPadAir='32', iPad11='34', iPad12p9='34', Nexus1='14', NexusR='14', Nexus7R='22', Pixel2R='20', Pixel3R='20', GalaxyS8='20', Nexus6P='18', Pixel3XL='18', PixelC='34'}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal"></VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#ae00ff"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Relevant code from the ViewModel:
public Command<Faction> ItemTapped { get; }
public FactionTopPageViewModel()
{
ItemTapped = new Command<Faction>(OnItemSelected);
}
private void OnItemSelected(Faction faction)
{
if (faction == null)
{
SelectedItem = null;
DisableFactionButtons();
return;
}
EnableFactionButtons();
SelectedItem = faction;
}
This results in our buttons enabled at the bottom of the page, but the faction (essentially a team) is not being highlighted in the CollectionView, so the user does not receive confirmation of selecting the proper faction.
If I remove the tapgesture and work through the CollectionView commands setting the SelectionMode="Single", SelectionChangedCommand={Binding IsTapped}, and SelectionChangedCommandParameter={Binding .} then the selected Faction highlights without enabling any buttons. These issues are not platform-specific, like the ListView ViewCell background.
I have been working on this longer than I care to mention, as it seems like I have not wrapped my head around some relatively simple concept. Any help would be greatly appreciated, this issue is holding up the next release of our app.

Xamarin's forms Date Picker android icon a bit out of way

I am trying to place a synfcusion button that has a calendar image near a xarmain forms entry box.
But its a bit out of alignment what I have tried so far is.
<Label Text="Please select a start date below"></Label>
<StackLayout Orientation="Horizontal">
<Entry x:Name="txtDateStartEntry" WidthRequest="200" ></Entry>
<syncfusion:SfButton x:Name="btnPickStartDate" CornerRadius="10" HorizontalTextAlignment="Start" ShowIcon="True" Clicked="btnPickStartDate_Clicked" FontSize="24" BorderThickness="2" TextColor="White" BackgroundColor="White" Text="..." >
<syncfusion:SfButton.Content>
<Grid ColumnDefinitions="Auto, *, Auto" ColumnSpacing="16" Padding="16">
<Image Grid.Column="1" HorizontalOptions="End" WidthRequest="30" HeightRequest="30" Source="cal16.png" VerticalOptions="End"/>
</Grid>
</syncfusion:SfButton.Content>
</syncfusion:SfButton>
</StackLayout>
What I have eneded up with is this.
As you can see its a bit far away from the entry field and doesnt look like it belongs.
If you're using Syncfusion, you should make use of SfTextInputLayout which provides leading/trailing view functionality for the express purpose of adding descriptive iconography to an input view.
Code example assumes importing the following xmlns:
xmlns:sftl="clr-namespace:Syncfusion.XForms.TextInputLayout;assembly=Syncfusion.Core.XForms"
<sftl:SfTextInputLayout TrailingViewPosition="Inside">
<Entry x:Name="txtDateStartEntry" WidthRequest="200" />
<sftl:SfTextInputLayout.TrailingView>
<Image HeightRequest="30" Source="cal16.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="btnPickStartDate_Clicked" />
</Image.GestureRecognizers>
</Image>
<sftl:SfTextInputLayout.TrailingView>
</sftl:SfTextInputLayout>
Note: when using LeadingView or TrailingView, it's recommended to not assign a Width value as Syncfusion will calculate the width based on the height you request to maintain aspect ratio of the specified view. Therefore I have intentionally excluded HeightRequest from the Image.

Copy attached properties to new instance in xamarin forms

I am creating new instance of grid object which have two labels as following.
<Grid x:Name="Grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70*"></ColumnDefinition>
<ColumnDefinition Width="30*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Label x:Name="Label1" Text="Label1" Grid.Column="0" Grid.Row="0"></Label>
<Label x:Name="Label2" Text="Label2" Grid.Column="1" Grid.Row="0"></Label>
</Grid>
I am using Activator.CreateInstance method as given below:
Grid ClonedView =(Grid) Activator.CreateInstance(Grid.GetType());
foreach (PropertyInfo propertyInfo in Grid.GetType().GetProperties())
{
if (propertyInfo.CanRead && propertyInfo.CanWrite )
{
object PropertyValue = propertyInfo.GetValue(Grid, null);
propertyInfo.SetValue(ClonedView, PropertyValue);
}
}
When I use this ClonedView in my page, it Cloned Grid don't understand that Label2 should appear in second column of grid. I did a lot of research on it but could not find any solution. Please let me know if anybody has any solutions.
thanks
They way you're thinking about this, feels very JavaScript like to me. You want to create a deep copy by copying all properties. However the Xamarin objects are way too complex for something like this (even if you where able to do a deep copy, Bindings for example won't work with simple copying).
So instead of making a single Grid object and trying to create copies of it, another way of looking at it would be "I want to define a template, and then create multiple instances according to that template". For this Xamarin has the DataTemplate class, which enables us to do something like this in xaml:
<ContentPage>
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyGridTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70*"></ColumnDefinition>
<ColumnDefinition Width="30*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Text="Label1" Grid.Column="0" Grid.Row="0"></Label>
<Label Text="Label2" Grid.Column="1" Grid.Row="0"></Label>
</Grid>
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<Button Text="Add Grid" Clicked="AddGridClicked">
</Button>
<StackLayout x:Name="GridHolder">
<StackLayout>
<ContentPage>
And in our code behind we can implement AddGridClicked like this:
private void AddGridClicked(object sender, EventArgs e)
{
var myGridTemplate = (DataTemplate) Resources["MyGridTemplate"];
GridHolder.Children.Add((View)myGridTemplate.CreateContent());
}
Now the downside is that you need to define a DataTemplate instead of just pointing at an object and saying getting a copy. Also keep in mind that x:Name of a View in the DataTemplate can't be accessed in code behind of your page, but with a good Xamarin setup that should not be needed. The upside is that this works, complete with possible bindings, so you're allowed to define a Label in your grid like this:
<Label Text="{Binding Name}" Grid.Column="0" Grid.Row="0"></Label>
And then you can give each instance if your templated Grid a different BindingContext to bind different data to it's views.
Hope this helps

PopUp to contains other controls like DateTime Picker,dropdownlist

newbie here. I remember in Win10, there is a PopUp UI which can contains many controls like Dropdown box, DatePciker and others. This PopUp can be constructed in Xaml.
I am developing an app for Tablet.
It is possible to use such PopUp in Xamarin forms which contains Dropdown List, DatePicker, Time Picker? It is advisable to do such complex UI ? will it cause problem in Android and iOS?
Problem : How to create PopUp in Xaml
so ,I can use a button to open or close it.
var button = new Button {Text = "Show Popup"};
button.Clicked += (s, e) => popup.Show();
Update
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.View.PopUpDemo"
Title="PopUp Demo"
Icon="itemIcon3">
<Grid>
<Grid>
<!--Your Page xaml-->
<Button x:Name="btnPopUp" Click="ShowPopUp"/>
</Grid>
<Grid HorizontalOptions="Fill" BackgroundColor="##60000000" IsVisible="{Binding GridVisibility}">
<Grid VerticalOptions="CenterAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width="9*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<Frame Grid.Column="1" Padding="0" HasShadow="True" OutlineColor="#e6e6e6">
<Grid BackgroundColor="White" RowSpacing="0" ColumnSpacing="0">
<!--content for popup-->
</Grid>
</Frame>
</Grid>
</Grid>
</Grid>
</ContentPage>
I would like to know :
I am using a normal ContentPage.
1) In it, i place your MarkUp <Grid> ,I remove <ContentPage.Content>. Is this correct?
2) Is Frame will be the PopUp ?
3) what code to use in btnPopUp to show the PopUp?
Thanks
Yes you can achieve it without any performance issue. Either you can use an overlay to show pop up as shown below
<Grid>
<Grid>
<!--Your Page xaml-->
</Grid>
<Grid HorizontalOptions="Fill" BackgroundColor="##60000000" IsVisible="{Binding GridVisibility}">
<Grid VerticalOptions="CenterAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width="9*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<Frame Grid.Column="1" Padding="0" HasShadow="True" OutlineColor="#e6e6e6">
<Grid BackgroundColor="White" RowSpacing="0" ColumnSpacing="0">
<!--content for popup-->
</Grid>
</Frame>
</Grid>
</Grid>
</Grid>
This one is quite simple. Here i have added grid to achieve this. You can also use other layouts. But for performance grid is better as per xamarin team.
For the second grid i have added Visibility property so that you can wire up with any event.
Here i have added backgroundcolor for popup ##60000000 so that when popup appears background will be transparent with light black color effect.
If you need to have same pop up in multiple screens you can use 3rd party plugin https://github.com/rotorgames/Rg.Plugins.Popup which will make you work easier. For this you have to make a new contentpage, replace the contentpage with
<pages:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup"
x:Class=""
BackgroundColor="##60000000"
InputTransparent="True"
HasSystemPadding="True"
CloseWhenBackgroundIsClicked="False">
<pages:PopupPage.Animation>
<animations:MoveAnimation
PositionIn="Bottom"
PositionOut="Bottom"/>
</pages:PopupPage.Animation>
<Grid>
<!--your xaml code-->
</Grid>
Note:Replace contentPage with PopupPage in youpage.xaml.cs. To get popup effect your layout should be like 1st example i shown. Here you can also get nice animation effects. More details you can see in their site.
Now you can call the pop up by calling:navigation.PushPopupAsync(new YourContentPage());
And remove the popup by calling await PopupNavigation.RemovePageAsync(this);
Hope this will be useful for you

WP7 Using binding how can I manage data relevant on prior items

When I am binding to a collection with a listbox, is there any way I can manage the output based upon the prior items that were displayed in the collection.
For example in the following binding
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="AgendaListbox"
ItemsSource="{Binding AgendaItems2}"
ItemTemplate="{StaticResource EventDisplay3}"/>
</Grid>
Where the Template is as follows
<DataTemplate x:Key="EventDisplay3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DateTimeDayString}" Style="{StaticResource PhoneTextSmallStyle}"
Grid.Row="0" Grid.Column="0"/>
<TextBlock Text="{Binding DisplayTimeString}" Foreground="{Binding DisplayColor}"
Grid.Row="1" Grid.Column="0" Style="{StaticResource PhoneTextLargeStyle}"/>
<TextBlock Text="{Binding Details}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"
Grid.Row="0" FontSize="30" Grid.Column="2" Grid.RowSpan="3"
VerticalAlignment="Center" />
<TextBlock Text="{Binding Location}" Style="{StaticResource PhoneTextSmallStyle}"
Grid.Row="3" Grid.Column="0"/>
</Grid>
</DataTemplate>
If I want to alter the display based upon they previous item that was displayed (e.g say I want to drop the DateTimeString Binding if its identical to the previous one) is there a way that I can do it without having to make specific allowance for it when I build up the collecton.
Because so much happens automatically as it were with the listbox being bound to that collection I cannot see any other way without me handling this as the collection is being built... because after that I have little control.
Thanks
Is there any way I can manage the output based upon the prior items that were displayed in the collection?
No.
You have to do such changes when you build the collection. Consider if using a DataTemplateSelector can be of any help.

Resources