Pass CommandParameter to Command in Silverlight using MVVM - mvvm

I'm just learning Silverlight and looking at MVVM and Commanding.
Ok, so I have seen the basic RelayCommand implementation:
public class RelayCommand : ICommand
{
private readonly Action _handler;
private bool _isEnabled;
public RelayCommand(Action handler)
{
_handler = handler;
}
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if (value != _isEnabled)
{
_isEnabled = value;
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler();
}
}
How can I pass a parameter down with a Command using this?
I've seen that you can pass a CommandParameter like this:
<Button Command="{Binding SomeCommand}" CommandParameter="{Binding SomeCommandParameter}" ... />
In my ViewModel, I need to create the Command, but RelayCommand is expecting an Action delegate. Can I implement RelayCommand<T> using Action<T> - if so, how do I do it and how to I use it?
Can anyone give me any practical examples on CommandParameters with MVVM that don't involve using 3rd-party libraries (e.g. MVVM Light) as I want to understand it fully before using existing libraries.
Thanks.

public class Command : ICommand
{
public event EventHandler CanExecuteChanged;
Predicate<Object> _canExecute = null;
Action<Object> _executeAction = null;
public Command(Predicate<Object> canExecute, Action<object> executeAction)
{
_canExecute = canExecute;
_executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute(parameter);
return true;
}
public void UpdateCanExecuteState()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
if (_executeAction != null)
_executeAction(parameter);
UpdateCanExecuteState();
}
}
Is the Base Class for Commands
And this is your Command Property in ViewModel:
private ICommand yourCommand; ....
public ICommand YourCommand
{
get
{
if (yourCommand == null)
{
yourCommand = new Command( //class above
p => true, // predicate to check "CanExecute" e.g. my_var != null
p => yourCommandFunction(param1, param2));
}
return yourCommand;
}
}
in XAML set Binding to Command Property like:
<Button Command="{Binding Path=YourCommand}" .../>

Maybe this article explains what you're looking for. I also had the same problem just minutes ago.
http://www.eggheadcafe.com/sample-code/SilverlightWPFandXAML/76e6b583-edb1-4e23-95f6-7ad8510c0f88/pass-command-parameter-to-relaycommand.aspx

Related

How to data bind class A{prop masterProp} to a class B{ bound prop to class A masterProp}

I am learning Xamarin forms.
I wanted to be able to bind some ViewModel to some values in a DataManager class (Singleton).
Say the Singleton is a BleManager and all ViewModel need to use it to get or set some information to or from the BLE device.
I Know how to bind my VM to the XAML view code.
But now I need to be able to get the viewModels to update local Data when the BLEManager updates some info, like battery level.
so for example (semi sudo-code).
public class BLEInterface: INotifyPropertyChanged
{
protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public async Task<float> GetDeviceName()
{
await Task.Delay(1000);
return float.random(0f:100f)
}
}
public sealed class BLEManager: INotifyPropertyChanged
{
private static BLEManager shared;
private static object objectLockCheck = new Object();
private BLEInterface BleModel { get; set; }
private float batteryLevel;
public float BatteryLevel {
get => batteryLevel;
set {
batteryLevel = value;
OnNotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private BLEManager()
{
}
public async Task ConnectToBLE()
{
await Task.Delay(1000);
BleModel = new TestModel();
}
public async void GetBatteryLevel()
{
BatteryLevel = await bleModel.GetDeviceName();
}
public static BLEManager Shared
{
get
{
if(shared == null)
{
lock (objectLockCheck)
{
if(shared == null)
{
shared = new BLEManager();
}
}
}
return shared;
}
}
}
The Part I need to know is how can my viewModel hook on changes from the BleManager battery level property.

Xamarin Forms MVVM Databinding failing when I'm binding to a single object

I'm having an issue with data not binding correctly on a details page when I have clicked through from a ListView via a button. The ListView binds perfectly and the object gets passed through to the details page. The Id of the object is read and a full version of the object is called from an API and set to a new instance of the object. When I add a breakpoint, the full object is available, but Labels on the view aren't populated. Here is the ViewModel:
DetailsViewModel.cs
public class DetailsViewModel
{
public Deal Deal { get; set; }
public int DealId { get; set; }
public DetailsViewModel(int id)
{
Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if(deal != null)
{
Deal = deal;
}
}
}
The codebehind looks like this:
DetailPage.Xaml.cs
DetailsViewModel viewModel;
int dealId;
public DetailPage(int id)
{
InitializeComponent();
dealId = id;
viewModel = new DetailsViewModel(dealId);
BindingContext = viewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.GetDeal();
}
And the Xaml file is
DetailPage.Xaml
<ContentPage.Content>
<ScrollView>
<StackLayout x:Name="detailsLayout">
<Label Text="{Binding Deal.Name}" />
</StackLayout>
</ScrollView>
</ContentPage.Content>
When I put a breakpoint in Deal = deal on DetailsViewModel, the Deal object exists and has the correct data, but I just get a blank screen. I have tried Labels with Text="{Binding Name}" and Text="{Binding Deal.Name}".
I have also tried manually creating a deal in the GetDeal function of the ViewModel and still nothing is bound.
1) Ensure your property Notifies the UI of a change implementing the INotifyPropertyChanged interface. See https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
2) Ensure the set is done on the UI thread using Device.BeginInvokeOnMainThread. https://learn.microsoft.com/fr-fr/dotnet/api/xamarin.forms.device.begininvokeonmainthread?view=xamarin-forms
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace YourNamespace
{
public class DetailsViewModel : INotifyPropertyChanged
{
private Deal _deal;
public Deal Deal
{
get => _deal;
set
{
if (_deal != value)
{
_deal = value;
OnPropertyChanged();
}
}
}
public int DealId { get; set; }
public DetailsViewModel(int id)
{
//!! useless assignation
//Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if (deal != null)
{
//Ensure we are on UI thread
Device.BeginInvokeOnMainThread(() => Deal = deal);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

UWP Binding to AutoSuggestBox in MVVM

i am invoking the QuerySubmitted command of the AutoSuggestBox control in UWP.
the command binds to ICommand in the view model.
the problem is it requires to accept AutoSuggestBoxQuerySubmittedEventArgs which is pure UI and it's not acceptable in MVVM.
my code looks like that:
<AutoSuggestBox Name="SearchAutoSuggestBox"
PlaceholderText="Search by keywords"
QueryIcon="Find"
>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="QuerySubmitted">
<core:InvokeCommandAction Command="{x:Bind ViewModel.SearchCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AutoSuggestBox>
and my view model looks like that:
public DelegateCommand<AutoSuggestBoxQuerySubmittedEventArgs> SearchCommand { get; }
public MainPageViewModel()
{
SearchCommand = new DelegateCommand<AutoSuggestBoxQuerySubmittedEventArgs>(ExecuteMethod);
}
private void ExecuteMethod(AutoSuggestBoxQuerySubmittedEventArgs o)
{
// CODE HERE
}
ofcours AutoSuggestBoxQuerySubmittedEventArgs is not acceptable in the view model.
looking for alternatives...
same goes to SuggestionChosen...
InvokeCommandAction has a parameter named InputConverter which you can use to convert the event args to some other parameter that can be passed to your ViewModel.
First create a IValueConverter class to extract what you need from your event args like this:-
public class AutoSuggestQueryParameterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
// cast value to whatever EventArgs class you are expecting here
var args = (AutoSuggestBoxQuerySubmittedEventArgs)value;
// return what you need from the args
return (string)args.ChosenSuggestion;
}
}
Then use that converter in your XAML like this:
<Page.Resources>
<converters:AutoSuggestQueryParameterConverter x:Key="ArgsConverter" />
</Page.Resources>
<AutoSuggestBox Name="SearchAutoSuggestBox"
PlaceholderText="Search by keywords"
QueryIcon="Find">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="QuerySubmitted">
<core:InvokeCommandAction
Command="{x:Bind ViewModel.SearchCommand}"
InputConverter="{StaticResource ArgsConverter}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AutoSuggestBox>
Finally in your viewmodel change your command to accept a string as parameter. So you would have the following in your vm:
public DelegateCommand<string> SearchCommand { get; }
public MainPageViewModel()
{
SearchCommand = new DelegateCommand<string>(ExecuteMethod);
}
private void ExecuteMethod(string o)
{
// CODE HERE
}
You can bind the search string (Text property) to a view model property and the events to parameter-less methods. This way the view model wont have to deal with UI objects:
XAML:
<AutoSuggestBox Header="What's your name?"
TextChanged="{x:Bind ViewModel.FilterUsuals}"
QuerySubmitted="{x:Bind ViewModel.ProcessQuery}"
SuggestionChosen="{x:Bind ViewModel.ProcessChoice}"
ItemsSource="{x:Bind ViewModel.Usuals, Mode=OneWay}"
Text="{x:Bind ViewModel.SearchText, Mode=TwoWay}"
QueryIcon="Find" />
Code behind:
public class MainPageViewModel : SomeViewModelBaseClass
{
/* Boilerplate code and constructor not included */
private string _SearchText;
public string SearchText {/* getter and setter INotyfyPropertyChange compliant */ }
private List<string> _Usuals; // Initialized on constructor
public string Usuals {/* getter and setter INotyfyPropertyChange compliant */ }
public void FilterUsuals()
{
// the search string is in SearchText Example:
Usuals = _UsualsStore.Where(u => u.Contains(_SearchText.ToLower())).ToList();
}
public void ProcessQuery() { /* TODO - search string is in SearchText */ }
public void ProcessChoice() { /* TODO - search string is in SearchText */ }
}
If you don't mind doing non pure MVVM way.
MainPage.xaml :
<AutoSuggestBox Name="SearchAutoSuggestBox"
PlaceholderText="Search by keywords"
QueryIcon="Find" QuerySubmitted="{x:Bind ViewModel.SearchQuerySubmitted}" IsEnabled="{x:Bind ViewModel.CanExecuteSearchCommand, Mode=TwoWay}"
>
</AutoSuggestBox>
MainPageViewModel.cs :
public class MainPageViewModel : INotifyPropertyChanged
{
private bool _canExecuteSearchCommand;
public MainPageViewModel()
{
this.CanExecuteSearchCommand = true;
}
public bool CanExecuteSearchCommand
{
get { return _canExecuteSearchCommand; }
set
{
bool changed = _canExecuteSearchCommand != value;
_canExecuteSearchCommand = value;
if(changed)
this.OnPropertyChanged();
}
}
public void SearchQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
// Just example disabling SearchBox
this.CanExecuteSearchCommand = false;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainPage.cs :
MainPageViewModel ViewModel = new MainPageViewModel();
UWP Binding Command/Delegate to AutoSuggestBox in MVVM
For UWP Mobile Application
Make a DelegateCommand class
public class DelegateCommand<T> : ICommand
{
private readonly Action<T> executeAction;
Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<T> executeAction)
: this(executeAction, null)
{
//var a = ((Page)(((Func<object, bool>)(executeAction.Target)).Target)).Name;
//((ViewModel.VMBranchSelection)(executeAction.Target)).;
}
public DelegateCommand(Action<T> executeAction, Func<object, bool> canExecute)
{
this.executeAction = executeAction;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null ? true : canExecute(parameter);
}
public void Execute(object parameter)
{
executeAction((T)parameter);
}
public void RaiseCanExecuteChanged()
{
EventHandler handler = this.CanExecuteChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
In View Model
public ICommand SuggessionSelectCity_QuerySubmitted
{
get { return new DelegateCommand<AutoSuggestBoxQuerySubmittedEventArgs>(this.SuggessionSelectCityQuerySubmitted); }
}
private void SuggessionSelectCityQuerySubmitted(AutoSuggestBoxQuerySubmittedEventArgs obj)
{
if (obj.ChosenSuggestion != null)
{
AutosuggestionTextBoxName.Text = ((ModelName) (obj.ChosenSuggestion)).Model's Property name;
//or
AutosuggestionTextBoxName.Text =(obj.ChosenSuggestion).property name
}
else
{
}
}
In XAML Code
<AutoSuggestBox Grid.Column="1" x:Name="SuggessionSelectCity"
PlaceholderText="Search by keywords" QueryIcon="Find"
ItemsSource="{Binding PApplicantCityList}"
HorizontalAlignment="Center" VerticalAlignment="Center" DisplayMemberPath="Description" Width="250" Height="45">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="TextChanged">
<Core:EventTriggerBehavior.Actions>
<Core:InvokeCommandAction Command="{Binding SuggessionSelectCityTextChange}"/>
</Core:EventTriggerBehavior.Actions>
</Core:EventTriggerBehavior>
<Core:EventTriggerBehavior EventName="QuerySubmitted">
<Core:EventTriggerBehavior.Actions>
<Core:InvokeCommandAction Command="{Binding SuggessionSelectCity_QuerySubmitted}"/>
</Core:EventTriggerBehavior.Actions>
</Core:EventTriggerBehavior>
<Core:EventTriggerBehavior EventName="SuggestionChosen">
<Core:EventTriggerBehavior.Actions>
<Core:InvokeCommandAction Command="{Binding SuggessionSelectCitySuggestionChosen}"/>
</Core:EventTriggerBehavior.Actions>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</AutoSuggestBox>
</Grid>
Create a list in View Model for Autosuggestion TextBox Itemssource
private ObservableCollection<ResultMasterModel> ApplicantCityList;
public ObservableCollection<ResultMasterModel> PApplicantCityList
{
get { return ApplicantCityList; }
set { this.SetProperty(ref this.ApplicantCityList, value); }
}
add some hard code value in above list
Create a Model In Model Folder
public class ResultMasterModel
{
public string Code { get; set; }
public string Description { get; set; }
}

MVVM Light navigation service - parameters in windows rt

I see in the MVVM Light toolkit there is a NavigationService. This is very straightforward when you are just navigating to a view and passing no data i.e.
_navigationService.Navigate(typeof(DetailView));
There is an overload on the Navigate method which takes an object parameter i.e.
_navigationService.Navigate(typeof(DetailView), this.SelectedItem.Id);
Question is, how do I then retrieve the parameter in my target view model?
I ended up with the following
public interface IView
{
IViewModel ViewModel { get; }
}
public interface IViewModel
{
void Initialise(object parameter);
}
Then in my view's code behind;
public sealed partial class MyView : IView
{
public ViewModel.IViewModel ViewModel
{
get { return this.DataContext as IViewModel; }
}
public MyView()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter != null)
{
this.ViewModel.Initialise(e.Parameter);
}
}
}
From there I can now use the parameter in my ViewModel where I am implementing the IViewModel interface.
public void Initialise(object parameter)
{
if (parameter != null)
{
// do something
}
}

responding to the model's property changes in asp.net mvc2

I am having a model not in EF, but in plain text. I have to have the updated events handled for each of the model's properties so that i can log their changes.
Is there a way for this to be achieved.
Implement the INotifyPropertyChanged interface.
A simple example:
using System.ComponentModel;
public class MyModel : INotifyPropertyChanged
{
string _myProperty;
public event PropertyChangedEventHandler PropertyChanged;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
You can use it like...
public class Test
{
public static void Main()
{
var model = new MyModel();
model.PropertyChanged += new PropertyChangedEventHandler(LogChange);
model.MyProperty="apples";
model.MyProperty="oranges";
model.MyProperty="pears";
}
public static void LogChange(object sender, PropertyChangedEventArgs args)
{
Console.WriteLine(args.PropertyName + " has changed!");
Console.WriteLine("New value: "
+ sender.GetType().GetProperty(args.PropertyName)
.GetValue(sender, null));
}
}