I have an issue with the property IsVisible within a label. If the text was immediate (a normal string)
and the value of IsVisible is true it would work properly, but if I used a binded text or the property IsVisible binded it would not work. Please help.
<Label x:Name="st" Text="{Binding status}" HorizontalOptions="Start" FontSize="Medium"
BindingContext="{x:Reference sw}" IsVisible="{Binding IsToggled}" />
<Switch x:Name="sw" IsToggled="True" HorizontalOptions="EndAndExpand" />
First of all, please accept Jason's suggestion, take some time to copy your code and format it ptoperly.
According to your code, don't binding Label St BindingContext to Switch Sw, because you binding Label St Text to status, but Switch Sw don't have this property, so please change your code.
<Label
FontSize="Medium"
HorizontalOptions="StartAndExpand"
IsVisible="{Binding Source={x:Reference sw}, Path=IsToggled}"
Text="{Binding status}" />
<Switch
x:Name="sw"
HorizontalOptions="EndAndExpand"
IsToggled="True" />
public partial class Page12 : ContentPage, INotifyPropertyChanged
{
private string _status;
public string status
{
get { return _status; }
set
{
_status = value;
RaisePropertyChanged("status");
}
}
public Page12()
{
InitializeComponent();
status = "this is test";
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Note: don't forget to implement INotifyPropertyChanged interface, to notify data changed.
About binding, please take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/basic-bindings
Related
I'm currently trying to bind a few properties when I click a button and it pushes a new page.
Starting from the top, this is how my app is setup
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
I have a "MainPage" which is essentially the first page that shows when starting the app.
In my MainPage.xaml
I've set the BindingContext to the MainViewModel
<ContentPage.BindingContext>
<viewModels:MainViewModel/>
</ContentPage.BindingContext>
And I also have a button which has it's Command bound to a Command in my MainViewModel
<Button Text="New Goal"
HeightRequest="50" WidthRequest="100"
TextColor="White"
Margin="10"
CornerRadius="4"
Command="{Binding NewGoalCommand}">
<Button.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#8FDF70" Offset="0.1" />
<GradientStop Color="#1DBE95" Offset="1.0" />
</LinearGradientBrush>
</Button.Background>
</Button>
The MainViewModel is pretty straightforward. It has a Command property which I initialize in the constructor
public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item>();
public Command NewGoalCommand { get; set; }
public MainViewModel()
{
NewGoalCommand = new Command(() => ShowNewGoalPage());
}
private async void ShowNewGoalPage()
{
await Application.Current.MainPage.Navigation.PushAsync(new NewGoal(SelectedItem));
}
NewGoal.xaml
As you can see in the code it's invoking NewGoal which is my second page which shows up when I click the button, this page does show up when I click the button which to me, indicates that the binding was successful.
The same goes for this page, I'm setting the BindingContext to my other ViewModel which is responsible for it's corresponding view, like so
<ContentPage.BindingContext>
<viewModel:NewGoalViewModel/>
</ContentPage.BindingContext>
And I've also added components which are going to bind to it's corresponding property so that when I click "Save" it adds that item to the collection inside the MainViewModel
<ContentPage.Content>
<StackLayout Spacing="0">
<Image WidthRequest="50" HeightRequest="50"
Margin="10"
Source="{Binding ItemModel.ImageSource}"/>
<StackLayout Margin="20,0,20,0"
Spacing="0">
<Label Text="Title"/>
<Entry />
</StackLayout>
<StackLayout Margin="20,20,20,0"
Spacing="0">
<Label Text="Description"/>
<Entry />
</StackLayout>
<StackLayout Margin="20,20,20,0"
Spacing="0">
<Label Text="Type"/>
<Picker ItemsSource="{Binding ItemModel.Type}" />
</StackLayout>
<StackLayout Margin="20,20,20,0"
Spacing="0">
<Label Text="Price"/>
<Entry Keyboard="Numeric"/>
</StackLayout>
<Button Command="{Binding SaveCommand}"
Text="Save"
VerticalOptions="End">
</Button>
</StackLayout>
</ContentPage.Content>
And here is the NewGoalViewModel
class NewGoalViewModel : MainViewModel
{
public Item ItemModel { get; set; }
public Command SaveCommand { get; set; }
public NewGoalViewModel()
{
ItemModel = new Item();
ItemModel.Title = "Title";
ItemModel.Description = "Description";
ItemModel.Type = SavingsType.Other;
ItemModel.Price = 19.00f;
ItemModel.ImageSource = "cash.jpg";
SaveCommand = new Command(() => AddGoal());
}
private void AddGoal()
{
Items.Add(new Item { Title = "Rainy Day", Type = SavingsType.Other, Price = 100.00, ImageSource = "cash.jpg" });
}
}
The issue
So when I start the app and I click the first button, it takes me to the next page.
when I land on that page, it should show me an Image at the top. It's bound to a property which I've assigned in the constructor ItemModel.ImageSource = "cash.jpg";
The issue is however is that it doesnt actually bind, resulting in the image not showing until I save the NewGoal.xaml page and it reloads. Once it's done reloading it shows the image.
Try modifying
private Item itemModel { get; set; }
public Item ItemModel
{
get { return itemModel ; }
set
{
itemModel = value;
OnPropertyChanged();
}
}
and inherit your ViewModel to : INotifyPropertyChanged
and add this to implement
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I am using a bindable StackLayout to show a series of Entry bound to an ObservableCollection<string> (Addresses in the the viewModel down).
It is not a problem to show on the UI the content of the collection, but if I modify the content of any of the Entry, it does not get reflected back in the original ObservableCollection
Here is the view model:
public class MainViewModel
{
public ObservableCollection<string> Addresses { get; set; }
public ICommand AddCommand { get; private set; }
public MainViewModel()
{
AddCommand = new Command(AddEmail);
Addresses = new ObservableCollection<string>();
Addresses.Add("test1");
Addresses.Add("test2");
}
void Add()
{
AddCommand(string.Empty);
}
}
And here is the 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"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="TestList.MainPage"
x:Name="page">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Addresses"
FontSize="Large"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"/>
<Button Command="{Binding AddCommand}"
Text="+" FontSize="Title"
VerticalOptions="Center"/>
</StackLayout>
<StackLayout BindableLayout.ItemsSource="{Binding Addresses}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Entry Text="{Binding ., Mode=TwoWay}" HorizontalOptions="FillAndExpand"/>
<Button Text="-" FontSize="Title""/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentPage>
I suspect that this is due to the fact that I am working on strings, and as such they cannot be modified in place. Do you have a suggestion on how to solve this problem without introducing a wrapper class or similar?
If you want to change the value of source in code behind by editing the text in Entry .You need to implement the interface INotifyPropertyChanged in class of ObservableCollection .
Define a model class
public class MyModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string content;
public string Content
{
get
{
return content;
}
set
{
if (content != value)
{
content = value;
OnPropertyChanged("Content");
}
}
}
}
in ViewModel
public ObservableCollection<MyModel> Addresses { get; set; }
Addresses = new ObservableCollection<MyModel>();
Addresses.Add(new MyModel() {Content = "test1" });
Addresses.Add(new MyModel() { Content = "test2" });
in xaml
<Entry Text="{Binding Content, Mode=TwoWay}" HorizontalOptions="FillAndExpand"/>
You really need a wrapper class for this to work, besides if the syntax is too lengthy you can install PropertyCHanged.Fody package
Then all you need to do is add this tag:
[AddINotifyPropertyChangedInterface]
public class MainViewModel
{
public List<Address> Addresses { get; set; }
And in the wrapper class:
[AddINotifyPropertyChangedInterface]
public class Address
{
public string Street { get; set; }
I want to create a ListView which contains the department name. The ListView contains the CheckBox with department names. An user can check and unchecked the department and also the user can on clicking on select all check box user can select all department.
Which listview you want either a simple listview with textcell or imagecellits upto you, Here I'm posting code for listview with imagecell, also cell swipe option and just add Checkbox where you want to with its event and apply logics. Hope it works for you!
<AbsoluteLayout>
<ListView x:Name="Demolist" BackgroundColor="White" ItemSelected="Demolist_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell Height="30"
Text="{Binding deparment_name }"
Detail="{Binding department_description}"
ImageSource="ImageName.png">
<ImageCell.ContextActions>
<MenuItem x:Name="OnMore" Clicked="OnMore_Clicked" CommandParameter="{Binding .}" Text="More" />
<MenuItem x:Name="OnDelete" Clicked="OnDelete_Clicked" CommandParameter="{Binding .}" Text="Delete" IsDestructive="True" />
</ImageCell.ContextActions>
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</AbsoluteLayout>
Checkbox is not a control present in XF framework, so I think you can not add checkbox in listview in Xamarin.form, but you can use different to display check and uncheck status.
<ContentPage
x:Class="test2.Page3"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:convert="clr-namespace:test2"
x:Name="ToDoPage">
<ContentPage.Resources>
<convert:converter1 x:Key="converterbool" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView x:Name="listview1" ItemsSource="{Binding todoList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding ItemDescription}" VerticalOptions="Center" />
<Button
Grid.Column="1"
Command="{Binding Source={x:Reference ToDoPage}, Path=BindingContext.UpdateCheckBoxCommand}"
CommandParameter="{Binding Id}"
Opacity="0" />
<Image
Grid.Column="1"
HeightRequest="20"
HorizontalOptions="Center"
Source="{Binding IsDone, Converter={StaticResource converterbool}}"
VerticalOptions="Center" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public class TodoItem:INotifyPropertyChanged
{
private string _Id;
public string Id
{
get { return _Id; }
set
{
_Id = value;
RaisePropertyChanged("Id");
}
}
private string _ItemDescription;
public string ItemDescription
{
get { return _ItemDescription; }
set
{
_ItemDescription = value;
RaisePropertyChanged("ItemDescription");
}
}
private bool _IsDone;
public bool IsDone
{
get { return _IsDone; }
set
{
_IsDone = value;
RaisePropertyChanged("IsDone");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class ToDoViewModel:INotifyPropertyChanged
{
public ObservableCollection<TodoItem> todoList { get; set; }
public ICommand UpdateCheckBoxCommand { get; set; }
public ToDoViewModel()
{
todoList = new ObservableCollection<TodoItem>()
{
new TodoItem(){ Id = "1", ItemDescription = "Task 1", IsDone = false},
new TodoItem(){ Id = "2", ItemDescription = "Task 2", IsDone = false},
new TodoItem(){ Id = "3", ItemDescription = "Task 3", IsDone = false},
new TodoItem(){ Id = "4", ItemDescription = "Task 4", IsDone = false},
new TodoItem(){ Id = "5", ItemDescription = "Task 5",IsDone=false }
};
UpdateCheckBoxCommand = new Command((Id) => UpdateCheckBox(Id.ToString()));
}
private void UpdateCheckBox(string id)
{
IEnumerable<TodoItem> items = todoList.Where(x=>x.Id==id);
foreach(var item in items )
{
if (item.IsDone) item.IsDone = false;
else item.IsDone = true;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class converter1 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool ischeck = (bool)value;
if(ischeck==false)
{
return "uncheck.png";
}
else
{
return "check.png";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page3 : ContentPage
{
public Page3 ()
{
InitializeComponent ();
this.BindingContext = new ToDoViewModel();
}
}
I would like to bind an Entry with a Slider and vice versa. I wrote something like this:
<Entry x:Name="myEntry" Text="{Binding Value, Mode=TwoWay}" BindingContext="{x:Reference slider}"/>
<Slider x:Name="slider" Maximum="100" Minimum="0" BindingContext="{x:Reference myEntry}"/>
When I use the slider, the value in the entry is updated, but when I put manually some value in the Entry, the value append a 0 or change to 0. What can be the problem. I am working on android.
You should bind both your Slider and Entry to string/ integer in a backing View Model.
class MyViewModel
{
private int _sliderValue;
public string EntryText
{
get => _sliderValue.ToString();
set => SetProperty(ref _sliderValue, int.Parse(value) );
}
public int SliderValue
{
get => _sliderValue;
set => (ref _sliderValue, value);
}
}
And in the view
<Entry Text="{Binding EntryText}" />
<Slider Value="{Binding SliderValue}" />
More MVVM Info
Fresh MVVM for Xamarin
Caliburn Micro for Xamarin
please refer the following xaml code
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Entry Text="{Binding Path=Value}"
FontSize="18"
x:Name="label"
BindingContext="{x:Reference Name=slider}"/>
<Slider x:Name="slider"
Maximum="1500"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</Frame>
The same can be achieved by using the view model please refer the code
Xaml
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Entry x:Name="NameEntry" Placeholder="Enter Name" Text="{Binding Forename,Mode=TwoWay}" />
<Slider Value="{Binding Forename}" Minimum="0" Maximum="10"/>
</StackLayout>
</Frame>
------------------- now see the model in a c# file-----------------------------------
public partial class MainPage : ContentPage
{
public class DetailsViewModel : INotifyPropertyChanged
{
int forename;
public int Forename
{
get
{
return forename;
}
set
{
if (forename != value)
{
forename = value;
OnPropertyChanged ("Forename");
}
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public MainPage()
{
InitializeComponent();
BindingContext = new DetailsViewModel();
}
}
I have 2 ObservableCollection<T>s and each of the them has their Cards.
public class Card: INotifyPropertyChanged
{
private string _CardTitle;
public string CardTitle
{
get { return _CardTitle; }
set
{
_CardTitle = value;
OnPropertyChanged("CardTitle");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Now when a CardTitle of the card is changed it is reflected on the UI thanks to PropertyChangedEventHandler.
But when I move a card from one collection to another and then change the CardTitle, I get handler as null thus, PropertyChangedEventHandler is not fired and I cannot see the change in UI.
I have scratched my head enough but cannot figure out why. If anyone has any idea then please help me before I get in trouble.
What do you mean by "move"? You remove from first collection, and add to second yes? Does your datacontext implement INotifyPropertyChanged?
I tried to reproduce your code, but i didn't have any problem. Look, maybe you find some hints:
MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
this.DataContext = this;
}
private ObservableCollection<Card> _first;
public ObservableCollection<Card> First
{
get { return _first; }
set
{
_first = value;
OnPropertyChanged("First");
}
}
private ObservableCollection<Card> _second;
public ObservableCollection<Card> Second
{
get { return _second; }
set
{
_second = value;
OnPropertyChanged("Second");
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
//Init collections
}
private void ChangeItemName(object sender, RoutedEventArgs e)
{
var card = (sender as Button).DataContext as Card;
card.CardTitle = ":)";
}
private void ChangeCollection(object sender, RoutedEventArgs e)
{
var card = (sender as Button).DataContext as Card;
First.Remove(card);
Second.Add(card);
card.CardTitle = "xD";
}
MainPage.xaml
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding First}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding CardTitle}" />
<Button Content="Change name" Click="ChangeItemName" />
<Button Content="Change collection" Click="ChangeCollection" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView ItemsSource="{Binding Second}" Grid.Column="1">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding CardTitle}" />
<Button Content="Change name" Click="ChangeItemName" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</StackPanel>