ViewCell execue command from ViewModel - xamarin.forms

I want to bind my MenuItem from my ViewCell which has a ViewModel.
This is my View cell, Do any one know the proper way do this type of binding?
I have an ObservableCollection<object> that populate my ListView, this the cell I need to bind.
<?xml version="1.0" encoding="UTF-8" ?>
<ViewCell
x:Class="DataTemp.Controls.DeviceViewCell"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="this">
<ViewCell.ContextActions>
// I've been trying these two ways
<MenuItem Command="{Binding Source={x:Reference this}, Path=BindingContext.DeviceCommand}" Text="Call" />
<MenuItem Command="{Binding DeviceCommand}" Text="Call" />
</ViewCell.ContextActions>
<ViewCell.View>
<StackLayout Padding="10">
<Label Text="{Binding Name}" />
<Label Text="{Binding Number}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
Here is my view model
I'm trying to catch the event with a a breakpoint, but i never get into the method OnDeviceCommnad.
public class DeviceViewCellViewModel : BaseViewModel, IDevice
{
public string Number
{
get
{
return _numberDevice;
}
set
{
_numberDevice = value;
OnPropertyChanged("Number");
}
}
public string Name
{
get
{
return _nameDevice;
}
set
{
_nameDevice = value;
OnPropertyChanged("Number");
}
}
public ICommand DeviceCommand { get; set; }
private string _nameDevice;
private string _numberDevice;
public DeviceViewCellViewModel()
{
DeviceCommand = new Command(OnDeviceCommnad);
}
private void OnDeviceCommnad()
{
}
}

If you not need to pass a parameter, the second way should work.
<MenuItem Command="{Binding DeviceCommand}" Text="Call" />
And in Model sample code could as follows:
public Command DeviceCommand { get; private set; }
public DeviceViewCellViewModel()
{
DeviceCommand = new Command(() =>
{
// Execute logic here
});
}
=============================Update==================================
From shared sample, I found that the binded MainViewModel not using DeviceViewCellViewModel, therefore the command will not be invoked.
Modify the folloing code inside MainViewModel:
PaymentsAndDevices.Add(new Devices()
{
Number = rdn.Next().ToString(),
Name = "I'm a device"
});
as follows:
PaymentsAndDevices.Add(new DeviceViewCellViewModel()
{
Number = rdn.Next().ToString(),
Name = "I'm a device"
});
Then OnDeviceCommnad method will be invoked.

Related

Binding StackLayout to ViewModel doens't work

I would like to have a property in my ViewModel that is linked to my StackLayout. I tried this by Binding my StackLyout to the ViewModel.
When I click on a button, this layout should be made invisible.
When I do this with the code below, my program crashes with a NulReferenceObject: Object Reference not set to an instance of an object. The StackLayout that i am talking about is the first one in the code below.
<FlexLayout>
<StackLayout BindableLayout.ItemTemplate="{Binding CreateQuizPageQuizNameSL}"> // This StackLayout should be bind to the ViewModel
<Label Text="Create New Quiz" />
<StackLayout >
<Entry Text="{Binding QuizNameInput}" Placeholder="Enter quiz name"/>
</StackLayout>
</StackLayout>
<Button Command="{Binding SubmitCreateQuizCommand}" Text="Create my quiz now!"></Button>
</FlexLayout>
ViewModel
internal class CreateQuizPageViewModel
{
// Quiz Name Input
public String QuizNameInput { get; set; }
// Command submit creating a quiz
public Command SubmitCreateQuizCommand { get; set; }
public StackLayout CreateQuizPageQuizNameSL { get; set; } = new StackLayout();
public CreateQuizPageViewModel()
{
// Declaring a new command, giving the OnSubmitCreateNewQuizClick to the delegate
SubmitCreateQuizCommand = new Command(OnSubmitCreateNewQuizClick);
}
// When a user submit the creation of new quiz
public void OnSubmitCreateNewQuizClick()
{
CreateQuizPageQuizNameSL.IsVisible = false;
}
}
Here is how to switch two layouts using IsVisible binding.
FIRST Add Nuget Xamarin.CommunityToolkit to your Xamarin Forms project. (The one that is "MyProjectName", without ".iOS" or ".Android" at end.)
TwoLayoutPage.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:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:local="clr-namespace:TestBugs"
x:Class="TestBugs.TwoLayoutPage">
<ContentPage.BindingContext>
<local:TwoLayoutViewModel/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<StackLayout
IsVisible="{Binding UseSecondLayout, Converter={StaticResource InvertedBoolConverter}}"
VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<Label Text="First Layout" FontSize="20" />
<Button Text="To Second" Command="{Binding SwitchToSecondLayoutCommand}" />
</StackLayout>
<StackLayout IsVisible="{Binding UseSecondLayout}"
VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<Label Text="Second Layout!" FontSize="32" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
TwoLayoutViewModel.cs:
using Xamarin.Forms;
namespace TestBugs
{
public class TwoLayoutViewModel : BindableObject
{
private bool _usesecondLayout = false;
public bool UseSecondLayout {
get => _usesecondLayout;
set {
_usesecondLayout = value;
OnPropertyChanged();
}
}
public TwoLayoutViewModel()
{
SwitchToSecondLayoutCommand = new Command(SwitchToSecondLayout);
}
public Command SwitchToSecondLayoutCommand { get; set; }
private void SwitchToSecondLayout()
{
UseSecondLayout = true;
}
}
}

Xamarin Forms: How can i correctly bind data from two view models to a single view?

This is the short code for testing purpose. The problem is that the UI is not displaying the Text from the Label which is binded with ViewModelB. In debugging when I hover the mouse in xaml over the Text from the Label I see the right binding data is there, but the UI simply won't display. With ViewModelA there are no problems.
In XAML:
<StackLayout>
<StackLayout>
<StackLayout.BindingContext>
<testbinding:ViewModelA/>
</StackLayout.BindingContext>
<Button Command ="{Binding Get}"/>
</StackLayout>
<StackLayout>
<StackLayout.BindingContext>
<testbinding:ViewModelB/>
</StackLayout.BindingContext>
<Label Text="{Binding Metadata}"/>
</StackLayout>
ViewModelA: where BaseViewModel is a INotifyPropertyChanged interface
public ViewModelA:BaseViewModel
{
public ViewModelA()
{
Get = new Command(SendText);
vmB = new ViewModelB();
}
ViewModelB vmB;
public ICommand Get { get; }
private void SendText()
{
string data = "someText";
vmB.GetMetadata(data);
}
}
ViewModelB is like this:
class ViewModelB:BaseViewModel
{
private string _metadata = string.Empty;
public string Metadata
{
get { return _metadata; }
set
{
_metadata = value;
OnPropertyChanged();
}
}
GetMetadata()
{
Metadata = "Some text";
}
}
In ViewModelA there are more properties which I need and in ViewModelB is just one property which gets data from a function. I could make just one ViewModel from both of them which works fine, but I'm trying to keep them smaller and organized. I already tried so many scenarios and is getting really frustrating.
Thanks for helping.
In the second StackLayout in your xaml file you're not binding it's BindingContext property to the ViewModelB instance from ViewModelA but instead you are creating a new one.
Here's a working solution for you:
public class ViewModelA : BaseViewModel
{
public ViewModelB ViewModelB { get; }
public ICommand GetMetadataCommand { get; }
public ViewModelA()
{
ViewModelB = new ViewModelB();
GetMetadataCommand = new Command((_) => GetMetadata());
}
private void GetMetadata()
{
string data = "someText";
ViewModelB.GetMetadata(data);
}
}
public class ViewModelB : BaseViewModel
{
private string _metadata;
public string Metadata
{
get { return _metadata; }
set
{
_metadata = value;
OnPropertyChanged();
}
}
public void GetMetadata(string data)
{
Metadata = data;
}
}
XAMl:
<StackLayout>
<StackLayout x:Name="StackLayout1">
<StackLayout.BindingContext>
<local:ViewModelA />
</StackLayout.BindingContext>
<Button Command ="{Binding GetMetadataCommand}"/>
</StackLayout>
<StackLayout BindingContext="{Binding Source={x:Reference StackLayout1}, Path=BindingContext.ViewModelB}">
<Label Text="{Binding Metadata}" />
</StackLayout>
</StackLayout>

Adding data to CarouselView item source results in error

I have a Xamarin CarouselView initialized with an empty ObservableCollection. The Carousel view correctly shows the EmptyView on page load.
However, when I add an item to the Observable Collection iOS throws the following exception:
attempt to insert item 0 into section 0, but there are only 0 items in section 0 after the update
The way I'm populating the item source is after the page loads I've hooked a button to an event handler to add items to the Observable Collection.
If I initialize the Observable Collection with initial data then the CarouselView works fine, it's just when I add items later that it breaks.
Page:
...
<ContentPage.BindingContext>
<viewModel:MatchesPageViewModel></viewModel:MatchesPageViewModel>
</ContentPage.BindingContext>
<CarouselView Margin="-10,15,-10,0"
ItemsSource="{Binding PendingMatches}"
HorizontalOptions="FillAndExpand"
HorizontalScrollBarVisibility="Never"
IsSwipeEnabled="True"
VerticalOptions="StartAndExpand">
<CarouselView.EmptyView>
<Frame>
<Label Text="It's empty"></Label>
</Frame>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="Some Content"></Label>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
View Model:
public class MatchesPageViewModel : BaseViewModel
{
public ObservableCollection<MatchResponse> PendingMatches { get; set; } =
new ObservableCollection<MatchResponse>();
//pretend it's invoked from a button in the page
public void SomeEventHandler()
{
//throws exception: attempt to insert item 0 into section 0...
PendingMatches.Add(new MatchResponse());
}
}
I guess that you may have some problem about adding item in observablecollection for Button click method. I do one demo using Button command binding that you can take a look.
<CarouselView
HorizontalOptions="FillAndExpand"
HorizontalScrollBarVisibility="Never"
IsSwipeEnabled="True"
ItemsSource="{Binding PendingMatches}"
VerticalOptions="StartAndExpand">
<CarouselView.EmptyView>
<Frame>
<Label Text="It's empty" />
</Frame>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding str}" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<Button
x:Name="btn1"
Command="{Binding command1}"
Text="add data" />
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new MatchesPageViewModel();
}
}
public class MatchesPageViewModel
{
public ObservableCollection<MatchResponse> PendingMatches { get; set; } = new ObservableCollection<MatchResponse>();
public ICommand command1 { get; set; }
public MatchesPageViewModel()
{
command1 = new Command(SomeEventHandler);
}
public void SomeEventHandler()
{
//throws exception: attempt to insert item 0 into section 0...
PendingMatches.Add(new MatchResponse() { str = "test" });
}
}
public class MatchResponse
{
public string str { get; set; }
}

How can I clear the entry text when doing pull down refresh?

I have an entry on the top of a page and a listview on below of that. When doing pull down refresh I want to clear the text on that entry.
Following is my entry code:
<Entry
x:Name="SearchEntry"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
PlaceholderColor="Black"
FontFamily="Bold"
TextColor="Black"
Placeholder="Search a Directory"/>
Thanks in advance
I assume that you use MVVM and have a ViewModel.
Create and bind a property of type string from your ViewModel to Entry.Text. Inside refresh method set this property to null or string.Empty.
Example:
class MyViewModel : INotifyPropertyChanged
{
// TODO: Handle INotifyPropertyChanged correctly
public string EntryTextProp { get; set; }
public async Task<?> UpdateList()
{
// ...
EntryTextProp = null;
// ...
}
}
<Entry
x:Name="SearchEntry"
Text="{Binding EntryTextProp}"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
PlaceholderColor="Black"
FontFamily="Bold"
TextColor="Black"
Placeholder="Search a Directory"/>
Updating the complete code here, this might help others.
class MyViewModel : INotifyPropertyChanged
{
string _entrytext = "";
public string EntryText
{
protected set
{
if (_entrytext != value)
{
_entrytext = value;
OnPropertyChanged("EntryText");
}
}
get { return _entrytext; }
}
public ICommand RefreshCommand
{
get
{
return new Command(async () =>
{
IsRefreshing = true;
EntryText = null;
MyList();
IsRefreshing = false;
});
}
}
}
<Entry
x:Name="SearchEntry"
Text="{Binding EntryTextProp}"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
PlaceholderColor="Black"
FontFamily="Bold"
TextColor="Black"
Placeholder="Search a Directory"/>

Dynamic binding with User Control does not work as Static is working in Silverlight and MVVM

I have created sample User Control
RestrictedBox.xaml
<UserControl.Resources>
<Converters:EnumToVisibilityConverter x:Key="enumToVisConverter" />
<Converters:EnumToVisibilityConverterReverse x:Key="enumToVisConverterReverse" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" Width="Auto">
<StackPanel Margin="0">
<TextBox Text="{Binding Value}" Visibility="{Binding Type,Converter={StaticResource enumToVisConverter}}" BorderBrush="Green" />
<PasswordBox Password="{Binding Value}" Visibility="{Binding Type,Converter={StaticResource enumToVisConverterReverse}}" BorderBrush="Red" />
</StackPanel>
</Grid>
RestrictedBox.xaml.cs
public partial class RestrictedBox : UserControl
{
public RestrictedBox()
{
InitializeComponent();
//If i bind static value and uncomment following line then it is working.
//If i bind static value and comment following line then it is not working
this.DataContext = this;
//Dynamic binding does not work using this code.
}
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(RestrictedBox), new PropertyMetadata("", ValueChanged));
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public Mode Type
{
get { return (Mode)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type", typeof(Mode), typeof(RestrictedBox), new PropertyMetadata(TypeChanged));
private static void TypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
LoginViewModel.cs
public class LoginViewModel : INotifyPropertyChanged, IRegionMemberLifetime
{
private string _UserName = "Imdadhusen Sunasara";
public string UserName
{
get { return _UserName; }
set
{
_UserName = value;
OnPropertyChanged("UserName");
}
}
}
LoginView.xaml (This following line does not work with binding)
<control:RestrictedBox Value="{Binding UserName}" Type="Text" />
This is working (with static binding)
<control:RestrictedBox Value="Imdadhusen" Type="Text" />
Thanks,
Imdadhusen
Actually It should work. Can you please verify that the DataContext of parent container of below control doesn't refering to any other property of viewmodel.
<control:RestrictedBox Value="Imdadhusen" Type="Text" />
eg. Something like below.
<StackPanel DataContext={Binding CurrentUser}>
<control:RestrictedBox Value="{Binding UserName}"
Type="Text" />
</StackPanel>
May be this help you....
I have got solution from following
http://forums.silverlight.net/t/250206.aspx/1?Dynamic+binding+with+User+Control+does+not+work+as+Static+is+working+in+Silverlight+and+MVVM
Thanks everybody who trying to help me.
Imdadhusen

Resources