I want to avoid repeating Source={RelativeSource AncestorType={x:Type vm:MainViewModel}}
in the following.
<SwipeView ... xmlns:vm="clr-namespace:Todo.ViewModel">
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem Text="Delete"
Command="{Binding DeleteCommand,Source={RelativeSource AncestorType={x:Type vm:MainViewModel}}}" />
</SwipeItems>
</SwipeView.LeftItems>
<Grid Padding="0,5">
<Frame >
<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand,Source={RelativeSource AncestorType={x:Type vm:MainViewModel}}}"/>
</Frame.GestureRecognizers>
</Frame>
</Grid>
</SwipeView>
I do the following but it does not work as expected.
<SwipeView ... xmlns:vm="clr-namespace:Todo.ViewModel"
BindingContext="{Binding Source={RelativeSource AncestorType={x:Type vm:MainViewModel}}}"
>
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem Text="Delete" Command="{Binding DeleteCommand}" />
</SwipeItems>
</SwipeView.LeftItems>
<Grid Padding="0,5">
<Frame >
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" />
</Frame.GestureRecognizers>
</Frame>
</Grid>
</SwipeView>
Repo
https://github.com/pstricks-fans/Todo
Here are the relevant parts:
MySwipeView:
public partial class MySwipeView : SwipeView
{
public MySwipeView()
{
InitializeComponent();
}
}
<SwipeView ...
x:Class="Todo.CustomControls.MySwipeView"
xmlns:vm="clr-namespace:Todo.ViewModel"
>
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem
Text="Delete"
Command="{Binding DeleteCommand,Source={RelativeSource AncestorType={x:Type vm:MainViewModel}}}"
CommandParameter="{Binding .}"/>
</SwipeItems>
</SwipeView.LeftItems>
<Grid Padding="0,5">
<Frame >
<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand,Source={RelativeSource AncestorType={x:Type vm:MainViewModel}}}"
CommandParameter="{Binding .}"/>
</Frame.GestureRecognizers>
<Label Text="{Binding .}" FontSize="24"/>
</Frame>
</Grid>
</SwipeView>
MainPage:
public partial class MainPage : ContentPage
{
public MainPage(MainViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
}
<ContentPage ...
xmlns:local="clr-namespace:Todo.CustomControls"
xmlns:vm="using:Todo.ViewModel"
x:DataType="vm:MainViewModel"
>
<Grid ... >
<CollectionView ... >
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type x:String}">
<local:MySwipeView />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
MainViewModel:
public partial class MainViewModel : ObservableObject
{
[RelayCommand]
void Delete(string s){}
[RelayCommand]
async Task Tap(string s){}
}
CodePudding user response:
In your case, SwipeView is being used inside an ItemTemplate
. You MUST NOT change its BindingContext; that has to be the associated Item.
Therefore, your original goal is NOT POSSIBLE; we can simplify the "Source" expression, but we cannot eliminate it.
Simplest I know is:
Command="{Binding VM.DeleteCommand, Source={x:Reference thePage}}"
Explanation: On "thePage", finds property "VM", which contains a property "DeleteCommand". Make the following changes to MainPage
.
<ContentPage
...
x:Name="thePage"
x:Class="MainPage">
class MainPage : ContentPage
{
// You can change this name. Be sure to use same name in XAML above.
public property MainViewModel VM { get; set; }
public MainPage(MainViewModel vm)
{
InitializeComponent();
// Put this line BEFORE set BindingContext. Used by XAML.
VM = vm;
BindingContext = vm;
}
CodePudding user response:
At first, the format of binding to an ancestor in the official document is something like {Binding Source={RelativeSource AncestorType={x:Type local:PeopleViewModel}}, Path=DeleteEmployeeCommand}
And then you can try to set the SwipeView's binding context in the construction method instead of the binding way you used.
I have done a sample to test, and the binding worked well:
The MySwipeView.cs :
public partial class MySwipeView : SwipeView
{
public ICommand TestCommand { get; private set; }
public MySwipeView()
{
InitializeComponent();
TestCommand = new Command<string>(Test);
//BindingContext = this;
BindingContext = new MyViewModel();
}
void Test(string print)
{
Debug.WriteLine("============" print);
}
}
The MySwipeView.xaml:
<SwipeView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAppTest.MySwipeView"
>
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem
Text="Delete"
Command="{Binding DeleteCommand}"
CommandParameter="xxxxxxxxx"/>
</SwipeItems>
</SwipeView.LeftItems>
<Grid Padding="0,5">
<Frame >
<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="xxxxxxxx"/>
</Frame.GestureRecognizers>
<Label Text="xxxxxxxx" FontSize="24"/>
</Frame>
</Grid>
</SwipeView>
The MainPage.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp21"
x:Class="MauiApp21.MainPage">
<HorizontalStackLayout>
<local:MySwipeView/>
</HorizontalStackLayout>
</ContentPage>
The MyViewModel.cs:
public partial class MyViewModel : ObservableObject
{
[RelayCommand]
void Delete(string value) { Debug.WriteLine("===========Delete"); }
[RelayCommand]
void Tap(string value) { Debug.WriteLine("===============Tap"); }
}
No matter the BindingContext
is this
or the MyViewModel
, the command will run successfully.