I have a simple app that has a PicListPage (picture list). The xaml and code behind allow picking a picture from the Android emulator gallery or from taking a picture.
The selected picture (or new picture) does not show up in an Image control. I have not been able to find a solution through a web search. This code was copied from a tutorial. Putting a breakpoint at the NewImage.Source line shows that there is a non-null value for the currentimage.
Any help would be appreciated. The MS Xamarin Forums won't allow me to register or log in so I can't ask the question there.
Jim Durbin
durbinjw#gmail.com
Here is 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"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="MyStuff.PicListPage">
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="selectImageButton"
Text="Select Image"
Command="{Binding SelectImageCommand}"/>
<ToolbarItem x:Name="takePicButton"
Text="Take Picture"
Command="{Binding TakePictureCommand}"
CommandParameter="{Binding NewImage}"/>
<ToolbarItem x:Name="deleteButton"
Text="Delete"
Command="{Binding DeletePictureCommand}"
CommandParameter="{Binding SelectedImage}"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout>
<Label Text="Picture List page"
HorizontalOptions="Center"
FontSize="Large"
FontAttributes="Bold"/>
<Frame BorderColor="Black"
WidthRequest="350"
HeightRequest="350"
Margin="10,0,10,0">
<FlexLayout Direction="Row">
**<Image x:Name="NewImage"**
IsVisible="true"
Aspect="AspectFit"
Source="{Binding NewImmage, Mode=TwoWay}"
Margin="10,0,10,0"
WidthRequest="350"
HeightRequest="350"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
</Image>
</FlexLayout>
</Frame>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Here is the code behind - first the command code for selecting a picture
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
namespace MyStuff.ViewModel.Commands
{
public class SelectImageCommand : ICommand
{
private PicListPageVM viewmodel;
public event EventHandler CanExecuteChanged;
public SelectImageCommand(PicListPageVM viewmodel)
{
this.viewmodel = viewmodel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
viewmodel.SelectImage();
}
}
}
And here is the ViewModel code snippet that copies the image from the emulated Gallery which has 5 pictures in it.
using MyStuff.Model;
using MyStuff.ViewModel.Commands;
using Plugin.Media;
using Plugin.Media.Abstractions;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Xamarin.Forms;
namespace MyStuff.ViewModel
{
public class PicListPageVM : INotifyPropertyChanged
{
public ObservableCollection<PicList> PicLists { get; set; }
public TakePictureCommand TakePictureCommand { get; set; }
public SelectImageCommand SelectImageCommand { get; set; }
public DeletePictureCommand DeletePictureCommand { get; set; }
private Image newimage;
public Image NewImage
{
get { return newimage; }
set
{
newimage = value;
OnPropertyChanged("NewImage");
}
}
public PicListPageVM()
{
SelectImageCommand = new SelectImageCommand(this);
TakePictureCommand = new TakePictureCommand(this);
DeletePictureCommand = new DeletePictureCommand(this);
NewImage = new Image();
NewImage.IsOpaque = false;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public async void SelectImage()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await App.Current.MainPage.DisplayAlert("Information", "Can't select pictures on this device", "OK");
return;
}
var mediaoptions = new PickMediaOptions()
{
PhotoSize = PhotoSize.Small
};
var selectedImageFile = await CrossMedia.Current.PickPhotoAsync(mediaoptions);
if (selectedImageFile == null)
{
await App.Current.MainPage.DisplayAlert("Information", "No image selected.", "OK");
return;
}
**var currentimage = ImageSource.FromStream(() => selectedImageFile.GetStream());
NewImage.Source = currentimage;**
}
first, you misspelt the name of your property ("NewImage") in the binding (TwoWay binding also doesn't make sense here)
<Image x:Name="NewImage" Source="{Binding NewImmage, Mode=TwoWay}" .. />
second, in your VM NewImage is of type Image, which is not right. Image is a UI control, not a data type. Use string instead
private string newimage;
public string NewImage
{
get { return newimage; }
set
{
newimage = value;
OnPropertyChanged("NewImage");
}
}
finally, when you select your image set the VM property to the image path
NewImage = selectedImageFile.AlbumPath;
This issue is resolved. Thanks to #Jason and a little further hacking. The key issues were 1) a typo in the binding 2) The binding needed to point to a property that had datatype string instead of an Image UI element - which in turn pointed to a file path and 3) setting the bound string to point to the selectedImageFile.Path rather than to selectedImageFile.AlbumPath.
Related
=>Output image
My Label Binding in ListView does not read data from the AllNotes list, but it does show the length of input string.
Is it because my AllNotes list is empty or is there any error in my code?
I also don't find any error or exception while running the code too.
public ObservableCollection AllNotes { get; set; } = new
ObservableCollection();
#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:local="clr-
namespace:App19.ViewModel"
x:Class="App19.MainPage">
<ContentPage.BindingContext>
<local:MainPageViewModel/>
</ContentPage.BindingContext>
<Grid RowDefinitions="*,auto">
<ListView ItemsSource="{Binding AllNotes}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame>
<Label Text="{Binding .}" TextColor="Red"
FontSize="20" LineBreakMode="WordWrap" BackgroundColor="Purple"
HorizontalOptions="StartAndExpand"/>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Grid.Row="1">
<Entry Placeholder="Enter new user name: " x:Name="userEntry" Text="{Binding GetName}"/>
<Button Text="Delete" Command="{Binding Delete}"/>
<Button Text="Save" Command="{Binding Change}"/>
</StackLayout>
</Grid>
</ContentPage>
#C-Sharp
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
using System.Threading.Tasks;
using Xamarin.Essentials;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace App19.ViewModel
{
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainPageViewModel()
{
Delete = new Command(() =>
{
Name = string.Empty;
GetName = string.Empty;
});
Change = new Command(() =>
{
if (GetName != string.Empty)
{
Name = GetName;
AllNotes.Add(GetName);
GetName = string.Empty;
}
});
}
public ObservableCollection<string> AllNotes { get; set; } = new
ObservableCollection<string>();
public string GetName
{
get => name;
set
{
name = value;
var args = new PropertyChangedEventArgs(nameof(GetName));
PropertyChanged?.Invoke(this, args);
}
}
public string Name
{
get => name;
set
{
name = value;
var args = new PropertyChangedEventArgs(nameof(Name));
PropertyChanged?.Invoke(this, args);
}
}
public string name;
public Command Delete { get; }
public Command Change { get; }
}
}
You're setting the data in your AllNotes Observable Collection AFTER construction. This means you effectively want to update the contents of the ObservableCollection but you haven't triggered the PropertyChanged event in your set method.
Every time you wish to make a change to a field AFTER construction, you have to include;
PropertyChanged?.Invoke(this, args);
Make the following change to your code;
private ObservableCollection<string> _allNotes = new ObservableCollection<string>();
public ObservableCollection<string> AllNotes
{
get => _allNotes;
set
{
_allNotes = value;
var args = new PropertyChangedEventargs(nameof(AllNotes));
PropertyChanged?Invoke(this, args);
}
}
Depending on what your use case is, you could also opt to use CollectionView, but this is optional. it will reduce your code complexity and also included some additional Commands and features that ListView does not have.
Try replacing the listview with the collectionview. And also replace the ViewCell with a StackLayout or a Grid when using collectionview.
You can check the documentation here below.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout
I have already created all of the controls, and have tested that the controls are added as needed without using the binding.
The issue is as soon as I add the binding which is supposed to determine which control to add, the controls stop working as needed.
The list view control will be populated from a collection of a class, which will have an indicator field to determine which control needs to be loaded. The list view contains a 2nd user control which basically acts as a placeholder for the correct control, it has a bindable property of type text which is set to determine the correct control to be loaded.
Here is the XAML Code for the list view control
<ContentView.Content>
<StackLayout>
<Label Text="Binding Control Type"/>
<Entry x:Name="cntName"/>
<ListView x:Name="GroupedView" GroupDisplayBinding="{Binding Title}" HasUnevenRows="True" GroupShortNameBinding="{Binding ShortName}" IsGroupingEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Add Comment"/>
<MenuItem Text="Add Attachment"/>
</ViewCell.ContextActions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="7*"/>
</Grid.RowDefinitions>
<Label Text="{Binding QUESTION_ID}" Grid.Row="0" Grid.Column="0" VerticalTextAlignment="Center" FontSize="Medium"/>
<Label Text="{Binding QUESTION_DETAILS}" Grid.Row="1" Grid.Column="0" VerticalTextAlignment="Center" FontSize="Medium"/>
<con:ucListViewControls ControlType="{Binding QUESTION_ANSWERCONTROL}" Grid.Row="1" Grid.Column="1"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Add Comment"/>
<MenuItem Text="Add Attachment"/>
</ViewCell.ContextActions>
<StackLayout Orientation="Horizontal" Padding="5,5,5,5" BackgroundColor="#E2F5F9">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference this}, Path=Tapped}" CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
<Button Image="{Binding StateIcon}" BackgroundColor="Transparent" BorderColor="Transparent" BorderWidth="0"/>
<Label Text="{Binding Title}" TextColor="#005569" FontSize="15" VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>
</StackLayout>
</ContentView.Content>
And the code behind for the control
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ucExpandibleListView : ContentView
{
private ObservableCollection<dbQuestionGroup> _allGroups;
private ObservableCollection<dbQuestionGroup> _expandedGroups;
public ucExpandibleListView()
{
InitializeComponent();
Tapped = new Command(x => HeaderTapped(x));
_allGroups = new ObservableCollection<dbQuestionGroup>()
{
new dbQuestionGroup("Category 1", "C1", false)
{
new dbQuestionModel() { QUESTION_ID = 1, QUESTION_DETAILS = "Testing Question 1", QUESTION_ANSWERCONTROL = "RBL" },
new dbQuestionModel() { QUESTION_ID = 2, QUESTION_DETAILS = "Testing Question 2", QUESTION_ANSWERCONTROL = "" }
}
};
UpdateListContent();
}
private void UpdateListContent()
{
_expandedGroups = new ObservableCollection<dbQuestionGroup>();
foreach (dbQuestionGroup group in _allGroups)
{
dbQuestionGroup newGroup = new dbQuestionGroup(group.Title, group.ShortName, group.Expanded);
newGroup.QuestionCount = group.Count;
if (group.Expanded)
{
foreach (dbQuestionModel question in group)
{
newGroup.Add(question);
}
}
_expandedGroups.Add(newGroup);
}
GroupedView.ItemsSource = _expandedGroups;
}
public Command Tapped { get; set; }
private void HeaderTapped(object group)
{
var groupCat = (dbQuestionGroup)group;
int selectedIndex = _expandedGroups.IndexOf(groupCat);
if (groupCat.Expanded)
{
_allGroups[selectedIndex].Expanded = false;
}
else
{
_allGroups.ToList().ForEach(x => x.Expanded = false);
_allGroups[selectedIndex].Expanded = !_allGroups[selectedIndex].Expanded;
}
UpdateListContent();
}
}
Here is the XAML Code for the placeholder control
<ContentView.Content>
<StackLayout x:Name="stkPlaceholder">
</StackLayout>
</ContentView.Content>
And the code behind for the placeholder control
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ucListViewControls : ContentView, INotifyPropertyChanged
{
public ucListViewControls()
{
InitializeComponent();
}
#region Control Attributes
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
#endregion
#region Bindable Properties
public static readonly BindableProperty ControlTypeProperty = BindableProperty.Create(nameof(ControlType), typeof(string), typeof(ucListViewControls));
public string ControlType
{
get
{
return (string)GetValue(ControlTypeProperty);
}
set
{
SetValue(ControlTypeProperty, value);
AddControl();
NotifyPropertyChanged("ControlType");
}
}
#endregion
public void AddControl()
{
switch (ControlType)
{
case "RBL":
ucRadiobuttons radiobuttons = new ucRadiobuttons();
radiobuttons.lblTitle1 = "Yes";
radiobuttons.lblTitle2 = "No";
radiobuttons.lblTitle3 = "N/A";
radiobuttons.OnColor1 = Color.Green;
radiobuttons.OnColor2 = Color.Red;
radiobuttons.OnColor3 = Color.Transparent;
stkPlaceholder.Children.Add(radiobuttons);
break;
default:
Entry placeholder = new Entry();
stkPlaceholder.Children.Add(placeholder);
break;
}
}
}
I have tested that the controls are added without the binding, which works perfectly.
I have tried to rewrite the bindable property multiple times in case I missed something, I also could not find any post relating to something similar which wouldve helped me.
Any clues?
The first screenshot shows the expected output, and the seconds screenshots shows what happens when the binding is applied.
dbQuestionModel:
using System.Collections.Generic;
namespace PivotMobile_BusinessLayer.Models
{
public class dbQuestionModel
{
public int QUESTION_PK { get; set; }
public int QUESTION_ID { get; set; }
public string QUESTION_CATEGORY { get; set; }
public string QUESTION_DETAILS { get; set; }
public string QUESTION_TYPE { get; set; }
public string QUESTION_ANSWERCONTROL { get; set; }
public string QUESTION_COMMENT { get; set; }
public List<string> QUESTION_ATTACHMENTS { get; set; }
}
}
dbQuestionGroup:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace PivotMobile_BusinessLayer.Models
{
public class dbQuestionGroup : ObservableCollection<dbQuestionModel>, INotifyPropertyChanged
{
public static ObservableCollection<dbQuestionGroup> All { private set; get; }
private bool _expanded;
public string Title { get; set; }
public string ShortName { get; set; }
public bool Expanded
{
get
{
return _expanded;
}
set
{
if (_expanded != value)
{
_expanded = value;
OnPropertyChanged("Expanded");
OnPropertyChanged("StateIcon");
}
}
}
public string StateIcon
{
get
{
return Expanded ? "expanded_blue.png" : "collapsed_blue.png";
}
}
public int QuestionCount { get; set; }
public dbQuestionGroup(string title, string shortName, bool expanded = true)
{
Title = title;
ShortName = shortName;
Expanded = expanded;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ListView Page 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="PivotMobile.Views.ObservationsView"
xmlns:con="clr-namespace:PivotMobile.Controls">
<ContentPage.Content>
<StackLayout Margin="5">
<con:ucExpandibleListView/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
ListView Page Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace PivotMobile.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ObservationsView : ContentPage
{
public ObservationsView ()
{
InitializeComponent ();
}
}
}
Building my first app with Xamarian.Forms. I have my basic menu and home page built with some labels and a button so that I can bind some data and a method and to make sure the logic code for my game is working. I got my data bindings working as far as the labels go, they appear on screen. However I was unsure if my bindings weren't updating or if my command wasn't binding. So I commented out the ICommand, removed the binding and put the method to advance a turn into the code behind my xaml. Even after this, the data is not updating when the button is clicked which leads me to believe it is a problem with my OnPropertyChanged and the data bindings. I've searched the web and related questions, I've implemented a couple different ways of writing the gets and sets for the bindings, wrote my OnPropertyChanged function a few different ways and still nothing happens when the toolbar button on the home page is clicked.
Here is my HomePageViewModel.cs containing INotifyPropertyChanged
***Edited to reflect changes made since getting the day value to update:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using Engine;
using Xamarin.Forms;
namespace TestApp
{
public class HomePageViewModel : INotifyPropertyChanged
{
public static Player _player = World.Player1;
public string Day = World.TrueDay.ToString();
public string MoneyValue = Convert.ToInt32(Math.Floor(World.Player1.PlayerMoney)).ToString();
public string CurrentLocation = _player.CurrentLocation.Name;
public HomePageViewModel()
{
OnTurn = new Command(execute: On1Turn);
}
public ICommand OnTurn { get; private set; }
public string CurrentDay
{
get { return Day; }
set { Day = value; OnPropertyChanged(); }
}
public string Money
{
get { return MoneyValue; }
set { MoneyValue = value; OnPropertyChanged(); }
}
public string PlayerLocation
{
get { return CurrentLocation; }
set { CurrentLocation = value; OnPropertyChanged(); }
}
void On1Turn()
{
World.TrueDay = World.TrueDay + 1;
CurrentDay = World.TrueDay.ToString();
World.Player1.PlayerMoney = World.Player1.PlayerMoney + 1000;
MoneyValue = Convert.ToInt32(Math.Floor(World.Player1.PlayerMoney)).ToString();
OnPropertyChanged(Money);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here is my HomePage.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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local1="clr-namespace:TestApp"
mc:Ignorable="d"
x:Class="TestApp.HomePage">
<ContentPage.BindingContext>
<local1:HomePageViewModel/>
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="+24hrs" Clicked="ToolbarItem_Clicked" />
</ContentPage.ToolbarItems>
<StackLayout Padding="20">
<StackLayout Orientation="Horizontal">
<Label Text="Money:" HorizontalOptions="Start"/>
<Label x:Name="lblPlayerMoney" Text="{Binding Money, Mode=OneWay}" HorizontalOptions="FillAndExpand"/>
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="EndAndExpand" Margin="0,-40,0,0">
<Label Text="Current Location:" HorizontalOptions="CenterAndExpand"/>
<Label x:Name="lblPlayerLocation" Text="{Binding PlayerLocation, Mode=OneWay}" HorizontalOptions="CenterAndExpand"/>
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="StartAndExpand">
<Label Text="Current Day:" HorizontalOptions="CenterAndExpand" Margin="30,0,0,0"/>
<Label x:Name="lblCurrentDay" Text="{Binding CurrentDay, Mode=OneWay}" HorizontalOptions="CenterAndExpand"/>
</StackLayout>
</StackLayout>
</ContentPage>
And the HomePage.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Engine;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TestApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
//BindingContext = new HomePageViewModel();
}
private void ToolbarItem_Clicked(object sender, EventArgs e)
{
World.TrueDay = World.TrueDay + 1;
World.Player1.PlayerMoney = World.Player1.PlayerMoney + 1000;
}
}
}
Any insight into the issue would be greatly appreciated as I'm new to Xamarian.Forms and the OnPropertyChanged feature in general. Thanks for the time!
EDIT******
Here is the World.cs were I set the properties if it helps
using System;
using System.Collections.Generic;
using System.Text;
namespace Engine
{
public class World
{
public static decimal TrueDay = 1;
//public string LocationText = Player1.CurrentLocation.Name.ToString();
public static Player Player1;
public static readonly List<Location> Locations = new List<Location>();
public const int LOCATION_ID_OSHAWA = 1;
public const int LOCATION_ID_TORONTO = 2;
public static void GenerateWorld()
{
PopulateLocations();
Player1 = new Player("Jordan", LocationByID(LOCATION_ID_OSHAWA), 5000);
}
private static void PopulateLocations()
{
Location oshawa = new Location(LOCATION_ID_OSHAWA, "Oshawa");
Location toronto = new Location(LOCATION_ID_TORONTO, "Toronto");
Locations.Add(oshawa);
Locations.Add(toronto);
}
public static Location LocationByID(int id)
{
foreach (Location location in Locations)
{
if (location.ID == id)
{
return location;
}
}
return null;
}
}
}
Not too understanding logic about project, but if want to change model data can do as follow
HomePage.xaml.cs:
namespace TestApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HomePage : ContentPage
{
HomePageViewModel homePageViewModel = new HomePageViewModel();
public HomePage()
{
InitializeComponent();
BindingContext = homePageViewModel ;
}
private void ToolbarItem_Clicked(object sender, EventArgs e)
{
homePageViewModel.CurrentDay = xxx ;
homePageViewModel.xxxxx = xxxx;
//Something like this can change model data
}
}
}
Here is a sample data binding discussion can be refer to.
I'm trying to change an image source property on a ContentPage. I´m using a binding context to do this. But, even if I change the source in my model view, this don't update the image in my view.
UpdateMethod()
{
imageSource1 = imageSource[1];
}
public string ImageSource1
{
get
{
return imageSource1;
}
set
{
imageSource1 = value;
this.Notify("ImageSource1");
}
}
The XAML:
<ContentView HorizontalOptions="Center" Grid.Row="0" >
<Image ClassId = "1" Source="{Binding ImageSource1}" BindingContextChanged="Handle_BindingContextChanged">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnTapGestureRecognizerTappedCommand1}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</ContentView>
Image component accepts ImageSource(FileImageSource, StreamImageSource etc). Luckily ImageSource class have implicit operator against string which creates itself from string regarding format(url or path). Check below Example:
Xaml
<Image Source="{Binding ImagePath}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ImageTapCommand}" />
</Image.GestureRecognizers>
</Image>
ViewModel.cs:
public class SomeViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand ImageTapCommand { get; set; }
private string imagePath;
public string ImagePath
{
get { return imagePath; }
set
{
imagePath = value;
PropertyChanged(this, new PropertyChangedEventArgs("ImagePath"));
}
}
public SomeViewModel()
{
ImageTapCommand = new Command(CmdTapImage);
}
private void CmdTapImage()
{
ImagePath = YourNewImagePath;
}
}
When you are Binding the ImageSource use the Xamarin.Forms.ImageSource as the return type of your property. Or you can use it's derived classes like FileImageSource if you are specifying a filepath. Also make sure that the path is present in the native projects.
First add de ImageSource in your view model, don´t forget to include Xamarin.Forms dependencys...
private ImageSource _imageSource;
public ImageSource ImageSource
{
get { return _imageSource; }
set
{
_imageSource= value;
PropertyChanged(this, new PropertyChangedEventArgs("ImageSource"));
}
}
after this, include de source binding in you xaml file:
<Image Source="{Binding ImageSource}">
<!--<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ImageTapCommand}" />
</Image.GestureRecognizers>-->
</Image>
Here is a "prism example"
private ImageSource _imageSource;
public ImageSource ImageSource
{
get { return _imageSource; }
set { SetProperty(ref _imageSource, value); }
}
I'm trying to make a Xamarin.Forms project where I have a BoxView and an Entry field and a Button. I want to enter the name of a color into my Entry field, press the button, and have my BoxView change to the color that I input. Here is the code I have written till now:
Views/MainView.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="TestGround.MainView">
<ContentPage.Content>
<StackLayout VerticalOptions="Center">
<Label
Text="Enter a color:"
VerticalOptions="Center"
HorizontalOptions="Center"
/>
<BoxView
Color="{Binding Color}"
/>
<Entry
Text="{Binding Name}"
/>
<Button
Text="Enter"
Command="{Binding SetColorCommand}"
/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
ViewModels/MainViewModel.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace TestGround
{
public class MainViewModel :INotifyPropertyChanged
{
private string _color; //backing field for Greeting
public string Color //implementation for Greeting method
{
get { return _color; }
set
{
_color = value;
OnPropertyChanged ("Color"); //Notify view that change has taken place
}
}
public string Name { get; set; } //Name method for Entry field
public ICommand SetColorCommand { get; set; } //ICommand binds to buttons in XAML
public void SetColor() //Need a regular method to add to ICommand
{
Color = Name;
}
//Main VIEW MODEL
public MainViewModel ()
{
//Color = Name;
Name = "Enter color here";
SetColorCommand = new Command(SetColor); //Regular command added to ICommand
}
#region PropertyChangedRegion
public void OnPropertyChanged (string propertyName)
{
if (PropertyChanged != null)
PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Here is the error I get:
Java.Lang.RuntimeException: java.lang.reflect.InvocationTargetException
I want to know if my approach is wrong and how can I go about fixing it and making this pretty simple program.
According to the BoxView Documentation, the property "Color" must actually be a color... where as you have it defined as a string named color. Your types are mixed up. It should be something like Colors.Blue.
You can use class ColorTypeConverter for change string to Color.
I 've simplified your problem to this source code
//You simplified model
public class bModel : BindableObject
{
private Color _realColor;
public Color Color
{
get { return _realColor; }
set
{
_realColor = value;
OnPropertyChanged ("Color");
}
}
public string _stringColor;
public string StringColor {
get {
return _stringColor;
}
set {
_stringColor = value;
Color = (Color)(new ColorTypeConverter ()).ConvertFrom (_stringColor);
}
}
public bModel ()
{
StringColor = "Blue";
}
}
}
//Your simplified page 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="s2c.MyPage">
<ContentPage.Content>
<BoxView x:Name="box" Color="{Binding Color}"/>
</ContentPage.Content>
</ContentPage>
//Your simplified page csharp
public partial class MyPage : ContentPage
{
public MyPage ()
{
InitializeComponent ();
this.BindingContext = new bModel ();
}
}