got a question on how to convert this code behind into a mvvm style, Here's a sample
void CameraView_MediaCaptured(object sender, MediaCapturedEventArgs e)
{
switch (cameraView.CaptureMode)
{
default:
case CameraCaptureMode.Default:
case CameraCaptureMode.Photo:
previewVideo.IsVisible = false;
previewPicture.IsVisible = true;
previewPicture.Rotation = e.Rotation;
previewPicture.Source = e.Image;
doCameraThings.Text = "Snap Picture";
break;
case CameraCaptureMode.Video:
previewPicture.IsVisible = false;
previewVideo.IsVisible = true;
previewVideo.Source = e.Video;
doCameraThings.Text = "Start Recording";
break;
}
}
A combination of Jason's and Karas's answer .
Bind with the corresponding properties between xaml and viewmodel .
//xaml
IsVisible = "{Binding IsVisible}"
Implement INotifyPropertyChanged inside viewmodel .
public class ViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool isVisible;
public bool IsVisible {
get
{
return isVisible;
}
set
{
isVisible = value;
NotifyPropertyChanged();
}
}
Replace the event with EventToCommandBehavior and change the properties value.
//xaml
<xct:EventToCommandBehavior
EventName="MediaCaptured"
Command="{Binding MyCommand}" />
//viewmodel
public ICommand MyCommand { get; set; }
public ViewModel()
{
MyCommand = new Command((obj)=> {
var cameraView = obj as CameraView;
switch (cameraView.CaptureMode)
{
default:
case CameraCaptureMode.Default:
case CameraCaptureMode.Photo:
IsVisible = false;
break;
case CameraCaptureMode.Video:
IsVisible = false;
break;
}
});
}
First you create a BaseViewModel, which you can now inherit on every other ViewModel. Then your ViewModel and bind it via BindingContext = new YourPageVM() to your page via YourPage.cs. You can now create properties in your ViewModel and Binding them in the XAML. e.g .:
Here Is the BaseViewModel:
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string
propertyName =
"")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
}
//In Your VM:
public class YourPageVM : BaseViewModel
{
bool isVisPreVideo;
public bool IsVisPreVideo{
get=> isVisPreVideo;
set=> SetProperty(ref isVisPreVideo,value);}
//set the Value in Constructor or in Your Method
public YourPageVM()
{
IsVisPreVideo = false;
}
//........
}
//At Xaml:
xmlns:viewmodel="clr-namespace:YourProject.ViewModel"
x:DataType="viewmodel:YourPageVM"
IsVisible = "{Binding IsVisPreVideo}"
And this you can do also with the other values Rotation, Source and Text.
Related
I have this code that sets a label to one or another color depending on a property called IsEnabled:
public class LinkLabel : Label
{
public LinkLabel()
{
PropertyChanged += LinkLabel_PropertyChanged;
SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
}
private void LinkLabel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsEnabled")
{
var label = sender as LinkLabel;
var newValue = label.IsEnabled;
if ((bool)newValue)
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelDisabledColor");
}
}
}
What I would like to do is to add another property called IsImportant
so that if that is set to true and IsEnabled = true, then the LinkLabelColor would be set to red
Can someone give me some suggestions on how I can do this? I do know how to add things like bindable properties but I am not sure in this case how to combine it with the code that I already have?
Add a new property
public static readonly BindableProperty IsImportantProperty = BindableProperty.Create(nameof(IsImportant), typeof(bool), typeof(CustomLabel), false,/*Can remove property changed as well*/ propertyChanged: IsImportantPropertyChanged);
public bool IsImportant
{
get { return (bool)GetValue(IsImportantProperty); }
set { SetValue(IsImportantProperty, value); }
}
New Method UPDATE
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (propertyName == IsEnabledProperty.PropertyName || propertyName == IsImportantProperty.PropertyName)
{
if (this.IsImportant && this.IsEnabled)
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelDisabledColor");
}
}
Some issues with the other answer not working. I revisited the code and came up with this which works good:
public class LinkLabel : Label
{
public LinkLabel()
{
SetDynamicResource(Label.FontFamilyProperty, "Default-Regular");
SetDynamicResource(Label.FontSizeProperty, "LabelTextFontSize");
SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
VerticalOptions = LayoutOptions.CenterAndExpand;
VerticalTextAlignment = TextAlignment.Center;
}
public static readonly BindableProperty IsImportantProperty =
BindableProperty.Create(nameof(IsImportant), typeof(bool), typeof(LinkLabel), false);
public bool IsImportant
{
get { return (bool)GetValue(IsImportantProperty); }
set { SetValue(IsImportantProperty, value); }
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == IsEnabledProperty.PropertyName ||
propertyName == IsImportantProperty.PropertyName)
{
if (this.IsEnabled) {
if (this.IsImportant)
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelImportantColor");
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
}
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelDisabledColor");
}
}
}
Binding command to a Button - yields no effect (Xamarin, MVVM):
notes:
Pressing the Button and nothing happens: no CanExecute check occur.
Binding a Button in a ContentPage which is Part of a Tabbed-Template
functionality check and the rest of related MVVM binding works well: Defined a clicked-event and manually triggered the command from the code behind.
//Could someone see the reason?// editted
editted, new:
What would be a good practice when CanExecute relies on fields of a compound data type that are updated independently ? (*can take of the command parameter which is the compound data type, which is accessible to the command directly through the VM).
xaml for the View:
<ContentPage.Content>
<StackLayout>
<Entry Placeholder="Notes"/>
<Entry x:Name="courseIDEntry"
Text="{Binding CourseID, Mode=TwoWay}"
IsReadOnly="{Binding !ExistUnit}"
Placeholder="CourseID *"/>
<Entry x:Name="unitIDEntry"
Text="{Binding UnitID, Mode=TwoWay}"
IsReadOnly="{Binding !ExistUnit}"
Placeholder="UnitID *"/>enter code here
<Label Text="* Fields are mandatory"/>
<Button x:Name="AddSave"
Text="{Binding CommandText}"
Command="{Binding AddSaveCMD}"
CommandParameter="{Binding EdittedUnit}"/>
<!--Clicked="AddSave_Clicked"/>-->
</StackLayout>
</ContentPage.Content>enter code here
c# code behind for the view (*including the Button-Clicked check for)
public partial class EditUnitPage : ContentPage
{
EditUnitViewModel editUVM;
public EditUnitPage()
{
InitializeComponent();
editUVM = new EditUnitViewModel();
BindingContext = editUVM;
}
public EditUnitPage(Unit6 unitSelected) : this()
{
if (unitSelected != null)
{
editUVM.EdittedUnit = unitSelected;
editUVM.ExistUnit = true;
}
}
protected override void OnAppearing()
{
base.OnAppearing();
}
//private void AddSave_Clicked(object sender, EventArgs e)
//{
// if (editUVM.AddSaveCMD.CanExecute(editUVM.EdittedUnit))
// {
// editUVM.AddSaveCMD.Execute(null);
// }
//}
}
C# MyCommand (newbie. using ICommand and not the Command Class)
public class AddSaveUnitCommand : ICommand
{
public EditUnitViewModel EditUVM { get; set; }
public event EventHandler CanExecuteChanged;
public AddSaveUnitCommand(EditUnitViewModel euvm)
{
EditUVM = euvm;
}
public bool CanExecute(object parameter)
{
var editted = parameter as Unit6;
if (editted != null )
{
if (!string.IsNullOrEmpty(editted.CourseID) || !string.IsNullOrEmpty(editted.UnitID))
return true;
}
return false;
}
public void Execute(object parameterf)
{
EditUVM.AddSaveUnitAsync();
}
}
c# for VM (BaseViewModel implements INotify)
public class EditUnitViewModel : BaseViewModel
{
public AddSaveUnitCommand AddSaveCMD { get; set; }
private Unit6 edittedUnit;
public Unit6 EdittedUnit
{
get { return edittedUnit; }
set { edittedUnit = value; OnPropertyChanged(); }
}
private bool existUnit;
public bool ExistUnit
{
get { return existUnit; }
set
{
existUnit = value;
//OnPropertyChanged();
}
}
public string CommandText
{
get { return ExistUnit? "Save": "Add"; }
}
public string CourseID
{
get { return EdittedUnit.CourseID; }
set { EdittedUnit.CourseID = value; OnPropertyChanged(); }
}
public string UnitID
{
get { return EdittedUnit.UnitID; }
set { EdittedUnit.UnitID = value; OnPropertyChanged(); }
}
public EditUnitViewModel()
{
EdittedUnit = new Unit6();
AddSaveCMD = new AddSaveUnitCommand(this);
}
public async void AddSaveUnitAsync()
{
var curPage = App.Current.MainPage;
try
{
switch (ExistUnit)
{
case false: //insert new unit to the DB
EdittedUnit.UnitKey = ""; //Todo: look for more elegant of assigning auto value to property
Unit6.Insert(EdittedUnit);
break;
case true: //update details on existing unit
EdittedUnit.UnitKey = ""; //Todo: look for more elegant of assigning auto value to property
Unit6.Update(EdittedUnit);
break;
}
await curPage.DisplayAlert("Success", "Unit was succesffuly updateded", "OK");
}
catch
{
await curPage.DisplayAlert("Error", "Unit was not updated", "OK");
}
finally
{
EdittedUnit = null;
await curPage.Navigation.PushAsync(new MyTabbedPage());
}
}
}
xaml for the TabbedPage:
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:P205.Views"
x:Class="P205.Views.MyTabbedPage">
<views:UnitsPage Title="Units" />
<views:EditUnitPage x:Name="editOrAddUnit" Title="Edit U"/>
<views:DBChangesPage Title="Edit DB"/>
<views:CoursesPage Title="Course"/>
<ContentPage Padding="10">
</ContentPage>
When your ViewModel defines a property of type ICommand, the ViewModel must also contain or reference a class that implements the ICommand interface. This class must contain or reference the Execute and CanExecute methods, and fire the CanExecuteChanged event whenever the CanExecute method might return a different value.
So you could try change like below:
public class AddSaveUnitCommand : ICommand
{
public EditUnitViewModel EditUVM { get; set; }
public event EventHandler CanExecuteChanged;
public AddSaveUnitCommand(EditUnitViewModel euvm)
{
EditUVM = euvm;
}
public bool CanExecute(object parameter)
{
var editted = parameter as Unit6;
if (editted != null )
{
if (!string.IsNullOrEmpty(editted.CourseID) || !string.IsNullOrEmpty(editted.UnitID))
return true;
}
return false;
}
public void Execute(object parameterf)
{
EditUVM.AddSaveUnitAsync();
CanExecuteChanged?.Invoke(this, EventArgs.Empty); //add this line.
}
}
I am trying to make simple app which will provide features to read/write data to database trough an Web API.
I have view model which is bind to view, but it is not working properly after web api get request, even that call was successfully done. I've tried to check value with display alert, value is correct, but it is not presented in view part, exactly in one label. Here is my code:
<?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="SOSEUApp.Pages.NotePage"
Title="Dnevnik">
<ContentPage.ToolbarItems>
<ToolbarItem Text="GET" Clicked="ToolbarItem_Clicked"></ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}"></ActivityIndicator>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Date,StringFormat='Date: {0}'}"></Label>
</StackLayout>
<StackLayout>
</StackLayout>
</StackLayout>
</ContentPage.Content>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NotePage : ContentPage
{
NoteViewModel nvm = new NoteViewModel();
public NotePage()
{
InitializeComponent();
BindingContext = nvm;
}
private async void ToolbarItem_Clicked(object sender, EventArgs e)
{
nvm.IsBusy = true;
nvm.Notes = await App.NotesWebApiService.GetAll(nvm.CurrentActiveNote.Route);
nvm.GetLastNote();
nvm.IsBusy = false;
await DisplayAlert("Info", nvm.Date.ToString(), "Close");
}
}
public class NoteViewModel : BaseViewModel
{
IList<Note> notes = new List<Note>();
public IList<Note> Notes
{
get { return notes; }
set { SetProperty(ref notes, value); }
}
private Note currentActiveNote = new Note();
public Note CurrentActiveNote { get { return currentActiveNote; } }
public string Date { get { return currentActiveNote.Date.ToString("dd.MM.yyyy"); } }
public string OrderedNumber
{
get { return currentActiveNote.OrderNumber.ToString(); }
set
{
string v = currentActiveNote.OrderNumber.ToString();
SetProperty(ref v, value);
currentActiveNote.OrderNumber = Convert.ToInt16(v);
}
}
public string Description
{
get { return currentActiveNote.Description; }
set
{
string v = currentActiveNote.Description;
SetProperty(ref v, value);
currentActiveNote.Description = v;
}
}
public void GetLastNote()
{
notes.OrderBy(a => a.Date);
currentActiveNote = notes.Last();
}
}
public class BaseViewModel : DataModel, INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Thank you in advance!
just called SetProperty method and passed desired values there. It works
Said as Jason, you need to fire a PropertyChanged event when Date changes.Here is official document for reference.
Generally, usually writed in Set methods.As follow:
private string propertyname;
public string PropertyName
{
set { SetProperty(ref propertyname, value); }
get { return propertyname; }
}
Or write as follow:
public string PropertyName
{
set
{
if (propertyname!= value)
{
propertyname= value;
OnPropertyChanged("PropertyName");
}
}
get
{
return propertyname;
}
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When data of model change , this will be invoked automatically.
I am trying to get some strings into current.properties but am not sure how to do so the right way.
Right now i am creating a Label with the binding and then afterwards getting the label.text into the property but it seems to a bit stupid:
var userLabel = new Label {};
userLabel.SetBinding(Label.TextProperty, "name");
Application.Current.Properties["studentName"] = userLabel.Text;
Looking forward to an easier way ;-)
Here is a workaround:
class MyViewModel : INotifyPropertyChanged
{
string text;
public event PropertyChangedEventHandler PropertyChanged;
public MyViewModel()
{
Text = "York";
}
public string Text
{
set
{
if (text != value)
{
text = value;
OnPropertyChanged("Text");
Application.Current.Properties["Text"] = text;
}
}
get
{
return text;
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Set BindingContext:
protected override void OnAppearing()
{
base.OnAppearing();
MyViewModel mainPageViewModel = new MyViewModel();
BindingContext = mainPageViewModel;
}
Use it in your label:
<Label Text="{Binding Text}">
For more detailed information, you could refer to: Xamarin Forms Data Binding Basics.
I have a custom checkbox control that I created with an ICommand property and the corresponding bindable property (my checkbox is a Xamarin.Forms XAML Page), the code is:
CheckBox.xaml
<Image x:Name="imgCheckBox"
WidthRequest="20"
HeightRequest="20"/>
<Label x:Name="lblCheckBox"
TextColor="Black"
VerticalOptions="CenterAndExpand"/>
<TapGestureRecognizer Tapped="OnCheckBoxTapped"/>
CheckBox.xaml.cs
public partial class CheckBox : ContentView
{
private static ImageSource uncheckedImage;
private static ImageSource checkedImage;
public CheckBox()
{
InitializeComponent();
uncheckedImage = ImageSource.FromResource("cbUnchecked.png");
checkedImage = ImageSource.FromResource("cbChecked.png");
imgCheckBox.Source = uncheckedImage;
}
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create<CheckBox, bool>(
checkbox =>
checkbox.IsChecked,
false,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, newValue);
}
});
public bool IsChecked
{
set { SetValue(IsCheckedProperty, value); }
get { return (bool)GetValue(IsCheckedProperty); }
}
void OnCheckBoxTapped(object sender, EventArgs args)
{
IsChecked = !IsChecked;
if (IsChecked)
{
imgCheckBox.Source = checkedImage;
}
else
{
imgCheckBox.Source = uncheckedImage;
}
}
public static readonly BindableProperty CheckBoxCommandProperty =
BindableProperty.Create<CheckBox, ICommand>(
checkbox =>
checkbox.CheckBoxCommand,
null,
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, checkbox.IsChecked);
}
});
public event EventHandler<bool> CheckedChanged;
public ICommand CheckBoxCommand
{
get { return (ICommand)GetValue(CheckBoxCommandProperty); }
set { SetValue(CheckBoxCommandProperty, value); }
}
}
This checkbox implementation is on another Page called TermsAndConditionsPage, that is also a a Xamarin.Forms XAML Page, the code of the implementation is:
<toolkit:CheckBox Text="{Binding txtCheckBox}"
FontSize="Small"
CheckBoxCommand="{Binding OnCheckBoxTapChanged}"
IsChecked="{Binding IsCheckedChanged, Mode=TwoWay}"/>
<Button Text="Next"
Command="{Binding Next_OnClick}"
IsEnabled="{Binding Next_IsEnabled}"
HorizontalOptions="CenterAndExpand"
Clicked="OnNextClicked"/>
The Code Behind of this page is empty (Constructur with InitializeComponent()).
I also have the ViewModel of this page with this code:
TermsAndConditionsViewModel.cs
private string _txtCheckBox;
public string txtCheckBox
{ get { return _txtCheckBox; }
set
{
_txtCheckBox = value;
OnPropertyChanged("txtCheckBox");
}
}
private bool _Next_IsEnabled;
public bool Next_IsEnabled
{
get { return _Next_IsEnabled; }
set
{
_Next_IsEnabled = value;
OnPropertyChanged("Next_IsEnabled");
}
}
private bool _IsCheckedChanged;
public bool IsCheckedChanged
{
get { return _IsCheckedChanged; }
set
{
_IsCheckedChanged = value;
OnPropertyChanged("IsCheckedChanged");
}
}
public ICommand Next_OnClick { get; set; }
public ICommand OnCheckBoxTapChanged { get; set; }
public TermsAndConditionsViewModel()
{
txtCheckBox = "I agree with the terms and conditions";
Next_OnClick = new Command(NextClicked);
OnCheckBoxTapChanged = new Command(CheckBoxTapped);
}
private void CheckBoxTapped()
{
if (IsCheckedChanged)
{ Next_IsEnabled = true; }
else
{ Next_IsEnabled = false; }
}
private void NextClicked()
{ App.Current.MainPage = new Views.HelloWorld(); }
#region INPC
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
Now, the question time: the problem I'm having is the CheckBoxTapped Command is not working, I mean, it doesn't do anything, although the checkbox image changes every time I touch it, it does not change the Next_IsEnabled property of my button. I'd like to know what I am missing here to make this command work properly.
EDIT
What I'm looking for is a Command that behaves similarly to the one that Buttons have.
Thanks all for your time!
Since the original answer is now obsolete, here is the new method:
using System.Windows.Input;
public partial class MyControlExample : ContentView
{
// BindableProperty implementation
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(MyControlExample), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Helper method for invoking commands safely
public static void Execute(ICommand command)
{
if (command == null) return;
if (command.CanExecute(null))
{
command.Execute(null);
}
}
public MyControlExample()
{
InitializeComponent();
}
// this is the command that gets bound by the control in the view
// (ie. a Button, TapRecognizer, or MR.Gestures)
public Command OnTap => new Command(() => Execute(Command));
}
Something like that (pseudocode):
public class YourClassName : View
{
public YourClassName()
{
var gestureRecognizer = new TapGestureRecognizer();
gestureRecognizer.Tapped += (s, e) => {
if (Command != null && Command.CanExecute(null)) {
Command.Execute(null);
}
};
var label = new Label();
label.GestureRecognizers.Add(gestureRecognizer);
}
public static readonly BindableProperty CommandProperty =
BindableProperty.Create<YourClassName, ICommand>(x => x.Command, null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}