I have a list of Settings that are a mix of types (int and string, url, ip address etc.) and the parameters that allow these to be validated are stored alongside the actual value in an object. This object is selected from a list of settings and passed to a detail page where the value can be modified.
On the detail page, I have an Entry control that contains the value that requires validation. The Entry takes it's inital value from the ViewModel property SettingItem. This works fine.
I have a Behaviour that i'm going to use to validate the user input to the value on-the-fly however, when the HandleTextChanged method is fired, ItemToValidate is null. I can't work out why.
The XAML:
<?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="DwyApp.Views.ItemDetailPage"
xmlns:local="clr-namespace:DwyApp.Behaviours"
Title="{Binding Title}">
<StackLayout Spacing="20" Padding="15">
<Label Text="{Binding SettingItem.ParameterName}" FontSize="Medium" FontAttributes="Bold"/>
<Entry Text="{Binding SettingItem.Value}" FontSize="Medium">
<Entry.Behaviors>
<local:ValidateBehaviour ItemToValidate="{Binding SettingItem}" />
</Entry.Behaviors>
</Entry>
<Label x:Name="Value_Error" Text="{Binding SettingItem.ValidationMessage}" FontSize="Medium" IsVisible="{Binding SettingItem.ValidateValue}" TextColor="Red" />
<Button Text="Save"
VerticalOptions="Center"
HorizontalOptions="Center"/>
<Label Text="{Binding SettingItem.Description}" FontSize="Medium"/>
</StackLayout>
Behaviour:
using DwyApp.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Xamarin.Forms;
namespace DwyApp.Behaviours
{
public class ValidateBehaviour:Behavior<Entry>
{
public static readonly BindableProperty ItemToValidateProperty = BindableProperty.Create(nameof(ItemToValidate),
typeof(SettingItem),
typeof(ValidateBehaviour),
defaultValue: null);
public SettingItem ItemToValidate
{
get {
return (SettingItem)GetValue(ItemToValidateProperty);
}
set {
SetValue(ItemToValidateProperty, value);
}
}
protected override void OnAttachedTo(Entry bindable)
{
bindable.TextChanged += HandleTextChanged;
base.OnAttachedTo(bindable);
}
void HandleTextChanged(object sender, TextChangedEventArgs e)
{
bool IsValid = false;
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.TextChanged -= HandleTextChanged;
base.OnDetachingFrom(bindable);
}
}
}
A breakpoint at HandleTextChanged is always hit but the value of ItemToValidate is always null. Any ideas?
Change your binding to something like this:
<Entry x:Name="EntryValue" Text="{Binding SettingItem.Value}" FontSize="Medium">
<Entry.Behaviors>
<local:ValidateBehaviour
ItemToValidate="{Binding BindingContext.SettingItem, Source={x:Reference EntryValue}}" />
</Entry.Behaviors>
</Entry>
As you can see I added a x:Name on the Entry and used that value to tell the Binding on the Behaviour what will its Source.
Hope this helps.-
Related
I'm trying to get working the MediaManager library playing a couple videos from youtube using MVVM approach.
My idea is to have a single view where initially is loaded a video, once the user watched the first loaded video he can click a button to see another video in the same view.
I can't find many examples on the internet related about how to accomplish this using MVVM, every example I've found uses the codebehind approach but my app is created in full MVVM so I need to get it working like that.
This is what I've done by now.
XAML
<?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:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
xmlns:mediamanager="clr-namespace:Plugin.MediaManager.Forms;assembly=Plugin.MediaManager.Forms"
x:Class="VideoView"
BindingContext="{Binding VideoViewModel, Source={StaticResource ServiceLocator}}">
<ContentPage.Behaviors>
<behaviors:EventHandlerBehavior EventName="Appearing">
<behaviors:InvokeCommandAction Command="{Binding PageAppearingCommand}" />
</behaviors:EventHandlerBehavior>
</ContentPage.Behaviors>
<StackLayout Margin="10,60,10,0">
<Label x:Name="LblMsg"/>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<mediamanager:VideoView x:Name="TrainingVideoPlayer"
AspectMode="AspectFill"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
<StackLayout VerticalOptions="End" HorizontalOptions="FillAndExpand">
<ProgressBar x:Name="progress" HeightRequest="10" Progress="{Binding ProgressStatus, Mode=TwoWay}" />
<StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand" Spacing="10" VerticalOptions="End">
<Image x:Name="ImgPlay"
Source="video_play.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PlayTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="ImgPause"
Source="video_pause.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PauseTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="ImgStop"
Source="video_stop.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding StopTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</Grid>
<Button x:Name="BtnNext" Command="{Binding NextCommand}"/>
</StackLayout>
</ContentPage>
ViewModel
public class VideoViewModel : AppBaseViewModel
{
//Commands
public ICommand PageAppearingCommand { get; set; }
public ICommand PlayTappedCommand { get; set; }
public ICommand PauseTappedCommand { get; set; }
public ICommand StopTappedCommand { get; set; }
public ICommand NextCommand { get; set; }
//Fields
private double _progressStatus;
private string youtubevideourl;
//Properties
public double ProgressStatus
{
get => _progressStatus;
set
{
if (Set(ref _progressStatus, value))
{
RaisePropertyChanged(() => ProgressStatus);
}
}
}
public VideoViewModel()
{
PageAppearingCommand = new Command(OnPageAppearing);
PlayTappedCommand = new Command(OnImgPlay_Tapped);
PauseTappedCommand = new Command(OnImgPause_Tapped);
StopTappedCommand = new Command(OnImgStop_Tapped);
NextCommand = new Command(OnBtnNext_Click);
youtubevideourl = "https://urloftheyoutubevideo";
}
private async void OnPageAppearing()
{
await CrossMediaManager.Current.Stop();
//Sets the videoplayer events to control playback status
CrossMediaManager.Current.PlayingChanged += VideoPlayer_PlayingChanged;
CrossMediaManager.Current.MediaFinished += VideoPlayer_MediaFinished;
}
private async void OnImgPlay_Tapped()
{
await CrossMediaManager.Current.Play(youtubevideourl, MediaFileType.Video);
}
private async void OnImgPause_Tapped()
{
await CrossMediaManager.Current.Pause();
}
private async void OnImgStop_Tapped()
{
await CrossMediaManager.Current.Stop();
}
private void VideoPlayer_PlayingChanged(object sender, PlayingChangedEventArgs e)
{
Device.BeginInvokeOnMainThread(() =>
{
ProgressStatus = e.Progress;
});
}
private void VideoPlayer_MediaFinished(object sender, MediaFinishedEventArgs e)
{
//Some logic
}
private async void OnBtnNext_Click()
{
//logic to load the next video url
}
}
Using this code I can execute play/pause/stop methods of the MediaManager but nothing happens in the View, I cant even see 1 second of the youtube video.
I will appreciate your help
Note: I installed the MediaManager nuget package in my three projects: Android, iOS and NetStandard library
Note2: One of my requirements is to guarantee as much as possible the user watched the whole video before moving to the next one (the value will be stored in an external DB), so any solution different to this one must follow this requirement
I have a problem with the ActivityIndicator in a Xamarin UWP project. The indicator is always running. I have to set the property IsVisible to hide the indicator. I want to do a platform specific condition on ActivityIndicator and to set the property IsVisible only when platform is Windows.
This is what I tried:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Views.LoginPage">
<StackLayout Padding="10" Spacing="10">
<Label Text="User" />
<Entry Text="{Binding Email}" Placeholder="User" />
<Label Text="Pass" />
<Entry Text="{Binding Password}" Placeholder="Pass" />
<Button Text="Autentificare" />
<ActivityIndicator IsRunning="{Binding IsBusy}">
<OnPlatform x:TypeArguments="x:Boolean">
<On Platform="Windows" Value="IsVisible">{Binding IsBusy}</On>
</OnPlatform>
</ActivityIndicator>
</StackLayout>
</ContentPage>
I tried to use the OnPlatform property, but I don't know how to do it correctly. Any idea?
I have tested your code and reproduce your issue. You could find the cause from source code.
void UpdateIsRunning()
{
Control.ElementOpacity = Element.IsRunning ? Element.Opacity : 0;
}
The IsRunning property is only a condition for setting the transparency of the
native Control rather than changing Active property for native control . But it does not work as expected. I will report this issue to related team. Currently there is a workaround. You could bind IsBusy to IsVisible and IsRunning just like the following.
<ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}"/>
UPDATE
You could create CustomActivityIndicator class that inherit ActivityIndicator. And then implement the custom renderer for it within native client project. For more please refer to the following code.
CustomActivityIndicator.cs
public class CustomActivityIndicator : ActivityIndicator
{
public CustomActivityIndicator()
{
}
}
CustomActivityIndicatorRenderer.cs
[assembly: ExportRenderer(typeof(CustomActivityIndicator), typeof(CustomActivityIndicatorRenderer))]
namespace XamarinActivatorTest.UWP
{
public class CustomActivityIndicatorRenderer : ActivityIndicatorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ActivityIndicator> e)
{
base.OnElementChanged(e);
if (Control != null)
{
UpdateStatus();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(Element.IsRunning))
{
UpdateStatus();
}
}
private void UpdateStatus()
{
Control.ShowPaused = !Element.IsRunning;
Control.Opacity = Element.IsRunning ? 1 : 0;
}
}
}
You could bind IsRunning property directly. Because the function of IsRunningproperty was changed in your custom renderer.
<StackLayout Padding="10" Spacing="10">
<Button Text="Autentificare" Clicked="Button_Clicked"/>
<local:CustomActivityIndicator IsRunning="{Binding IsBusy}" >
</local:CustomActivityIndicator>
</StackLayout>
I have uploaded the code sample to git hub.
I have tried to develop a usercontrol in Xamarin.Forms but I am not able to bind the controls of that Usercontrol using ViewModel.
My CustomeEntryUserControl is look like
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="LearningApp.UserControls.CustomeEntry"
xmlns:local="clr-namespace:LearningApp.Extension">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Entry Grid.Column="0" x:Name="entry" />
<local:RoundedButton Grid.Column="1" Text="X" BackgroundColor="Gray" TextColor="White" HeightRequest="20" WidthRequest="10" VerticalOptions="Center" Margin="-30,0,30,0" x:Name="cancelbtn" />
</Grid>
Code behind of this usercontrol is look like
public partial class CustomeEntry : Grid
{
public CustomeEntry()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(CustomeEntry), default(string));
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(CustomeEntry), typeof(ICommand), default(ICommand));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string PlaceHolder
{
get { return entry.Placeholder; }
set { entry.Placeholder = value; }
}
}
I have tried to use this control in one of my content page is like
<?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="LearningApp.Views.TriggersDemo.DataTriggers"
xmlns:local="clr-namespace:LearningApp.UserControls">
<StackLayout Orientation="Vertical">
<Label FontSize="Large" FontAttributes="Bold" Text="{Binding lblText}"></Label>
<local:CustomeEntry Text="{Binding Name}" Command="{Binding CancelCommand}" PlaceHolder="Enter Name"/>
<Button Command="{Binding CheckCommand}" Text="Check" />
</StackLayout>
</ContentPage>
code behind of above mentioned content page is
namespace LearningApp.Views.TriggersDemo
{
public partial class DataTriggers : ContentPage
{
public DataTriggers()
{
InitializeComponent();
this.BindingContext = new TriggersViewModel();
}
}
}
and My ViewModel contain the one Property like
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; RaisePropertyChanged(); }
}
Can anyone please guide how I can bind the Entry Text of UserControl to my ViewModel property Name direct?
In you CustomerEntry there is no control that displays the Text you're binding to.
You have to set up all the bindings in your custom control too.
You can also pass a handler to the TextProperty's `propertyChanged' parameter, and set other view's properties on behalf.
Remove the properties from the custom control and bind directly to the BindingContext which will be passed along (containing you viewmodel).
Then set Text="{Binding Text=.}" in your custom control's entry (same with Button's Command) to have it bind to the text from the BindingContext, in fact you can remove all your properties from the custom control, and bind directly to the ViewModel.
I've got a user control that I'm including in a ListView and I want to add dynamic content when the user control initialises based on the listitem binding. I'm not sure how to do this. See the "HOW DO I BIND THIS???"... I presume I should binding my PropertyBinding to the listitem somehow?
Here's my original view with my listview
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:EngineerApp" x:Class="EngineerApp.GigsPage" NavigationPage.HasNavigationBar="false" xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms">
<ContentPage Title="Gigs" Icon="icon.png">
<StackLayout Orientation="Vertical">
<local:HeaderBar LeftButtonText="Back" RightButtonText="Leave" LeftButtonClickEvent="Back" RightButtonClickEvent="Back"></local:HeaderBar>
<local:ButtonBar LeftButtonText="Add Gig" RightButtonText="Month View" LeftButtonClickEvent="AddGig" RightButtonClickEvent="Back"></local:ButtonBar>
<ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}"></ActivityIndicator>
<ListView HasUnevenRows="true" SeparatorVisibility="Default" ItemsSource="{Binding gigs}" ItemSelected="Handle_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Padding="20,20,20,20">
<Frame.Content>
<Frame Padding="15,15,15,15" OutlineColor="Gray" BackgroundColor="White">
<Frame.Content>
<StackLayout Padding="20,0,0,0" Orientation="Vertical" HorizontalOptions="CenterAndExpand">
<Label Text="{Binding venue}"
HorizontalTextAlignment="Center"
TextColor="#69add1"/>
<Label Text="{Binding date}"
HorizontalTextAlignment="Center"
FontFamily="OpenSans-Light"
FontSize="9"
TextColor="#69add1"/>
<local:AuthorisationBar SelectedGig="{Binding .}"></local:AuthorisationBar>
</StackLayout>
</Frame.Content>
</Frame>
</Frame.Content>
</Frame>
</ViewCell>
<local:GigCard />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
</TabbedPage>
And then here's the user control xaml.
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="EngineerApp.AuthorisationBar">
<ContentView.Content>
<StackLayout BackgroundColor="Red" HeightRequest="50" x:Name="barcontent" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label HorizontalOptions="FillAndExpand" VerticalOptions="Center" Text="I want to show some data here once the bindings are working"></Label>
</StackLayout>
</ContentView.Content>
</ContentView>
And here's the code behind for the user control
using System;
using System.Collections.Generic;
using EngineerApp.ViewModels;
using Xamarin.Forms;
namespace EngineerApp
{
public partial class AuthorisationBar : ContentView
{
public static readonly BindableProperty GigProperty = BindableProperty.Create("SelectedGig", typeof(GigViewModel), typeof(AuthorisationBar), new GigViewModel());
public GigViewModel SelectedGig
{
get
{
return (GigViewModel)GetValue(GigProperty);
}
set
{
SetValue(GigProperty, value);
}
}
public AuthorisationBar()
{
InitializeComponent();
BindingContext = this;
}
}
}
UPDATE 1 - Updated all pages to reflect most recent suggestion. With the {Binding .} I now get the error stated below:
"Cannot assign property "SelectedGig": Property does not exists, or is not assignable, or mismatching type between value and property"
Try this:
<local:AuthorisationBar SelectedGig="{Binding .}"></local:AuthorisationBar>
where "." will be the "current" item of the list.
Notice I used SelectedGid in my code as this is the name of the property you defined your custom control, not SelectedGigId.
Also you need to remove this line from the constructor of your custom control:
BindingContext = SelectedGig;
The BindingContext will already be of type GigViewModel
Hope this helps!
UPDATE
Your Custom Control code behind should look:
public partial class AuthorisationBar : ContentView
{
public AuthorisationBar()
{
InitializeComponent();
}
public static readonly BindableProperty SelectedGigProperty = BindableProperty.Create(nameof(SelectedGig), typeof(GigViewModel), typeof(AuthorisationBar), null);
public GigViewModel SelectedGig
{
get
{
return (GigViewModel)GetValue(SelectedGigProperty);
}
set
{
SetValue(SelectedGigProperty, value);
}
}
}
As previously stated you don't need to set anything to the BindingContext
UPDATE 2
In response to your question. The error was that the BindableProperty of your custom control was not complying with the naming conventions requirements.
From Xamarin BindableProperty documentation:
The naming convention for bindable properties is that the bindable property identifier must match the property name specified in the Create method, with "Property" appended to it.
This is why updating your BindableProperty from GigProperty to SelectedGigProperty fixed your issue.
I've done something similar for my current project - passing the current item's ID to my ViewModel (from the code you've posted, it seems you're not using proper separation of concerns under the MVVM paradigm - the VM shouldn't be initialising anything in your view):
<Button Text="Reconnect" Command="{Binding Path=BindingContext.ReconnectCommand, Source={x:Reference Name=ServicesList}}" CommandParameter="{Binding}" VerticalOptions="Center"/>
For reference, the button which is passing the command is embedded in a ListView.DataTemplate, and is passing the ID of the list item it's part of
I have a Forms XAML Page and in there I have a listview, and each element has a Switch (xamarin default). I can bind the data from the items to the listview, but I cannot subscrive the Switch event "Toggled", as it causes the item not to show. I also tried with ICommand and Command, as it is instructed to do with buttons, but the result is the same, nothing shown. How can I handle the switch toggle from the my viewmodel?
View
<?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="TouristicWallet.Views.WalletManagementPage"
xmlns:vm="clr-namespace:TouristicWallet.ViewModels"
xmlns:converters="clr-namespace:TouristicWallet.Converters"
>
<ContentPage.BindingContext>
<vm:WalletManagementViewModel x:Name="ViewModel"/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<converters:CurrencyIdToCodeConverter x:Key="idToCodeConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="MyCurrencies" ItemsSource="{Binding Currencies, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Currency.Initials, Mode=OneWay}" />
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}"
Toggled="{Binding Toggled}"
/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
ViewModel
public class WalletManagementViewModel : ViewModelBase
{
private readonly List<OwnedCurrencyWrapper> _currencies = new List<OwnedCurrencyWrapper>();
public List<OwnedCurrencyWrapper> Currencies { get { return _currencies; } }
public WalletManagementViewModel()
{
CurrencyDataAccess cda = new CurrencyDataAccess();
foreach (var item in cda.GetCurrencies())
{
Currencies.Add(new OwnedCurrencyWrapper(item));
}
OnPropertyChanged(nameof(Currencies));
}
public class OwnedCurrencyWrapper
{
public Currency Currency { get; private set; }
public Boolean IsOwned { get; set; }
public ICommand Toggled { get; set; }
public OwnedCurrencyWrapper(Currency currency)
{
Currency = currency;
WalletDataAccess wda = WalletDataAccess.Instance;
IsOwned = wda.IsOwned(Currency.Id);
Toggled = new Command(() => Update());
}
public void Update()
{
WalletDataAccess wda = WalletDataAccess.Instance;
if (IsOwned) wda.RemoveOwnedCurrency(Currency.Id);
else wda.OwnCurrency(Currency.Id);
}
public void Toggled_handler(object sender, ToggledEventArgs e)
{
Update();
}
}
}
I am not using any mvvm framework
First off a Switch can not bind to a Command. See:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/#Commanding_with_ViewModels
From the above, the Forms controls that can bind to an ICommand are:
Button
MenuItem
ToolbarItem
SearchBar
TextCell (and hence also
ImageCell )
ListView
TapGestureRecognizer
you can just do the following to run code in the View's code behind file, do this in the XAML:
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}"
Toggled="Handle_Toggled" />
And then in the Code behind file:
void Handle_Toggled(object sender, Xamarin.Forms.ToggledEventArgs e)
{
// Do stuff
}
Alternately, since you are binding, you could run code in the actual OwnedCurrencyWrapper class (which is what you seem to want) just by adding code to the setter for IsOwned. IN this case, don't assign anything to the Toggled property of your switch::
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}" />
And then in your OwnedCurrencyWrapper class:
bool _isOwned;
public bool IsOwned {
get
{
return _isOwned;
}
set
{
_isOwned = value;
// Do any other stuff you want here
}
}
That said, your binding is not complete since your view model is not implementing INotifyPropertyChanged so changes made directly to the view model will not be reflected in the UI. For more info on binding with Forms MVVM, see:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/
UPDATE: I was not aware of Behaviors in Xamarin Forms. See:
https://github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/EventToCommandBehavior
In the context of commanding, behaviors are a useful approach for connecting a control to a command. In addition, they can also be used to associate commands with controls that were not designed to interact with commands. This sample demonstrates using a behavior to invoke a command when an event fires.
So this should allow you to bind the Toggled event to a Command.
If you adhere to Prism framework you may easily wire an event to a command. Your xaml will look like in the following example.
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
x:Class="TouristicWallet.Views.WalletManagementPage">
<ContentPage.Content>
<StackLayout VerticalOptions="CenterAndExpand" Padding="20">
<Switch IsToggled="{Binding IsOwned}" x:Name="IsOwnedSwitch">
<Switch.Behaviors>
<b:EventToCommandBehavior EventName="Toggled" Command="{Binding ToggleIsOwnedCommand}"/>
</Switch.Behaviors>
</Switch>
</StackLayout>
</ContentPage.Content>
</ContentPage>
As others have mentioned, you should bind the Toggled event to an eventHandler behavior which will forward a command. The code below can be used.
<Switch IsToggled="{Binding SwitchEnabled}" x:Name="MySwitch">
<Switch.Behaviors>
<!-- behaviors namespace comes from "Xamarin.Forms Behaviors" nuget -->
<behaviors:EventHandlerBehavior EventName="Toggled">
<behaviors:InvokeCommandAction Command="{Binding ToggleSwitchCommand}" />
</behaviors:EventHandlerBehavior>
</Switch.Behaviors>
</Switch>
Solution : After doing some R&D i found the root cause of this issue,
Error Code in very first post:
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}"
Toggled="{Binding Toggled}"
/>
Just do Two steps.
Declare event listener function OnToggled in ContentPage class and not into your ViewModel class that you need to bind
In your ContentPage class
void OnToggled(object sender, ToggledEventArgs e){
}
change Toggled="{Binding Toggled}" == to ==> Toggled="OnToggled"
it will fix the issue, Don't know why it don't work for event listener function declared in ViweModel class.
--I hope it will work.
I had the same issue and solved it in a very easy way.
=> Goal: Get items with a switch control in a listview to respond to a command.
<?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="TouristicWallet.Views.WalletManagementPage"
xmlns:vm="clr-namespace:TouristicWallet.ViewModels"
x:Name="pageName"
xmlns:converters="clr-namespace:TouristicWallet.Converters"
>
<ContentPage.BindingContext>
<vm:WalletManagementViewModel x:Name="ViewModel"/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<converters:CurrencyIdToCodeConverter x:Key="idToCodeConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="MyCurrencies" ItemsSource="{Binding Currencies, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Currency.Initials, Mode=OneWay}" />
<Switch IsToggled="{Binding Selected}" HorizontalOptions="Start">
<Switch.Behaviors>
<b:EventToCommandBehavior
EventName="Toggled" Command="
{Binding
Path=BindingContext.SendCommand,
Source={x:Reference
Name=pageName}}" />
</Switch.Behaviors>
</Switch>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
In ViewModel
Define your Command /ICommand
public ICommand SendCommand { get; set; }
SendCommand = new Command(() => //do something.....);
Please Take special note of the areas in bold.