How can I set the BindingContext of each view in a CarouselView? - xamarin

I'm trying to let each view in a CarouselView have the same BindingContext object as the parent ContentPage. I've tried the following which doesn't seem to work. MainPage.xaml is the page that is initialized at runtime. It holds a CarouselView with four ContentViews holding "pages" of functionality I can swipe back and forth between from the MainPage.
MainPage.xaml:
...
<CarouselView Grid.Row="1" Grid.ColumnSpan="4"
Position="{Binding CarouselPosition}"
HorizontalScrollBarVisibility="Never">
<CarouselView.ItemsSource>
<x:Array Type="{x:Type ContentView}">
<local:HomeView></local:HomeView>
<local:NewsView></local:NewsView>
<local:ChartsView></local:ChartsView>
<local:SettingsView></local:SettingsView>
</x:Array>
</CarouselView.ItemsSource>
<CarouselView.ItemTemplate>
<DataTemplate>
<ContentView Content="{Binding .}" BindingContext="{Binding BindingContext, Source={x:Reference mainPage}}"/>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
...
Basically, I want to be able to set the BindingContext for each of these ContentViews to be the same as the parent's. I've also read that the ContentViews should be able to inherit the BindingContext sometimes, but it doesn't seem to be happening in this situation.
Thanks.

This is because this kind of control doesn't inherit its BindingContext from its parent but it has its own BindingContext set to the item of the collection binded to the ItemsSource which makes sense.
Let's say you want to display a collection of User. The DataTemplate will be the user interface of a User. You want to display some properties of a User, not your ViewModel.
If you want to bind to something exposed in the ViewModel and not the User, you have to bind the BindingContext of your element with either a RelativeSource or by targeting an element by its x:Name.
Exemple : to bind a button inside your DataTemplate to a command exposed in your ViewModel (not in User), you need to do that :
<Button
Text="Try me"
Command="{Binding
Path=BindingContext.MyViewModelCommand,
Source={x:Reference xNameOfParentOutsideDataTemplateOrThePage}" />
To make it easier to understand, what it does is : I want to bind to something on the control called xNameOfParentOutsideDataTemplateOrThePage (x:Name="xNameOfParentOutsideDataTemplateOrThePage"). On this control, I want to a property of its BindingContext (most of the time, your view model).
Instead of x:Reference, you can also use a RelativeSource binding.

Related

Xamarin forms Switch Toggled event fires automatically whenever navigate to that page

I am using Xamarin forms switch inside collection view like the below.
<CollectionView ItemsSource="{Binding SwitchStatus}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout HeightRequest="100" Orientation="Horizontal" HorizontalOptions="CenterAndExpand" Spacing="30">
<Switch x:Name="mySwitch" IsToggled="{Binding State}">
<Switch.Behaviors>
<prism:EventToCommandBehavior EventName="Toggled" CommandParameter="{Binding .}" Command="{Binding BindingContext.UpdateCommand,Source={x:Reference myPage}}"/>
</Switch.Behaviors>
</Switch>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Toggled event of the Switch getting fired automatically whenever I navigate to that page because of IsToggled Binding property set. Is there any way to restrict Switch Toggled event when the user not manually triggered?
To be more clear, I have attached my sample here.
Followed prism MVVM, EventToCommandBehavior I should follow all these.
Help me with the sample code would be greatful.
. Is there any way to restrict Switch Toggled event when the user not manually triggered?
I'm afraid there is no method to achieve it .
First , Check the source code of Switch .
public static readonly BindableProperty IsToggledProperty = BindableProperty.Create(nameof(IsToggled), typeof(bool), typeof(Switch), false, propertyChanged: (bindable, oldValue, newValue) =>
{
((Switch)bindable).Toggled?.Invoke(bindable, new ToggledEventArgs((bool)newValue));
((Switch)bindable).ChangeVisualState();
}, defaultBindingMode: BindingMode.TwoWay);
It is clear that Toggled event would trigger if the IsToggled property changes .
In other word ,no matter we toggle the switch manually or changes IsToggled programmatically ,
Toggled will be triggerd , and then EventToCommandBehavior will works after that .

ListView's refresh command not working with binded IRelayCommand

I created a form with listview and ISingleOperation fo data refresh.
Then i created command in ViewModel.
public IRelayCommand LoadInvoicesCommand
{
get
{
return GetCommand(() => Execution.ViewModelExecute(new LoadInvoicesOperation(_model), 10000));
}
}
ISingleOperation works well and returns
new Result() { ResultAction = ResultType.None };
Refresh operation is bound well
RefreshCommand="{Binding LoadInvoicesCommand}"
But refresh indicator "hangs" and not disapearing, what is wrong here?
You need to bind a second property from the ListView named IsRefreshing to your ViewModel. This is a boolean property and is the one responsible to tell the ListView that the refreshing has started/completed.
An example of a ListView XAML
<ListView
VerticalOptions="FillAndExpand"
IsPullToRefreshEnabled="true"
RefreshCommand="{Binding LoadInvoicesCommand}"
IsRefreshing="{Binding IsRefreshing, Mode=OneWay}"
ItemsSource="{Binding YourItemSource}"
ItemTemplate="{StaticResource ItemTemplate" />
Your ViewModel will need a public property called IsRefreshing and you will need to set this to false when you the refresh command has completed.

Xaml Listview ItemTapped Binding MVVM

I have a Listview in Xaml and I need to bind ItemTapped event to my ModelView using MVVM.
My ListView looks like.
<ListView x:Name="list"
ItemsSource="{Binding employeeList}"
ItemTapped= {Binding selectedItem} >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ItemTapped is an event which, in MVVM, normally corresponds to an ICommand*. If you meant to bind to a normal data property, judging from the attempted binding statement in your XAML snippet, then it would make more sense to do bind ListView's SelectedItem property instead :
<ListView x:Name="list"
ItemsSource="{Binding EmployeeList}"
SelectedItem="{Binding SelectedItem}">
.....
</ListView>
If there is data related action that need to be taken upon ItemTapped event occur, it might instead be implemented on SelectedItem property changed which triggered via binding (as in the XAML binding above) :
private ItemModel _selectedItem;
public ItemModel SelectedItem
{
get { return _selectedItem; }
set
{
if(_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged();
//this can be placed before or after propertychanged notification raised,
//depending on the situation
DoSomeDataOperation();
}
}
}
*) Xamarin Blog : Turn Events into Commands with Behaviors

Separate viewmodel for page with Pivot into individual viewmodels

I am using MVVMLight, i have created standard view and viewmodel. In view i have placed Pivot:
<Grid x:Name="LayoutRoot" Background="Transparent">
<controls:Pivot Title="MY APPLICATION">
<local:FirstPivotItem />
<local:SecondPivotItem />
</controls:Pivot>
</Grid>
Here how my Pivot item looks like:
<controls:PivotItem x:Class="Pivot.WindowsPhoneControl1"
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
// standard references and settings
d:DesignHeight="480" d:DesignWidth="480" Header="First One">
<Grid x:Name="LayoutRoot">
</Grid>
</controls:PivotItem>
In code-behind
public partial class WindowsPhoneControl1 : PivotItem
{
public WindowsPhoneControl1() {
InitializeComponent();
}
}
I want to create viewmodel for this pivot item, and then work with it as i do with standard views.
I will use
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding PivotChangedCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
to handle selection changing event and than inform appropriate viewmodels by Messanger.
I do not know how can i use possibilities of viewmodel in Pivot Item class, that is inherited from PivotItem but not from ViewModelBase, as i need.
Use the standard PivotItem control for the individual items and put user controls inside each of them. Each user control can then have its viewmodel as the data context.
<Grid x:Name="LayoutRoot"
Background="Transparent">
<controls:Pivot Title="MY APPLICATION">
<controls:PivotItem Header="first">
<local:PivotUserControl1 />
</controls:PivotItem>
<controls:PivotItem Header="second">
<local:PivotUserControl2 />
</controls:PivotItem>
</controls:Pivot>
</Grid>
Depends on whether you know what the pivot items are going to be in advance or if they are dynamically created based on data. I'll assume its the first, judging by what you've written so far.
The way I've done this before is to have a number of PivotItem view models as public properties on the parent viewmodel, one for each pivot.
Then just set the dataContext of each PivotItem to the correct viewmodel property on the parent viewmodel.
this way you may be able to get away without the selection changed event.
You can also subscribed directly to any events on the view models so don't necessarily need to bother with messaging for communication as all communication can go through the parent view model.
The second approach, if you're not sure in advance exactly what pivot items you'll have is to have a collection of PivotItemViewModels on the parent view model- one per pivot item, each inheriting from a common base class or interface. Bind that collection to the ItemSource of the viewmodel.
This is slightly trickier as you then have to determine what control to display but you can do this via a converter, if you have the Templates stored somehwere as a resource :
public class PivotItemToTemplateConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((int) value)
{
case 1:
return Application.Current.Resources["Control1Template"];
case 2:
return Application.Current.Resources["Control2Template"];
default:
return Application.Current.Resources["Control3Template"];
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
#endregion
}
you can then create a pivot item template for your pivot items and use the converter to get the correct control within it based on a value. in this case imagine the shared base viewmodel has a property ViewModelId which effctively identifies which control you'll be interested in:
<ContentControl
Content="{Binding}"
ContentTemplate="{Binding ViewModelId,
Converter={StaticResource PivotItemToTemplateConverter }}" />

SelectedItem set to first item with CollectionViewSource

I have a view databound through mvvm light to a viewmodel in my WP7 project.
The view contains a Listbox with following settings:
<ListBox x:Name="StationList"
ItemsSource="{Binding StationList}"
SelectedItem="{Binding SelectedStation, Mode=TwoWay}"
>
The StationList is a ObservableCollection.
Now when the view gets loaded, everything looks great! The list is shown and NO item is selected!
But when I change the XAML to:
<ListBox x:Name="StationList"
ItemsSource="{Binding Source={StaticResource StationListSorted}}"
SelectedItem="{Binding SelectedStation, Mode=TwoWay}"
>
With the StationListSorted being a simple one property sort on the StationList as a CollectionViewSource.
Now things turn ugly!!
The same view is loaded with the same items in the listbox, but now correctly sorted, BUT the first item is selected and the selectedItem property is set!!
How can I sort a ListBox with a CollectionViewSource WITHOUT it auto selecting my first item?
On your listbox, try setting IsSynchronizedWithCurrentItem and see which value (either true or false) produces the desired effect.

Resources