I have an Xamarin Content Page with a List. For the ListItems I want something similar to the cardview in Android.
Base on what I found that could be accomplished by a Frame. I have this code:
<ViewCell>
<StackLayout Padding="8" >
<controls:CardView HasShadow="True" OutlineColor="LightGray">
<StackLayout Orientation="Vertical" Padding="5">
// Some labels and Buttons
</StackLayout>
</Frame>
</StackLayout>
</ViewCell>
The CardView has the following code:
public class CardView : Frame
{
public CardView()
{
Padding = 0;
if (Device.RuntimePlatform == Device.iOS)
{
HasShadow = false;
OutlineColor = Color.Transparent;
BackgroundColor = Color.Transparent;
}
}
}
This results in a something like this:
This looks more like a Border than this card levitation effect.
The example above actually uses the same cardview control, without any styles (even without the OutlineColor). Do I miss a option I have to configure? Or how could I achieve the same result as in the sample?
Xamarin.Forms Version: 2.5.0.280555
I have implemented something very similar (also Frames as cards to be displayed in a stack view). Unfortunately I can't share the exact code, for it's not me owning it, but my employer, but I can tell you how to achieve this.
I have added a property ShadowRadius to CardView and created a custom renderer, derived from Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer. In the renderer I am setting the Elevation of the renderer
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
/* ... */
this.Elevation = ((CardView)e.NewElement).ShadowRadius;
}
My cards are showing a nice elevation shadow with Xamarin.Forms 2.5.0.280555.
You can try this
<Frame HasShadow="True">
<controls:CardView OutlineColor="LightGray">
<StackLayout Orientation="Vertical" Padding="5">
// Some labels and Buttons
</StackLayout>
</controls:CardView>
</Frame>
Related
Currently, I have updated Xamarin.Forms version in my project to 5.0.0.2083. After that, I am facing some weird issues in CarouselView control. I have used Carousel and IndicatorView controls in one place, and the issue I am facing is IndicatorView is not working on an Android device.
It's working fine in iOS, the issue comes only from Android. To fix this problem I have tried to use the PositionChanged event of CarouselView but that also works in iOS only and not in Android. I have tried many things but none of them worked.
Here is my code:
<CarouselView x:Name="creditCardsList"
ItemsSource="{Binding CardList}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Loop="False"
HorizontalScrollBarVisibility="Never"
Margin="20,0"
IsScrollAnimated="True"
ItemsUpdatingScrollMode="KeepItemsInView"
Position="{Binding CarouselPosition}"
HeightRequest="{StaticResource HeightWidth160}"
PositionChanged="creditCardsList_PositionChanged"
IndicatorView="CardIndicatorView">
<CarouselView.ItemTemplate>
<DataTemplate>
<!--UI that I want to display-->
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView x:Name="CardIndicatorView"
VerticalOptions="End"
Margin="{DynamicResource MarginPadding00_10_00_10}"
IndicatorColor="Gray"
SelectedIndicatorColor="{StaticResource action_button_color}"
IndicatorsShape="Square"
ItemsSource="{Binding CardList}"/>
Does anyone know any workaround for this? I just want to change Indicators as I swipe items in CarouselView. Any help would be appreciated!
I test with Xamarin.Forms 5.0.0.2083 on Android , IndicatorView and event PositionChanged works fine ,see the following screenshot below.
Update
Here is my testing code
xaml
<StackLayout>
<CarouselView x:Name="creditCardsList"
ItemsSource="{Binding CardList}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Loop="False"
HorizontalScrollBarVisibility="Never"
Margin="20,0"
IsScrollAnimated="True"
ItemsUpdatingScrollMode="KeepItemsInView"
HeightRequest="200"
PositionChanged="creditCardsList_PositionChanged"
IndicatorView="CardIndicatorView">
<CarouselView.ItemTemplate>
<DataTemplate>
<Image Source="dog.png"/>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView
x:Name="CardIndicatorView"
VerticalOptions="End"
Margin="20"
IndicatorColor="Gray"
SelectedIndicatorColor="Red"
IndicatorsShape="Square"
ItemsSource="{Binding CardList}"/>
</StackLayout>
code behind
public class Model
{
public List<string> CardList { get; set; }
}
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
Model model = new Model();
model.CardList = new List<string> { "1", "2", "3" };
this.BindingContext = model;
}
private void creditCardsList_PositionChanged(object sender, PositionChangedEventArgs e)
{
}
}
I have xamarin page that shows list of products and a picker which represents type of product.
My problem is that when i start application and try to access page for the first time,
in debugging mode i can see that list i am using as ItemsSource has value,
But when page is loaded picker is greyed out and doesnt have any data.
When I leave page with picker and open it up second time, picker is loaded with data!
Here is my code!
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Converters="clr-namespace:eProdaja.Mobile.Converters"
mc:Ignorable="d"
x:Class="Restoran.Mobile.Views.ProizvodiPage">
<ContentPage.Resources>
<ResourceDictionary>
<Converters:ImageConverter x:Key="imgConv"></Converters:ImageConverter>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<Picker ItemsSource="{Binding TipProizvodaList}" ItemDisplayBinding="{Binding Naziv}" SelectedItem="{Binding SelectedTipProizvoda}"></Picker>
<ListView ItemsSource="{Binding ProizvodiList}" ItemSelected="ListView_ItemSelected" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10" Margin="5" HorizontalOptions="CenterAndExpand" >
<Image Source="{Binding Slika, Converter={StaticResource imgConv}}" ></Image>
<Label Text="{Binding Naziv}"
d:Text="{Binding .}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
<Button HorizontalOptions="Center" BorderColor="Transparent" BackgroundColor="Transparent" TextColor="OrangeRed" Text="Dodaj u košaricu"></Button>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
Code inside of xaml.cs
private ProizvodiViewModel model = null;
public ProizvodiPage()
{
InitializeComponent();
BindingContext = model = new ProizvodiViewModel();
}
protected async override void OnAppearing()
{
base.OnAppearing();
await model.Init();
}
Code inside of viewModel
public ObservableCollection<TipProizvoda> TipProizvodaList { get; set; } = new ObservableCollection<TipProizvoda>();
public async Task Init()
{
if (TipProizvodaList.Count == 0)
{
var TPList = await _tipProizvoda.Get<List<TipProizvoda>>(null);
TipProizvodaList.Clear();
TipProizvoda empty = new TipProizvoda { TipProizvodaID = 0, Naziv = "" };
TipProizvodaList.Add(empty);
foreach (var tipProizvoda in TPList)
{
TipProizvodaList.Add(tipProizvoda);
}
}
}
Over the years, I've had various problems attempting to do ANYTHING in OnAppearing that affects what is seen on screen.
There must be something Xamarin does AFTER OnAppearing, that gets the page into a valid state to receive binding changes.
One way around this limitation is to delay the work that you want done, so that OnAppearing returns before your work sets the bindings.
The "downside" of this, is that the page will appear (at first) without your work. See the two references to "activityIndicator" - those are where you control what you want user to see before your work is ready.
The "upside" of this, is that it ensures Xamarin "sees" your binding changes. (It also provides a place to do slow background work, if that is needed.)
Try this:
public partial class MyPage : ...
{
protected override void OnAppearing()
{
base.OnAppearing();
This returns immediately, allowing OnAppearing to return.
DelayWork(100, BackgroundWork, UIWork);
}
// Custom class, used to pass results from background work to UI work.
class BackgroundResult {
...
}
private void DelayWork(int milliseconds, Func<BackgroundResult> backgroundWork, Action uiWork)
{
//OPTIONAL activityIndicator.IsRunning = true;
Task.Run( () => {
// The delay ensures Xamarin page preparation has a little time, before your work begins.
// Without this delay, under some circumstances, the page might not show up as quickly.
// You might not need this.
Task.Delay(milliseconds);
// Slow work -- do nothing that affects UI.
BackgroundResult backgroundResult = BackgroundWork();
Device.BeginInvokeOnMainThread(async () => {
await uiWork(backgroundResult);
});
});
}
private BackgroundResult BackgroundWork()
{
// Slow work -- do nothing that affects UI.
...
// fill this with whatever info you need to pass to UIWork.
var backgroundResult = new BackgroundResult();
// ...
return backgroundResult;
}
private async void UIWork(BackgroundResult backgroundResult)
{
// Work that affects UI, possibly via Bindings.
await model.Init();
//OPTIONAL activityIndicator.IsRunning = false;
}
}
In your situation, you might not need BackgroundWork nor BackgroundResult. Shown for completeness.
I am creating autocomplete textview in xamarin forms by this using this sample. But in this example they are using AutoCompleteTextView in mainActivity.cs onCreate(). How could I use the same code in my xaml forms
CODE IS:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "Main" layout resource
SetContentView (Resource.Layout.Main);
AutoCompleteTextView textView = FindViewById<AutoCompleteTextView> (Resource.Id.autocomplete_country);
var adapter = new ArrayAdapter<String> (this, Resource.Layout.list_item, COUNTRIES);
textView.Adapter = adapter;
}
Thank you.
You can't use Android samples (including Xamarin.Android) directly in Xamarin.Forms. Those are different technologies.
Xamarin.Forms can also runs on iOS (even if you don't want it to) and iOS won't run Android specific code, that's the main reason why it is impossible.
Other than that you may build Xamarin.Forms controls from native controls using custom renderers. So basically you could make the above sample work on Xamarin.Forms, but with lot of knowledge and hard work, a lot more code is needed than the sample above.
I did something like this in my Xamarin.Forms project, which was 100% shared by Android and iOS - no custom renderers. This example uses the MVVM pattern with some use of the code-behind. My view model contains an IList of object which the ListView binds to. It is updated when the user presses three or more characters with a query expression.
private IList<myObject> results;
public IList<myObject> QueryResults
{
get
{
return results;
}
set
{
SetProperty(ref results, value);
}
}
Within the OnTextChanged handler I run the query expression and populate QueryResults on every key stroke. This might not be that efficient, but it performs well.
So the view mark up and code-behind is something like this:
<StackLayout>
<Label Style="{StaticResource formLabel}" Text="My Searchable List" />
<!-- from poc -->
<Grid WidthRequest="550">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="AUTO" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" />
</Grid.ColumnDefinitions>
<Frame Grid.Row="0" Style="{StaticResource InputFrame}" HeightRequest="49">
<Entry x:Name="SearchText" Style="{StaticResource formLabel}" WidthRequest="550" HeightRequest="45" Text="{Binding SearchString, Mode=TwoWay}" TextChanged="Handle_TextChanged" />
</Frame>
<StackLayout Grid.Row="1" Orientation="Horizontal">
<Frame x:Name="MyObjectFrame" Style="{StaticResource InputFrame}" HeightRequest="{Binding ListHeight}" HasShadow="true">
<ListView x:Name="MyObjectList" ItemsSource="{Binding ResultantMyObjects}" ItemSelected="OnItemSelected" HeightRequest="{Binding ListHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Frame>
</StackLayout>
with the Handle_TextChanged in the code-behind:
void Handle_TextChanged(object sender, Xamarin.Forms.TextChangedEventArgs e)
{
if (e.NewTextValue.Length > 2)
{
(BindingContext as SampleDetailsViewModel).MySIteSearchResults(e.NewTextValue);
this.ForceLayout();// force the change in heightrequest for ListView
MyObjectList.IsVisible = true;
MyObjectFrame.IsVisible = true;
}
else
{
MyObjectList.IsVisible = false;
MyObjectFrame.IsVisible = false;
}
}
In the view model, I update the property above, note the return value really isn't used in this not-yet-perfect example:
public List<MyObjectDTO> MyObjectSearchResults(string keystrokes)
{
//TODO: encapsulate in a View
List<MyObjectDTO> searchResults = null;
IEnumerable<MyObjectDTO> queryResults;
queryResults = from site in MyObjects
where site.Name.ToLower().Contains(keystrokes.ToLower())
select site;
if (string.IsNullOrEmpty(SearchString))
{
QueryResults = MyObjects;
}
else
{
QueryResults = queryResults.ToList<MyObjectDTO>();
ListHeight = QueryResults.Count * 45; //TODO: detect size. magic number of 45 limits the height.
}
return searchResults;
}
NOTE: There might be some errors, as I had to scrub some code.
I'm making an app that scans for BLE devices and displays them in a ListView.
The problem is that when I once it displays the devices once but if I scan twice it result the devices twice etc.
This is the code what I got so far. In Mainpage.cs I create an ObservableCollection where I store the devices and which I clear everytime I call the scan methode.
<StackLayout Margin="15,15,10,15" >
<Label Text="Paired Devices" HorizontalOptions="Center" FontSize="Subtitle"/>
<ListView x:Name="paired" ItemsSource="{Binding deviceList}" ItemTapped="Paired_ItemTapped" BackgroundColor="White" SeparatorColor="Black">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Mainpage.cs
private void Button_Clicked(object sender, EventArgs e)
{
if(ble.State == BluetoothState.Off)
{
DisplayAlert("Bluetooth", "Please turn on bluetooth", "OK");
}
deviceList.Clear();
adapter.DeviceDiscovered += (s, a) =>
{
if (a.Device.Name != null)
{
deviceList.Add(a.Device);
}
Console.WriteLine(deviceList.Count);
};
if (!ble.Adapter.IsScanning)
{
adapter.StartScanningForDevicesAsync();
}
}
You have not shown the entire code here which you should have. So without that this is simply conjecture. The most probable cause to me of this occurring is you are adding the values to deviceList without removing the previous items.
Make sure when you are adding the items to deviceList to remove the old items in your Collection by either instantiating a new object of your Collection or by deleting unwanted (old) items from your Collection before adding any new items to it.
I have a <List> that adds and removes elements by calling a modal from a <Button> as shown in the figure.
When lifting the modal, I show the user the elements that can be associated to the main list and also indicate with a <Switch> if these elements are added or not, with the objective that when pressing the switch add or remove elements from the main list
The problem is that when the modal is lifted, the method that adds and deletes elements to the list is executed and every time I raise the modal, my records are duplicated, as shown in the figure
This is due to the fact that when lifting the modal, the method that adds or removes chemicals is executed, this is called every time the property value that is binded to the <Switch> in the view changes
Why is this happening? How can I avoid it?
Then I present my code ...
MODAL.XAML:
<StackLayout
BindingContext="{Binding AgregarSustancia}">
<ListView
ItemsSource="{Binding ListaSustancias}"
SelectedItem="{Binding SelectedSustancia}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<StackLayout Orientation="Vertical">
<Label Text="{Binding NombreSustancia}"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"/>
</StackLayout>
<Switch
OnColor="{StaticResource das.color.verde}" HorizontalOptions="EndAndExpand"
VerticalOptions="Start"
IsToggled="{Binding SustanciaAsociada, Mode=OneWay}">
</Switch>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout/>
AGREGARSUSTANCIA.CS:
bool sustanciaAsociada;
[JsonProperty(PropertyName = "chemicalIsAssociateWithInstallation")]
public bool SustanciaAsociada
{
get
{
return sustanciaAsociada;
}
set
{
if (value != sustanciaAsociada)
{
sustanciaAsociada = value;
AsociarDesasociar(sustanciaAsociada);
}
}
}
//METHOD THAT ADDS OR ELIMINATES DEPENDING ON THE VALUE OF THE PARAMETER
private async void AsociarDesasociar(bool sustanciaAsociada)
{
//ADD TO LIST
if (sustanciaAsociada)
{
}
else //REMOVE TO LIST
{
}
}
Then my ViewModel that fills the modal list
VIEWMODEL.CS: (MODAL)
#region Constructor
public AgregarSustanciaViewModel(ObservableCollection<AgregarSustancia> listaAgregarSustancia)
{
navigationService = new NavigationService();
ListaSustancias = new ObservableCollection<AgregarSustancia>();
listaSustanciasAux = new List<AgregarSustancia>(listaAgregarSustancia);
ListaSustancias = listaAgregarSustancia;
}
#endregion
How can I prevent the method AsociarDesasociar() in the Get-Set property of the Switch from executing the modal? How can I encapsulate this method?
Any help for me?
create a bool InitComplete and initialize it to false. This will prevent AsociarDesasociar from executing before initialization is complete
if (value != sustanciaAsociada)
{
sustanciaAsociada = value;
if (InitComplete) {
AsociarDesasociar(sustanciaAsociada);
}
}
after your class has finished whatever initialization is required, then set InitComplete = true