I have a Page with a ViewModel as a BindingContext:
public Page()
{
InitializeComponent();
this.BindingContext = new ViewModel();
}
ViewModel has a command:
public class ViewModel : INotifyPropertyChanged
{
...
public ICommand SomeCommand { get; set; }
public ViewModel()
{
SomeCommand = new Command((object data) => {});
}
...
}
In my Page.xaml I use my custom View component which serves the only to be displayed and has an ability to be clicked:
<local:CircleView
Radius="20"
InnerText="Click me"
InnerTextSize="15"
TapCommand="{Binding SomeCommand}"
/>
In my CircleView.xaml.cs
...
public ICommand TapCommand { get; set; }
...
In my CircleView.xaml:
...
<TapGestureRecognizer
Command="{Binding Path=TapCommand, Source={x:Reference Name=CircleView}}"
CommandParameter="{Binding Path=InnerText, Source={x:Reference Name=CircleView}}"
/>
...
When I run a program I get an error "No property, bindable property, or event found for TapCommand, or mismatching...". How can I pass a command in XAML?
You should add your TapCommand as dependency property to your user control. Add this to your CircleView.xaml.cs and remove the previously defined TapCommand.
See also: dependency-properties-overview
//making is a bindab
public static readonly DependencyProperty TapCommandProperty =
DependencyProperty.Register("TapCommand", typeof(ICommand), typeof(CircleView)
/* possible more options here, see metadata overrides in msdn article*/);
public ICommand TapCommand
{
get { return (ICommand)GetValue(TapCommandProperty); }
set { SetValue(TapCommandProperty, value); }
}
Then, I am not sure, but since you are using the TapCommand in your TapGestureRecognizer I think you'll need to implement INotificationChanged on your CircleView as well.
You need to pass a reference to the ViewModel to the CircleView by adding a bindable property to the CircleView:
public static BindableProperty ParentBindingContextProperty =
BindableProperty.Create(nameof(ParentBindingContext), typeof(object),
typeof(CircleView), null);
public object ParentBindingContext
{
get { return GetValue(ParentBindingContextProperty); }
set { SetValue(ParentBindingContextProperty, value); }
}
You can then bind that in your xaml (note the x:Name must match the x:Reference):
<ContentView ... x:Name="Home" ... >
...
<local:CircleView ParentBindingContext="{Binding Source={x:Reference Home}, Path=BindingContext}"/>
And finally, bind your tap gesture to the command in your "parent" view model in your xaml in your CircleView:
<TapGestureRecognizer BindingContext="{Binding Source={x:Reference CircleView}, Path=ParentBindingContext}" Command="{Binding Path=TapCommand}" CommandParameter="{Binding Path=InnerText, Source={x:Reference Name=CircleView}}" />
You won't need the TapCommand in your CircleView.
Related
in Xamarin Forms I created a Page that contains a custom component which I want to feed a value just like this:
<c:CustomComponent Test="{Binding Test}" />
This, however, doesn't work. When I use a raw number instead of the Binding it works. The problem that I figured out was, that my custom component used a ViewModel / BindingContext. So when I remove the line where I assign my ViewModel to the BindingContext the Binding works.
why is that so and how can I use use both, the BindingContext as well as BindableProperty within my custom component? Or do I have to do everything within my code behind?
For reference some code example how I created the BindableProperty
public static readonly BindableProperty TestProperty = BindableProperty.Create(nameof(Test), typeof(int),
typeof(CustomComponent), propertyChanged: (bindable, oldVal, newVal) => {
Debug.WriteLine("TEST " + newVal);
});
public int Test {
get => (int)GetValue(TestProperty);
set => SetValue(TestProperty, value);
}
"my custom component used a ViewModel / BindingContext."
Its easier to create a reusable custom component, if it is "self-contained" - no BindingContext.
Constructor:
public CustomComponent()
{
InitializeComponent();
}
Move EVERYTHING you currently have in the component's viewmodel, into the xaml.cs code behind file.
Now in CustomComponent.xaml, give it a name (here theComponent):
<ContentView ...
x:Name="theComponent"
x:Class=...>
This comes in handy when component's xaml wants to bind to a property in itself:
<Label Text="{Binding TestString, Source={x:Reference theComponent}}" />
public string TestString
{
get => _testString;
set {
_testString = value;
OnPropertyChanged();
}
}
private string _testString = "test";
tl;dr: If component has an x:Name="theComponent" in its xaml, can use {Binding ..., Source={x:Reference theComponent}}, to refer to its own properties. No BindingContext needed.
If you want the component to have an associated ViewModel, with the above technique you don't have to set BindingContext to that VM. Do it like this:
public class MyViewModel
{
public string TestString
{
get => _testString;
set {
_testString = value;
OnPropertyChanged();
}
}
private string _testString = "test";
}
CustomComponent:
public MyViewModel VM { get; private set; }
public CustomComponent()
{
InitializeComponent();
VM = new MyViewModel();
}
Usage in xaml:
<Label Text="{Binding VM.TestString, Source={x:Reference theComponent}}" />
I am using Exceed IntegerUpDown control in my .xaml file. I want to bind IntegerUpDown value as a CommandParameter of a button.
I do not have any code behind files and this is a custom control xaml file. So i want to achieve this by only using xaml systax.
<DockPanel>
<xctk:IntegerUpDown x:Name="ExtraExpressionValue" Increment="1" FormatString="N0" AllowSpin="True" Width="70" Watermark="Numeric" AllowTextInput="False" Minimum="0" Value="999"/>
<Button Style="{StaticResource ContextMenuButton}" Margin="5,0,0,0" Content="Add" Command="{Binding SetExtaExpressionValueCommand}" CommandParameter="{Binding ElementName=ExtraExpressionValue,Path=Value}"/>
</DockPanel>
Above is my xaml code. this return 0 to command method.
My command class is as follows,
public class DesignItemCommands
{
private ICommand setExtaExpressionValueCommand;
public ICommand SetExtaExpressionValueCommand => setExtaExpressionValueCommand ?? (setExtaExpressionValueCommand = new CommandHandler(SetExtaExpressionValue, canExecute));
private bool canExecute;
public DesignItemCommands()
{
canExecute = true;
}
private void SetExtaExpressionValue(object parameter)
{
//I need parameter here..
}
}
Couldn't find a way on the requirement. Just posting here to help someone later on this issue.
I used a ViewModel Variable to bind IntegerUpDown control value.
<DockPanel>
<xctk:IntegerUpDown Increment="1" Value="{Binding ExtraExpressionValue}"/>
<Button Content="Add" Command="{Binding SetExtaExpressionValueCommand}"/>
</DockPanel>
My ViewModel is as follows,
public class DesignItemCommands
{
private ICommand setExtaExpressionValueCommand;
public ICommand SetExtaExpressionValueCommand => setExtaExpressionValueCommand ?? (setExtaExpressionValueCommand = new CommandHandler(SetExtaExpressionValue, canExecute));
private bool canExecute;
public int ExtraExpressionValue { get; set; }
public DesignItemCommands()
{
canExecute = true;
ExtraExpressionValue = 1;
}
private void SetExtaExpressionValue(object parameter)
{
//I can use value here using variable ExtraExpressionValue
}
}
Hope this helps someone later.
ICommand:
public class CMDAddEditUser : ICommand
{
public event EventHandler CanExecuteChanged;
public VMAddEditUser ViewModel { get; set;}
public CMDAddEditUser()
{
}
public CMDAddEditUser(VMAddEditUser vm)
{
ViewModel = vm;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
this.ViewModel.SimpleMethod();
}
}
ViewModel:
public class VMAddEditUser
{
private Employee _employee = new Employee();
private CMDAddEditUser Command { get; set; }
public VMAddEditUser()
{
Command = new CMDAddEditUser(this);
}
public string txtFirstName
{
get { return _employee.FirstName; }
set { _employee.FirstName = value; }
}
public void SimpleMethod()
{
txtFirstName = "abc";
}
}
XAML:
<Window x:Class="WPF.AddEditUserView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ViewModel;assembly=ViewModel"
Title="AddEditUserView" Height="392.329" Width="534.143">
<Grid Margin="0,0,2,-3">
<Grid.Resources>
<vm:VMAddEditUser x:Key="abx"/>
</Grid.Resources>
<Grid.DataContext>
<vm:VMAddEditUser/>
</Grid.DataContext>
<Button x:Name="btn" Content="Cancel" Command="{Binding SimpleMethod, Source={StaticResource abx}}"/>
</Grid>
</Window>
The CMDAddEditUser and VMAddEditUser is in the same project while the xaml is in a different project.
The .Execute(Object Parameter) of the ICommand doesn't seem to work. I can't bind the SimpleMethod with the button that I have. When I type the Command Binding in the xaml file, the auto-complete/suggestions only shows the txtFirstName and not the SimpleMethod. I can't figure out why the SimpleMethod can't be binded and can't be found. What did I do wrong in this code?
First: All properties you want your view to be able to bind to, must be public. Since view binds to property, which is instance of ICommand implementation, property must be public, so view can access it. However, your SimpleMethod() can be private if you don't wanna expose it to the outside world, that why you have command calling it instead of letting view directly call it.
Second: You set you grids DataContext to your 'VMEditUser' class, so in binding there is no need to specify Source, DataContext is source.
This is a silverlight application, Im using asynchronous filtering for an autocompletebox, the problem is that so far i fail to bind FilterAsyncCommand property from the behaviour with the corresponding ViewModel property.
Following is the xaml declaration for the control in the View:
<controls:ChildWindow x:Class="MyApp.Views.View1"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:fw ="clr-namespace:NetBoxSys.Views"
...
>
<sdk:AutoCompleteBox
MinimumPrefixLength="3" MinimumPopulateDelay="150"
SelectedItem="{Binding Path=...}" ItemsSource="{Binding Path=...}" >
<i:Interaction.Behaviors>
<fw:FilterAsyncBehavior FilterAsyncCommand="{Binding Path=FilterAsyncCommand}" />
</i:Interaction.Behaviors>
</sdk:AutoCompleteBox>
</controls:ChildWindow>
...ViewModel code:
private ICommand filterAsyncCommand;
public ICommand FilterAsyncCommand {
get { return filterAsyncCommand; }
set {
filterAsyncCommand = value;
this.OnPropertyChanged( "FilterAsyncCommand" );
}
}
...and this is how im loading the View
var view = new View1();
view.DataContext = new ViewModel1();
view.Show(); //Modal
I have tried this syntax too but does not work either:
<fw:FilterAsyncBehavior FilterAsyncCommand="{Binding FilterAsyncCommand}" />
Need advice for this kind of binding.
UPDATE:
Behaviour code:
public class FilterAsyncBehavior : Behavior<AutoCompleteBox>
{
public ICommand FilterAsyncCommand
{
get
{
return (ICommand)GetValue(FilterAsyncCommandProperty);
}
set
{
SetValue(FilterAsyncCommandProperty, value);
}
}
public static readonly DependencyProperty FilterAsyncCommandProperty = DependencyProperty.Register("FilterAsyncCommand",
typeof(ICommand),
typeof(FilterAsyncBehavior),
new PropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
// handle the populating event of the associated auto complete box
AssociatedObject.Populating += AssociatedObject_Populating;
}
protected override void OnDetaching()
{
// detach the event handler
AssociatedObject.Populating -= AssociatedObject_Populating;
base.OnDetaching();
}
private void AssociatedObject_Populating(object sender, PopulatingEventArgs e)
{
// get the command
ICommand filterCommand = FilterAsyncCommand;
if (filterCommand != null)
{
// create the parameters for the command
var parameters = new FilterAsyncParameters(AssociatedObject.PopulateComplete, e.Parameter);
// execute command
filterCommand.Execute(parameters);
// cancel the population of the auto complete box
e.Cancel = true;
}
}
}
ViewModel code:
public class ViewModel1 : ViewModel, IViewModel {
public ViewModel1() {
//Initializing the command in constructor
FilterAsyncCommand = new DelegateCommand<FilterAsyncParameters>( ExecuteFilterAsync );
}
private void ExecuteFilterAsync( FilterAsyncParameters args ) {
....
}
private ICommand filterAsyncCommand;
public ICommand FilterAsyncCommand {
get { return filterAsyncCommand; }
set {
filterAsyncCommand = value;
this.OnPropertyChanged( "FilterAsyncCommand" );
}
}
}
I find the solution for my problem. I give up with passing the ViewModel at runtime, so i changed to the following implementation of the MVVM pattern
<controls:ChildWindow x:Class="MyApp.Views.View1"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:fw ="clr-namespace:NetBoxSys.Views"
...
>
<controls:ChildWindow.Resources>
<viewModel:ViewModelRecepcionOCViewModelLocator x:Key="viewModelLocator"/>
</controls:ChildWindow.Resources>
<controls:ChildWindow.DataContext>
<Binding Source="{StaticResource viewModelLocator}" Path="ViewModel" />
</controls:ChildWindow.DataContext>
<sdk:AutoCompleteBox HorizontalAlignment="Stretch" Margin="2" x:Name="remitente_Autocomplete" VerticalAlignment="Center" Grid.Column="1" Grid.Row="4"
MinimumPrefixLength="3" MinimumPopulateDelay="150" Padding="0" Height="23" TabIndex="1" Text="{Binding Path=NombreRemitente,Mode=TwoWay}"
SelectedItem="{Binding Path=SelectedRemitente,Mode=TwoWay}" ItemsSource="{Binding Path=Remitentes}" >
<i:Interaction.Behaviors>
<fw:FilterAsyncBehavior FilterAsyncCommand="{Binding Source={StaticResource viewModelLocator},Path=ViewModel.FilterAsyncCommand}" />
</i:Interaction.Behaviors>
</sdk:AutoCompleteBox>
</controls:ChildWindow>
The important part here is the binding definition for FilterAsyncCommand.
And RecepcionOCViewModelLocator is defined like this
public class RecepcionOCViewModelLocator : ViewModelLocatorBase<IRecepcionOCViewModel>
{
public IRecepcionOCViewModel ViewModel{get;set;}
}
I have using a DelegateCommand and i want to pass the UserControl with Command.
#region OpenViewCommand
private DelegateCommand<UserControl> _openViewCommand;
public ICommand OpenViewCommand
{
get
{
if (_openViewCommand == null)
_openViewCommand = new DelegateCommand<UserControl>(new Action<UserControl>(OpenView));
return _openViewCommand;
}
}
public void OpenView(UserControl ViewName)
{
UserControl ctrl = (UserControl)ViewName;
JIMS.Controls.Message.Show(ViewName.ToString());
}
#endregion
Command in XAML
<Button Name="btnStockGroups" Command="{Binding OpenViewCommand}" CommandParameter="JIMS.View.Stock.StockGroups">stock group</Button>
If you give your UserControl an x:Name (e.g. "MyView"), you should be able to do something like this:
<Button Name="btnStockGroups"
Command="{Binding OpenViewCommand}"
CommandParameter="{Binding ElementName=MyView}">