Color converter binding not being called - xamarin

I have a label with black text color inside a frame with white background color, the thing is, I want to assign the background color and text color from the viewmodel, I have created the converter, and did the bindings, but for some reason it isn't working
this is my view 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"
x:Class="ComanderoMovil.Views.PlatillosView"
xmlns:ios="clr -namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
xmlns:behaviorsPack="clr- namespace:Xamarin.Forms.BehaviorsPack;assembly=Xamarin.Forms.Behaviors Pack"
xmlns:converterPack="clr-namespace:ComanderoMovil.Converters">
<ContentPage.Resources>
<ResourceDictionary>
<converterPack:ColorConverter x:Key="ColorConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<SearchBar> </SearchBar>
<CollectionView ItemsSource="{Binding Grupos}"
HeightRequest="50"
ItemsLayout="HorizontalList"
SelectionMode="Single"
SelectionChangedCommand="{Binding
SelectedGrupoCommand, Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView Padding="2">
<Frame BorderColor="Black"
HasShadow="False"
Padding="2"
BackgroundColor="{Binding
ButtonBackColor, Converter={StaticResource ColorConverter}}">
<StackLayout>
<Label Margin="10"
Text="{Binding nombre}"
TextColor="{Binding
TextColor, Converter = {StaticResource ColorConverter}}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="Small"
VerticalOptions="CenterAndExpand"></Label>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage.Content>
</ContentPage>
Here is my ViewModel:
public class PlatillosViewModel : INotifyPropertyChanged
{
private INavigation Navigation;
private ObservableCollection<PlatilloModel> _platillos;
private string _textColor;
private string _backColor;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<GrupoModel> Grupos { get; set; }
public ObservableCollection<PlatilloModel> Platillos
{
get => _platillos;
set
{
_platillos = value;
OnPropertyChanged();
}
}
public string TextColor
{
get => _textColor;
set
{
_textColor = value;
OnPropertyChanged();
}
}
public string ButtonBackColor
{
get => _backColor;
set
{
_backColor = value;
OnPropertyChanged();
}
}
public PlatillosViewModel(INavigation navigation)
{
Navigation = navigation;
TextColor = "Black";
ButtonBackColor = "White";
PlatillosRepository repository = new PlatillosRepository();
Platillos = repository.GetAll();
GrupoRepository grupoRepository = new GrupoRepository();
Grupos = grupoRepository.GetAll();
}
public ICommand SelectedPlatilloCommand => new Command<PlatilloModel>(async platillo =>
{
await Navigation.PushAsync(new PlatilloView());
});
public ICommand SelectedGrupoCommand => new Command<GrupoModel>(async grupo =>
{
ButtonBackColor = "Black";
TextColor = "White";
PlatillosRepository platillosRepository = new PlatillosRepository();
Platillos = platillosRepository.GetFilteredByGroup(grupo);
});
protected virtual void OnPropertyChanged([CallerMemberName] string property = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
and Here is my converter:
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var valor = value.ToString();
switch(valor)
{
case "White":
return Color.White;
case "Black":
return Color.Black;
default:
return Color.Red;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}

Your issue is not with the ValueConverter but with your Bindings.
<CollectionView ItemsSource="{Binding Grupos}"
HeightRequest="50"
ItemsLayout="HorizontalList"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectedGrupoCommand, Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView Padding="2">
<Frame BorderColor="Black"
HasShadow="False"
Padding="2"
BackgroundColor="{Binding ButtonBackColor, Converter={StaticResource ColorConverter}}">
<StackLayout>
<Label Margin="10"
Text="{Binding nombre}"
TextColor="{Binding TextColor, Converter = {StaticResource ColorConverter}}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="Small"
VerticalOptions="CenterAndExpand">
</Label>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
You are using a CollectionView and when you set the ItemSource
<CollectionView ItemsSource="{Binding Grupos}"
All the bindings you do inside will assume this as the BindingContext.
<Label Margin="10"
Text="{Binding nombre}"
TextColor="{Binding TextColor, Converter = {StaticResource ColorConverter}}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="Small"
VerticalOptions="CenterAndExpand" />
The same way the nombre property you are binding to the Label Text Property, is part of the GroupModel class, that way the TextColor and ButtonBackColor properties are expected to be part of the same class you did bind as the ItemSource.
If you want to make it work: either add these two properties (TextColor and ButtonBackColor) to the GroupModel class or change the binding so that these two properties are accessed from the parent Binding.
The first one will give you more flexibility but at the same time might add repeated code (if all the items will share the same color for example).
The second option can be accomplished this way:
Add a name to the CollectionView
<CollectionView ItemsSource="{Binding Grupos}"
HeightRequest="50"
x:Name="GruposList"
....
Then we are gonna change a bit the binding of those items that are not part of your GrupoModel but are part of the ViewModel
<DataTemplate>
<ContentView Padding="2">
<Frame BorderColor="Black"
HasShadow="False"
Padding="2"
BackgroundColor="{Binding BindingContext.ButtonBackColor,
Source={x:Reference GruposList},
Converter={StaticResource ColorConverter}}">
<StackLayout>
<Label Margin="10"
Text="{Binding nombre}"
TextColor="{Binding BindingContext.TextColor,
Source={x:Reference GruposList},
Converter={StaticResource ColorConverter}}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="Small"
VerticalOptions="CenterAndExpand">
</Label>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
As you can see we are now accessing them through the CollectionView Binding, we do this when we specify the Source and use a Reference. More about bindings here
Hope this helps.-
Side Note:
In your converter watch for nulls.
var valor = value.ToString();
The above can make your application crash if value is null.
Use this instead:
var valor = value?.ToString();

Related

Binding Listview Items to multiple Models

I have a List view with a label and an entry. I set the item source of ListView to SQL table in codebehind. My label will have its text from this SQL, but I want to save my Entry.Text to a new model (binding to another model that is not related to listview itemsource)...
HOW TO DO IT??
The SQL stores names.
Here is my xaml:
<ListView x:Name="Mlist" Grid.Row="3" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5" BackgroundColor="White" RowSpacing="40">
<Label x:Name="label" HorizontalOptions="Center" Text="{Binding name}" />
<Entry x:Name="money" Placeholder="type the income" Text={ *** Binding to another model that is not related to listview itemsource}
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code behind:
Mlist.itemsource = conn.Table<peaople>().ToList();
Move the code which manipulate data into the viewmodel ,make ViewModel implement INotifyPropertyChanged , and set the viewmodel as the BindingContext in code behind.
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
List<People> list;
public List<People> List {
get
{
return list;
}
set
{
list = value;
NotifyPropertyChanged();
}
}
private string money;
public string Money //add Money property directly in viewmodel
{
get { return money; }
set
{
money = value;
NotifyPropertyChanged();
}
}
public ViewModel()
{
List = conn.Table<People>().ToList();
}
}
Page(code behind)
viewModel = new ViewModel();
this.BindingContext = viewModel;
Xaml
Name current page as Self , and redirect the binding path on Entry.Text.
x:Class="FormsApp.Views.Page1"
x:Name="Self"
>
<ListView x:Name="Mlist" ItemsSource="{Binding List}" RowHeight="100" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout >
<Label x:Name="label" TextColor="Red" HorizontalOptions="Center" Text="{Binding Name}" />
<Entry x:Name="money" Placeholder="type the income" Text="{Binding Source={x:Reference Self}, Path=BindingContext.Money}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Screen shot
All the Entry share the data Money .

Any idea how to add CarouselView indicators inside the CarouselView it self?

I am using a CarouselView to display a few views
each one of them is basically 2 StackLayouts
like so
what I have done so far is create a CarouselView with a template for both of these Stacklayouts
<CarouselView
x:Name="MyCarouselView"
IsVisible="{Binding ShouldShow}"
ItemsSource="{Binding MyItemSource}"
Scrolled="CarouselView_Scrolled">
<CarouselView.ItemTemplate>
<DataTemplate>
<ContentView>
<FlexLayout Direction="Column" JustifyContent="SpaceBetween">
<StackLayout FlexLayout.Basis="25%">
<elements:TopView Data="{Binding}"/>
</StackLayout>
<StackLayout FlexLayout.Basis="75%">
<elements:BottomView Data="{Binding}"/>
</StackLayout>
</FlexLayout>
</ContentView>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
Note: the views bind to different values but for simplicity sake, I wrote a simpler DataTemplate here.
it works fine and there is no problem in binding or anything
what I am trying to do is add indicators to show the user that there are more items and which one he is seeing now. (small circles or something like that).
the thing is I want to display them between the two views like so
Any idea of how to do it?
Any other way to achieve the wanted result is welcome as well
Please and thank you.
The easy way to implement that is using Grid to do that :
<Grid>
<!-- Place new controls here -->
<CarouselView x:Name="CustomCarouselView"
IndicatorView="indicatorView"
VerticalOptions="FillAndExpand"
>
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<ContentView BackgroundColor="LightBlue" >
<Label Text="{Binding TopViewTitle}" FontSize="Header" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HeightRequest="250"/>
</ContentView>
<ContentView BackgroundColor="LightGray" Margin="0,80,0,0">
<Label Text="{Binding BottomViewTitle}" FontSize="Header" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HeightRequest="250" />
</ContentView>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView x:Name="indicatorView"
IndicatorsShape="Square"
IndicatorColor="LightGray"
SelectedIndicatorColor="DarkGray"
VerticalOptions="Center"
Margin="0,-150,0,0" />
</Grid>
Above is my sample xaml code ,only need to custom the Margin property of IndicatorView and bottom ContentView after binding ItemSource for CarouselView :
List<CarouselModel> carouselModels = new List<CarouselModel>();
carouselModels.Add(new CarouselModel { TopViewTitle = "first top", BottomViewTitle = "first bottom" });
carouselModels.Add(new CarouselModel { TopViewTitle = "second top", BottomViewTitle = "second bottom" });
carouselModels.Add(new CarouselModel { TopViewTitle = "third top", BottomViewTitle = "third bottom" });
CustomCarouselView.ItemsSource = carouselModels;
The effect shows as expected :
The another way you can use RelativeLayout to implement that :
<RelativeLayout>
<!-- Place new controls here -->
<CarouselView x:Name="CustomCarouselView"
IndicatorView="indicatorView"
>
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<ContentView BackgroundColor="LightBlue">
<Label Text="{Binding TopViewTitle}"
FontSize="Header"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HeightRequest="250" />
</ContentView>
<ContentView BackgroundColor="LightGray"
Margin="0,80,0,0">
<Label Text="{Binding BottomViewTitle}"
FontSize="Header"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HeightRequest="250" />
</ContentView>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView x:Name="indicatorView"
IndicatorsShape="Square"
IndicatorColor="LightGray"
SelectedIndicatorColor="DarkGray"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=0.5,Constant=-25}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=0.5,Constant=-75}"
/>
</RelativeLayout>
Here you can modify RelativeLayout.XConstraint and RelativeLayout.YConstraint of IndicatorView to set the space between two ContentView . The Margin property
of Bottom ContentView also need to set , it's effect is to show the space between two ConentView .
You can achieve this in two methods :-
Grid, where you can place both the controls in the same Grid.Row.
Assign VerticalOptions="FillAndExpand" to the CarouselView and VerticalOptions="Center" to the IndicatorView.
By Using AbsoluteLayout.
Let me know if you have more queries.
We can achieve this scenario by placing a carousel view inside the parent carousel view and providing the Indicator view for the carousel placed inside.
Also we need to bind the "Position" property of inside carousel view properly, so that indicator view points to the proper selected index. Please find the code below for reference,
Inside Xaml:
<StackLayout Padding="30">
<CarouselView x:Name="MyCarouselView"
ItemsSource="{Binding MyItemSource}"
FlowDirection="MatchParent">
<CarouselView.ItemTemplate>
<DataTemplate>
<ContentView>
<FlexLayout Direction="Column"
JustifyContent="SpaceBetween"
BackgroundColor="Pink">
<StackLayout FlexLayout.Basis="5%">
<BoxView HeightRequest="100"
WidthRequest="100"
BackgroundColor="{Binding Boxview1Color}" />
</StackLayout>
<CarouselView FlexLayout.Basis="0%"
IndicatorView="indicatorView"
IsSwipeEnabled="False"
ItemsSource="{Binding Items}"
Position="{Binding Position}">
<CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding ItemText}"/>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView x:Name="indicatorView"
FlexLayout.Basis="10%"
HeightRequest="10"
IndicatorsShape="Circle"
IndicatorColor="White"
SelectedIndicatorColor="DarkBlue">
</IndicatorView>
<StackLayout FlexLayout.Basis="60%">
<BoxView HeightRequest="100"
WidthRequest="100"
BackgroundColor="{Binding Boxview2Color}"/>
</StackLayout>
</FlexLayout>
</ContentView>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</StackLayout>
Inside ViewModel:
public class MainPageViewModel
{
private ObservableCollection<MyItemSourceViewModel> _myItemSource;
public ObservableCollection<MyItemSourceViewModel> MyItemSource { get; set; }
public MainPageViewModel()
{
MyItemSource = new ObservableCollection<MyItemSourceViewModel>();
MyItemSource.Add(new MyItemSourceViewModel { Boxview1Color = Color.Red, Boxview2Color = Color.Blue, Position = 0 });
MyItemSource.Add(new MyItemSourceViewModel { Boxview1Color = Color.Green, Boxview2Color = Color.Purple, Position = 1});
MyItemSource.Add(new MyItemSourceViewModel { Boxview1Color = Color.PeachPuff, Boxview2Color = Color.Brown, Position = 2 });
var count = MyItemSource.Count;
foreach(var item in MyItemSource)
{
var itemCount = count;
var i = 0;
item.Items = new List<ItemsModel>();
while(itemCount > 0)
{
var itemModel = new ItemsModel { ItemText = i.ToString()};
item.Items.Add(itemModel);
i++;
itemCount--;
}
}
}
}
public class MyItemSourceViewModel
{
public Color Boxview1Color { get; set; }
public Color Boxview2Color { get; set; }
public int Position { get; set; }
public List<ItemsModel> Items { get; set; }
}
public class ItemsModel
{
public string ItemText { get; set; }
}
Please refer below screenshots as well:
Sample screenshot 1
Sample screenshot 2

View IsEnabled Property is not working on Xamarin Forms

Here is my Listview
Inside Listview button IsEnabled Property not working,IsEnabled False not working.
I followed This step but still its not working
https://forums.xamarin.com/discussion/47857/setting-buttons-isenabled-to-false-does-not-disable-button
Inside My ViewModel
OrderItems=PopuldateOrders();// getting List Items
<ListView x:Name="OrderItems" VerticalOptions="Fill"
BackgroundColor="White" HasUnevenRows="True"
SeparatorVisibility="None" ItemsSource="{Binding OrderItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView BackgroundColor="White">
<Grid BackgroundColor="Transparent" Margin="0" VerticalOptions="FillAndExpand" x:Name="Item">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*"/>
<ColumnDefinition Width="18*"/>
<ColumnDefinition Width="18*"/>
<ColumnDefinition Width="17*"/>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="17*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding PullSheetId}" Grid.Row="0" IsVisible="False"/>
<controls:CheckBox Checked="{Binding IsChecked}" Grid.Row="0" Grid.Column="0" IsVisible="{Binding IsEnableShipBtn}" Scale=".8"/>
<Label Text="{Binding KitSKU}" Grid.Row="0" Grid.Column="1"
HorizontalTextAlignment="Center" VerticalOptions="Center" FontSize="Small" TextColor="Black"/>
<Label Text="{Binding SKU}" Grid.Row="0" Grid.Column="2"
HorizontalTextAlignment="Center" VerticalOptions="Center" FontSize="Small" TextColor="{Binding ItemColor}"/>
<Label Text="{Binding ReqPackQty}" Grid.Row="0" Grid.Column="3"
HorizontalTextAlignment="Center" VerticalOptions="Center" FontSize="Small" TextColor="Black"/>
<local:EntryStyle Scale=".6" Text="{Binding ScanQuantity}" Grid.Row="0" Keyboard="Numeric"
Grid.Column="4" HorizontalTextAlignment="Center"
VerticalOptions="Center" Placeholder="Qty" IsEnabled="True" x:Name="QtyEntry"
>
<local:EntryStyle.Behaviors>
<eventToCommand:EventToCommandBehavior EventName="TextChanged"
Command="{Binding Source={x:Reference OrderItems}, Path=BindingContext.ChangeItemQty}"
CommandParameter="{Binding Source={x:Reference Item}, Path=BindingContext}"
/>
</local:EntryStyle.Behaviors>
</local:EntryStyle>
<Button Text="Ship" Scale=".6" Grid.Row="0" Grid.Column="5"
VerticalOptions="Center" BackgroundColor="#6eb43a" TextColor="White"
BorderRadius="20" CornerRadius="20" BorderColor="{Binding isError}" BorderWidth="3"
MinimumWidthRequest="60"
x:Name="ShipBtn"
Command="{Binding Source={x:Reference OrderItems}, Path=BindingContext.SubmitSingleItem}"
IsEnabled="{Binding IsEnableShipBtn}" IsVisible="{Binding IsEnableShipBtn}"
CommandParameter="{Binding .}"
/>
</Grid>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<eventToCommand:EventToCommandBehavior EventName="ItemTapped" Command="{Binding PackerItemsItemTapped}"/>
</ListView.Behaviors>
</ListView>
How to solve this?
You could achieve CanExecute method in ICommand to replace the IsEnabled property of Button. You could refer to my demo.
This is a GIF of my demo.
Firstly, You could see MainPage.xaml. Binding the model view and set command for Buttons.
<?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:local="clr-namespace:TestDemo"
x:Class="TestDemo.MainPage">
<!--BindingContext from ButtonExecuteViewModel -->
<StackLayout>
<StackLayout.BindingContext>
<local:ButtonExecuteViewModel/>
</StackLayout.BindingContext>
<Button
Text="click me to enable following button"
Command="{Binding NewCommand}"/>
<Button
Text="Cancel"
Command="{Binding CancelCommand}"/>
</StackLayout>
Here is View Model ButtonExecuteViewModel.cs. You could see the construction method of ButtonExecuteViewModel, it set the execute and canExecute to acheve isEnable of Button.
public class ButtonExecuteViewModel : INotifyPropertyChanged
{
bool isEditing;
public event PropertyChangedEventHandler PropertyChanged;
public ButtonExecuteViewModel()
{
NewCommand = new Command(
execute: () =>
{
IsEditing = true;
RefreshCanExecutes();
},
canExecute: () =>
{
return !IsEditing;
});
CancelCommand = new Command(
execute: () =>
{
IsEditing = false;
RefreshCanExecutes();
},
canExecute: () =>
{
return IsEditing;
});
}
void RefreshCanExecutes()
{
(NewCommand as Command).ChangeCanExecute();
(CancelCommand as Command).ChangeCanExecute();
}
public ICommand NewCommand { private set; get; }
public ICommand CancelCommand { private set; get; }
public bool IsEditing
{
private set { SetProperty(ref isEditing, value); }
get { return isEditing; }
}
//Determine if it can be executed
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I tried all the answers here and nothing worked, I only solved creating a custom button component:
CustomButtonView.xaml
<Button xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomComponents.CustomButton.CustomButtonView"
FontAttributes="Bold"
TextColor="White"
FontSize="20"
TextTransform="Uppercase"
CornerRadius="20"
WidthRequest="200"
HorizontalOptions="Center" />
CustomButtonView.xaml.cs: Here lives the magic of the custom IsEnabled
using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace CustomComponents.CustomButton
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomButtonView
{
#region constructors
public CustomButtonView()
{
InitializeComponent();
BackgroundColor = Color.Blue;
Clicked += (_, args) =>
{
if (IsEnabled) Command?.Execute(args);
};
}
#endregion
#region Command
public new static readonly BindableProperty CommandProperty = BindableProperty.Create(
nameof(Command),
typeof(ICommand),
typeof(CustomButtonView));
public new ICommand? Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
#endregion
#region IsEnabled
public new static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(
nameof(IsEnabled),
typeof(bool),
typeof(CustomButtonView),
true,
propertyChanged: (bindable, _, newValue) =>
{
var component = (CustomButtonView)bindable;
if ((bool)newValue)
{
component.BackgroundColor = Color.Blue;
}
else
{
component.BackgroundColor = Color.Red;
}
});
public new bool IsEnabled
{
get => (bool)GetValue(IsEnabledProperty);
set => SetValue(IsEnabledProperty, value);
}
#endregion
}
}
Now you can use it like any regular button in your XAML:
<customButton:CustomButtonView Text="Button enabled"
Command="{Binding SomeCommand}"
IsEnabled="True"/>
<customButton:CustomButtonView Text="Button disabled"
Command="{Binding SomeCommand}"
IsEnabled="False"/>

Binding Xamarin XAML to code behind properties (ListView)

I want to bind my code-behind property, which is a list, to a XAML ListView. However, my ListView is never populated and I have no idea why.
<?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="BLEPl.EvaluationPage"
Title="Evaluation">
<ContentPage.Content>
<StackLayout Padding="0,20,0,20">
<ListView HasUnevenRows="True" IsVisible="True" x:Name="TableLayout" ItemsSource="{Binding Path=SensorValues}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Style="{StaticResource TitleLabel}" Grid.Column="0" Grid.Row="0" Text="Zeitstempel: " />
<Label Style="{StaticResource DataLabel}" Grid.Column="1" Grid.Row="0" Text="{Binding TimeStamp}" />
<Label Style="{StaticResource TitleLabel}" Grid.Column="0" Grid.Row="1" Text="Wert: " />
<Label Style="{StaticResource DataLabel}" Grid.Column="1" Grid.Row="1" Text="{Binding Value}" />
<Label Style="{StaticResource TitleLabel}" Grid.Column="0" Grid.Row="2" Text="Sensor: " />
<Label Style="{StaticResource DataLabel}" Grid.Column="1" Grid.Row="2" Text="{Binding Sensor.Name}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EvaluationPage : ContentPage
{
private ObservableCollection<SensorValue> SensorValues = new ObservableCollection<SensorValue>();
public EvaluationPage ()
{
InitializeComponent ();
using (var context = new BlepContext(true))
{
var repo = new BLEPRepository(context);
SensorValues = new ObservableCollection<SensorValue>(repo.GetAllSensorValues());
}
}
}
When I run this, my ListView doesn't contain any items. I do not get errors and my app compiles fine. Am I doing something wrong here?
<ListView HasUnevenRows="True" IsVisible="True" x:Name="TableLayout" ItemsSource="{Binding SensorValues}">
you can only bind to public properties, and you need to set a BindingContext for the page
// needs to be a PUBLIC PROPERTY
public ObservableCollection<SensorValue> SensorValues { get; set; }
public EvaluationPage ()
{
InitializeComponent ();
// set the BindingContext
this.BindingContext = this;
using (var context = new BlepContext(true))
{
var repo = new BLEPRepository(context);
SensorValues = new ObservableCollection<SensorValue>(repo.GetAllSensorValues());
}
}

Group Swtich Xamarin forms

I have a Listview that is creating dynamically data and a switch in every row, what i want is when i toggle a switch the others untoggle.
Is this possible to do?
Example:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="{Binding article_description}"
FontAttributes="Bold" FontSize="13" Margin="10,5,0,-6" Grid.Row="0" LineBreakMode="NoWrap"/>
<Label Text="{Binding dish_name}"
FontSize="13" Margin="10,0,0,2" Grid.Row="1" Grid.Column="0"/>
<Label Grid.Row="0" Grid.Column="0" x:Name="LabelReserved" Text="{Binding reserved}" IsVisible="false" LineBreakMode="NoWrap"/>
<Switch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" IsEnabled="False" Toggled="SwitchMenu_OnToggled" >
<Switch.Triggers>
<DataTrigger TargetType="Switch" Binding="{Binding Source={x:Reference LabelReserved},
Path=Text.Length}" Value="7">
<Setter Property="IsToggled" Value="true" />
</DataTrigger>
</Switch.Triggers>
</Switch>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
Note: This solution was a bit of an experiment on my part - so I would recommend that, if you decide to implement this, use it with caution.
The intent here is to extend Switch to be able to act as a grouped radio button.
First step would be to create a IsToggled or IsChecked or similar property in the item that acts as BindingContext to each list-item. You can implement an interface like:
public interface IToggableItem
{
string GroupName { get; } //not mandatory, only added to support grouped lists
bool IsChecked { get; set; }
}
Second step would be extend Switch to be aware of items-list. We can do that by adding a GroupContext bindable property - which basically represents the parent list-view's ItemsSource.
When a switch is toggled, it iterates through the items-list to set the property as false on other items.
For example:
public class GroupedSwitch : CustomSwitch
{
public static readonly BindableProperty IsGroupingEnabledProperty =
BindableProperty.Create(
"IsGroupingEnabled", typeof(bool), typeof(GroupedSwitch),
defaultValue: default(bool));
public bool IsGroupingEnabled
{
get { return (bool)GetValue(IsGroupingEnabledProperty); }
set { SetValue(IsGroupingEnabledProperty, value); }
}
public static readonly BindableProperty GroupContextProperty =
BindableProperty.Create(
"GroupContext", typeof(IEnumerable), typeof(GroupedSwitch),
defaultValue: default(IEnumerable));
public IEnumerable GroupContext
{
get { return (IEnumerable)GetValue(GroupContextProperty); }
set { SetValue(GroupContextProperty, value); }
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName != nameof(IsToggled))
return;
if (IsToggled != true || GroupContext == null)
return;
var currentItem = BindingContext as IToggableItem;
if (currentItem == null)
return;
if (IsGroupingEnabled)
{
var groupList = GroupContext as IEnumerable<IGrouping<string, IToggableItem>>;
var currentGroup = groupList.FirstOrDefault(x => x.Key == currentItem.GroupName);
if (currentGroup != null)
foreach (var item in currentGroup)
{
if (item != currentItem)
item.IsChecked = false;
}
}
else
{
var simpleList = GroupContext as IEnumerable<IToggableItem>;
if (simpleList != null)
foreach (var item in simpleList)
{
if (item != currentItem)
item.IsChecked = false;
}
}
}
}
Third step would be bind GroupContext property to parent ListView's items source. For example:
<ListView x:Name="ParentListView" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" VerticalOptions="Center">
<Label Text="{Binding Name}" />
<local:GroupedSwitch
IsToggled="{Binding IsChecked}"
GroupContext="{Binding ItemsSource, Source={x:Reference ParentListView}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Or,
<ListView x:Name="_parentList" IsGroupingEnabled="true" >
<ListView.GroupHeaderTemplate>
<DataTemplate>
<TextCell Text="{Binding Key}" />
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<local:GroupedSwitch
ToggledStateFromCode="{Binding IsSwitchOn}"
IsGroupingEnabled="true"
GroupContext="{Binding ItemsSource, Source={x:Reference _parentList}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
EDIT 1: Updated code to add support for grouped list.
You should use triggers on each switch. Check this out:
https://www.tutorialspoint.com/xaml/xaml_triggers.htm

Resources