Xamarin Custom Control Binding Not Working - xamarin

I created a ContentView, called Switch, to host this slider as a custom control mainly so I can reuse the on and off visual states.
The ContentView contains a Label (which is there just for debugging) and a Syncfusion Switch plus some VisualStateManager stuff. The Label and the Switch are binded to BindableProperies in the code behind, with Switch using EventToCommandBehavior, so I doubt this is a Syncfusion problem.
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:core="clr-namespace:FEOD.Core;assembly=FEOD"
xmlns:buttons="clr-namespace:Syncfusion.XForms.Buttons;assembly=Syncfusion.Buttons.XForms"
mc:Ignorable="d"
x:Class="FEOD.Views.Components.Switch">
<ContentView.Content>
<StackLayout>
<Label Text="{Binding LabelText}" TextColor="Black" />
<buttons:SfSwitch VisualType="Cupertino" WidthRequest="50" HeightRequest="25">
<buttons:SfSwitch.Behaviors>
<core:EventToCommandBehavior EventName="StateChanged" Command="{Binding StateChangedCommand}"/>
</buttons:SfSwitch.Behaviors>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="SwitchSettings">
<Setter.Value>
<buttons:DefaultSwitchSettings
x:TypeArguments="buttons:OnState"
ThumbBorderColor="#039CDE" ThumbColor="#FFFFFF"
TrackBorderColor="#039CDE" TrackColor="#039CDE"/>
</Setter.Value>
</Setter>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="SwitchSettings">
<Setter.Value>
<buttons:DefaultSwitchSettings
x:TypeArguments="buttons:OffState"
ThumbBorderColor="#E77C21" ThumbColor="#FFFFFF"
TrackBorderColor="#E77C21" TrackColor="#E77C21"/>
</Setter.Value>
</Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</buttons:SfSwitch>
</StackLayout>
</ContentView.Content>
Code behind Bindable's
using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace FEOD.Views.Components
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Switch : ContentView
{
public static readonly BindableProperty StateChangedCommandProperty = BindableProperty.Create(
propertyName: nameof(StateChangedCommand),
returnType:typeof(ICommand),
declaringType:typeof(Switch),
defaultValue:default(ICommand),
defaultBindingMode: BindingMode.OneWay
);
public ICommand StateChangedCommand
{
get => (ICommand) GetValue(StateChangedCommandProperty);
set => SetValue(StateChangedCommandProperty, value);
}
public static readonly BindableProperty LabelTextProperty = BindableProperty.Create(nameof(LabelText), typeof(string), typeof(Switch), string.Empty, BindingMode.TwoWay);
public string LabelText
{
get => (string) GetValue(LabelTextProperty);
set => SetValue(LabelTextProperty, value);
}
public Switch()
{
InitializeComponent();
}
}
}
The ContentView is hosted in a ContentPage. Both of it's properties are set like this:
<components:Switch Grid.Column="1" LabelText="MY LABel" StateChangedCommand="{Binding CreatorOrContributorFinderCommand}" />
where LabelText is set in line as "MY LABel" and StateChangedCommand is set to the view model command "CreatorOrContributorFinderCommand". The page's BindingContext is set to an instance of the view model
public ICommand CreatorOrContributorFinderCommand { get; private set; }
private async Task OnCreatorOrContributorFinderChanged()
{
var a = 1;
}
constructor has this line:
CreatorOrContributorFinderCommand = new AsyncCommand(OnCreatorOrContributorFinderChanged);
Note: AsyncCommand comes from https://johnthiriet.com/mvvm-going-async-with-async-command
I cannot figure out why neither the Label nor the command are doing anything. During debugging, the LabelText doesn't show up
When I slide the switch, this breakpoint in EventToCommandBehavior shows that no command is tied to the event. The null command prevents the handler in the viewmodel from getting called.

After being confounded on this and trolling through the EventToCommandBehavoir source code and dozens of StackOverflow postings, I finally found the problems.
I found two things to watch out for:
The first is that EventToCommandBehavoir only wants a Xamarin.Forms.Command, not just any ICommand.
The second, thanks very much to this post, the ContentView must have this in its constructor for commands to work (though, not for other types like text).
Content.BindingContext = this;
Because there are a several ICommand implementations (useful especially if you want AsyncCommand) and that Labels and such work fine without setting Content.BindingContext, it is very easy to get confounded by EventToCommandBehavior.

Related

How to use Android controls in Xamarin.Forms

I am trying to create Xamarin form which has an Android rating bar and iOS slider.
Below is the XAML file I am using.
I can see the Label but not able to see the rating bar when I try to run in Android device.
Please help.
<?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:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:android="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidForms="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android"
mc:Ignorable="d"
x:Class="XamrineTest.Page1">
<StackLayout BackgroundColor="Green" >
<Label Text="Welcome to Page1!" BackgroundColor="Gray"/>
<ios:UIDatePicker />
<ios:UISlider MaxValue="10" Value="{Binding SlideValue}" />
<ios:UIStepper />
<ios:UISwitch />
<android:RatingBar BackgroundColor="Lavender" HorizontalOptions="Center"
Scale="0.4"
StarCount="5" Step="0.5" Margin="0" SelectedColor="Orange"
HeightRequest="40" UnSelectedColor="LightGray" Rate="{Binding Rating,Mode=TwoWay}">
</android:RatingBar>
</StackLayout>
</ContentPage>
I think there are several problem in your code:
1.the namespace you used is not right, just change to this:
<androidWidget:RatingBar x:Arguments="{x:Static androidLocal:MainActivity.Instance}" >
</androidWidget:RatingBar>
And using following namespacing:
xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
2.we also need the following code to init androidWidget:RatingBar
x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
define the Instance in MainActivity of android code:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; } // define Instance
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Instance = this;// init Instance
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
For more details, you can check:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/native-views/xaml
And this link also includes some useful samples, you can refer to it.
Update:
Yes, you can also use x:Arguments="{x:Static androidForms:Forms.Context}",
and the following code works properly.
<android:RatingBar x:Arguments="{x:Static androidForms:Forms.Context}" NumStars="5" StepSize="1.0" Rating="{Binding Rating,Mode=TwoWay}" />
Note:
The properties as follows you used in RatingBar does not exist.
BackgroundColor="Lavender" HorizontalOptions="Center"
Scale="0.4"
StarCount="5" Step="0.5" Margin="0" SelectedColor="Orange"
HeightRequest="40" UnSelectedColor="LightGray" Rate="{Binding Rating,Mode=TwoWay}"

Unable to set bindings to BindableProperties in Partial Views using Xamarin Prism 7.1

I'm using the Partial Views feature of Prism 7.1 in Xamarin in which the ContentView can have its own ViewModel. The binding to the viewmodel is working fine. However, I would also like to set a BindableProperty. For example, I would like to set a Title property on the ContentView. If the ContentView does not have its own ViewModel the Binding works fine. If it does have its own ViewModel the binding never occurs.
MainPage.xaml
<controls:CustomContentView Title="My Custom View Title"
mvvm:ViewModelLocator.AutowirePartialView="{x:Reference self}"/>
CustomContentView.cs:
public static readonly BindableProperty TitleProperty =
BindableProperty.Create(
nameof(Title),
typeof(string),
typeof(CustomContentView));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
CustomContentView.xaml:
<ContentView.Content>
<StackLayout>
<Label Text="{Title}" />
</StackLayout>
</ContentView.Content>
If I set a breakpoint on the Title's set method, it never gets hit and the Title in the Label control is never bound.
By using a Partial View your Binding Context is the ViewModel for the View not the View itself...
By setting the Label's text in the CustomContentView like:
<StackLayout>
<Label Text="{Binding Title}" />
</StackLayout>
This would expect to bind to something like:
public class CustomContentViewModel : BindableBase
{
private string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
}
Since you're instead expecting the binding to pull from the code behind, the XAML would need to be something like:
<ContentView x:Class="AwesomeApp.Views.CustomContentView"
x:Name="self">
<StackLayout>
<Label Text="{Binding Title,Source={x:Reference self}}" />
</StackLayout>
</ContentView>

Is it possible to give a Xamarin Frame different border colors?

Here is the XAML that I have:
<Frame CornerRadius="1" HasShadow="false" Margin="10"
BackgroundColor="White" BorderColor="Silver" Padding="0" >
I saw on the Google Translate that's on iOS that they use something like this kind of a frame to surround different rows in settings. However they have a different border color on the top and bottom.
Does anyone know if there is a way to do the with a frame?
You could achieve that with a component, like this
BorderEntryComponent.xaml
<?xml version="1.0" encoding="UTF-8"?>
<StackLayout
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="X.Y.Z.BorderEntryComponent"
Spacing="0">
<BoxView
x:Name="TopBorder"
HeightRequest="2"
HorizontalOptions="FillAndExpand"
VerticalOptions="EndAndExpand" />
<Entry x:Name="Entry" />
<BoxView
x:Name="BottomBorder"
HeightRequest="2"
HorizontalOptions="FillAndExpand"
VerticalOptions="EndAndExpand" />
</StackLayout>
And, in your BorderEntryComponent.xaml.cs
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace X.Y.Z
{
public partial class BorderEntryComponent : StackLayout
{
public static readonly BindableProperty TopColorProperty =
BindableProperty.Create(nameof(TopColor), typeof(Color), typeof(BorderEntryComponent), default(Color), BindingMode.OneWay);
public static readonly BindableProperty BottomColorProperty =
BindableProperty.Create(nameof(BottomColor), typeof(Color), typeof(BorderEntryComponent), default(Color), BindingMode.OneWay);
public UnderlineEntryComponent()
{
InitializeComponent();
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == TopColorProperty.PropertyName)
{
this.TopBorder.Color = TopColor;
}
else if (propertyName == BottomColorProperty.PropertyName)
{
this.BottomBorder.Color = BottomColor;
}
}
public Color TopColor
{
get => (Color)GetValue(TopColorProperty);
set => SetValue(TopColorProperty, value);
}
public Color BottomColor
{
get => (Color)GetValue(BottomColorProperty);
set => SetValue(BottomColorProperty, value);
}
}
}
Then, you just do this on your .xaml
<components:UnderlineEntryComponent
TopColor = "Blue"
BottomColor = "Black" />
You can read more on Bindable Properties here
AFAIK, you don't have a built in option for what you are looking for.
You could play around by drawing multiple frames on top of each other with different colors and properties, but it is a bit too "hacky" for my taste.
I suggest you create a Custom Render for your own Frame control. This way, you will be able to draw the frame however you want and reuse your control in any other place.

Using Command attribute in Windows 8.1 Store App TopAppBar

I am trying to get to grips with effective use of the TopAppBar in Windows Store Apps. I have used the Basic Page template and added a NavigateCommand to the NavigationHelper class in the same way that the GoForwardCommand and GoBackCommand have been implemented. I have also added a constructor to the RelayCommand to enable Execute and CanExecute delegates to take an object parameter
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_executeWithParam = execute;
_canExecuteWithParam = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? (_canExecuteWithParam == null ? true : _canExecuteWithParam(parameter)) : _canExecute();
}
public void Execute(object parameter)
{
if (_execute == null)
_executeWithParam(parameter);
else
_execute();
}
In my XAML for the MainPage I have the following code:
<Page
x:Name="pageRoot"
x:Class="UniAppTest.MainPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UniAppTest"
xmlns:common="using:UniAppTest.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
<x:String x:Key="AppName">Welcome to universal apps!</x:String>
</Page.Resources>
<Page.TopAppBar>
<AppBar>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<Button Content="Home" Width="140" Height="80" />
<Button Content="Summary" Width="140" Height="80"
Command="{Binding NavigateCommand}"
CommandParameter="SummaryPage" />
<Button Content="Reports" Width="140" Height="80"
Command="{Binding NavigationHelper.NavigateCommand, ElementName=pageRoot}"
CommandParameter="ReportsPage " />
</StackPanel>
<SearchBox Grid.Column="1" Width="300" Height="50" HorizontalAlignment="Right" />
</Grid>
</AppBar>
</Page.TopAppBar>
Neither of the Buttons invoke the command when pressed.
However, if I add a button to the main page content the command works successfully:
<Button x:Name="summaryButton" Margin="39,59,39,0"
Command="{Binding NavigationHelper.NavigateCommand, ElementName=pageRoot}"
CommandParameter="SummaryPage"
Grid.Row="1" Content="Summary"
Style="{StaticResource TextBlockButtonStyle}"
VerticalAlignment="Top"
FontSize="24"/>
Can anyone help me see what I'm doing wrong. I think it must be in the Binding reference. I aalways find this confusing. Would appreciate any help. Thanks.
Mmmm... As I know, you are not Binding Anything to the top.appbar or the parent, So when it tries to see were has to go to find the command he doesn't know where it is.
When you make it in a simple button the button has a parent with the Bind, isn't?
I too desire nice commands to do the navigation work. I came up with a strongly-type way of doing things. Works nicely from XAML, code-behind or viewmodel.
First, make a class that will contain as many commands as you have pages.
public class PagesLocator
{
private Dictionary<string, RelayCommand> commands = new Dictionary<string, RelayCommand>();
private Frame frame;
private bool useCurrentFrame;
public PagesLocator()
{
this.useCurrentFrame = true;
}
public PagesLocator(Frame frame)
{
this.frame = frame;
}
public RelayCommand Home
{
get { return this.GetCommand("Home", typeof(HomePage)); }
}
public RelayCommand Page2
{
get { return this.GetCommand("Page2", typeof(Page2)); }
}
private RelayCommand GetCommand(string key, Type typeOfPage)
{
if (this.commands.ContainsKey(key))
{
return this.commands[key];
}
var item = new RelayCommand(x => this.GetFrame().Navigate(typeOfPage, x));
this.commands.Add(key, item);
return item;
}
private Frame GetFrame()
{
if (this.frame != null)
{
return this.frame;
}
else if (this.useCurrentFrame)
{
return (Frame)Window.Current.Content;
}
else
{
throw new InvalidOperationException("Cannot navigate. Current frame is not set.");
}
}
}
Instantiate that in the app resources to have a data-bindable source.
<Application.Resources>
<common:PagesLocator x:Key="Navigator" />
</Application.Resources>
Finally, you can bind any command from this source.
<Button Content="go to page 2"
Command="{Binding Page2, Source={StaticResource Navigator}}"
CommandParameter="extra nav parameter here" />
<AppBarButton Label="page2" Command="{Binding Page2, Source={StaticResource Navigator}}" />

WP7 binding UserControl using MVVM [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Binding between Usercontrol with listbox and parent control (MVVM)
I’m trying to bind a UserControl to property on my main page’s viewmodel
The code looks like this:
UserControl xaml:
<UserControl x:Class="myUserControl" ....>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}" >
<ListBox Name="myListBox" ItemsSource="{Binding Path=myItemsSource}"/>
</Grid>
</UserControl>
the codebehind looks like this:
public partial class myUserControl : UserControl
{
public static DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("myItemsSource",
typeof(IEnumerable), typeof(myUserControl), null);
public IEnumerable myItemsSource
{
get
{
return (IEnumerable)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
}
The UC is used like this in the main page:
<phone:PhoneApplicationPage DataContext="{Binding myViewModel, Source={StaticResource Locator}}" ....>
<Grid x:Name="ContentPanel">
<uc:myUserControl x:Name="ucList" myItemsSource="{Binding Path=DataList}"/>
</Grid>
</phone:PhoneApplicationPage>
and the viewModel for the main page look like this:
public class myViewModel : ViewModelBase
{
public ObservableCollection<myObject> DataList
{
get
{
return _datalist;
}
set
{
if (_dataList != value)
{
_dataList = value;
RaisePropertyChanged("DataList");
}
}
}
}
But when the DataList property is set, the uc List in not populated.
What i'm I missing ?
It's still early and I haven't had my coffee yet, but it looks to me like you might need a data template for your list items. You've defined the ItemsSource but not told the control how to render the items themselves.
What does your list contain? Try binding a data template to one of the list item properites, like this:
<UserControl x:Class="myUsercontrol" ....>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}" >
<ListBox Name="myListBox" ItemsSource="{Binding Path=myItemsSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SomeListItemProperty}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
EDIT: Are you by chance using MVVM Light? I saw that you mentioned {StaticResource = Locator} in one of the comments. That would be helpful to know.
One other thing you might try is checking if your UserControl is in its own namespace. If you put it in a UserControl folder, it might be. I've had some issues with binding a UserControl when it wasn't in the same namespace as the ViewModel.

Resources