Xamarin - jak powiązać CommandParameter z obiektem kontrolki

0

Witam.
mam taki kod:

<StackLayout>
     <si:SignaturePadCanvasView x:Name="hSignature" HeightRequest="88" BackgroundColor="White"/>
     <uc:SimpleButton Text="Zapisz" Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.ExecuteSaveSignatureCommand}" CommandParameter=""/>
</StackLayout>

Są one jako jeden z wielu itemów w liście. Po kliknięciu w przycisk muszę wywołać metodę z kontrolki SignaturePadCanvasView. Jak mogę powiązać CommandParameter SimpleButton z obiektem SignaturePadCanvasView (a nie BindingContext) ???

2

Kombinujesz jak koń pod górkę. Nie idź na ślepo w MVVM. Zrób to w code-behind. Wszystko, co ma związek z View, możesz bez problemów robić w code-behind. Wszystko, co ma związek z logiką (nie widokową) i danymi - robisz przez ViewModel.

A więc załatw to normalnie w code-behind.

0

Tak właśnie zrobiłem, bo nic innego nie wykminiłem:

<!-- Signature image -->
                                        <StackLayout>
                                            <StackLayout.IsVisible>
                                                <Binding Path="Signature">
                                                    <Binding.Converter>
                                                        <conv:IsNullToBooleanConverter IsNull="False"/>
                                                    </Binding.Converter>
                                                </Binding>
                                            </StackLayout.IsVisible>
                                            <Image HeightRequest="88">
                                                <Image.Source>
                                                    <Binding Path="Signature">
                                                        <Binding.Converter>
                                                            <conv:StreamToImageSourceConverter/>
                                                        </Binding.Converter>
                                                    </Binding>
                                                </Image.Source>
                                            </Image>
                                            <uc:SimpleButton x:Name="hClearSignature" Text="Wyczyść" Clicked="hClearSignature_Clicked"/>
                                        </StackLayout>
                                        <!-- Signature creator -->
                                        <Grid HorizontalOptions="FillAndExpand">
                                            <Grid.IsVisible>
                                                <Binding Path="Signature">
                                                    <Binding.Converter>
                                                        <conv:IsNullToBooleanConverter IsNull="True"/>
                                                    </Binding.Converter>
                                                </Binding>
                                            </Grid.IsVisible>
                                            <Grid>
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="96"/>
                                                    <RowDefinition Height="auto"/>
                                                </Grid.RowDefinitions>
                                                <si:SignaturePadCanvasView BindingContextChanged="hSignature_BindingContextChanged" x:Name="hSignature" BackgroundColor="White"/>
                                                <uc:SimpleButton Grid.Row="1" x:Name="hSaveSignature" Text="Zapisz" Clicked="hSaveSignature_Clicked"/>
                                            </Grid>
                                        </Grid>

xaml.cs

public partial class FillTaskFormPage : ContentPage
    {
        public FillTaskFormPage()
        {
            SignatureList = new List<SignaturePadCanvasView>();
            InitializeComponent();
            hViewModel.Model = this;
        }
        protected override void OnAppearing()
        {
            base.OnAppearing();
            SignatureList.Clear();
            hViewModel.OnAppearing();
        }
        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            hViewModel.OnDisappearing();
            SignatureList.Clear();
        }

        List<SignaturePadCanvasView> SignatureList { get; }
        private void hSignature_BindingContextChanged(object sender, EventArgs e)
        {
            if((sender as SignaturePadCanvasView).BindingContext != null)
                SignatureList.Add(sender as SignaturePadCanvasView);
            Debug.WriteLine($"========================= ({SignatureList.Count().ToString()}) > { ((sender as SignaturePadCanvasView).BindingContext as TaskFormModelElement).Question}");
        }

        private async void hSaveSignature_Clicked(object sender, EventArgs e)
        {
            TaskFormModelElement model = (sender as SimpleButton).BindingContext as TaskFormModelElement;
            SignaturePadCanvasView canvas = SignatureList.FirstOrDefault(x => x.BindingContext as TaskFormModelElement == model);
            await hViewModel.SaveSignature(canvas, model);
            canvas.Clear();
        }

        private async void hClearSignature_Clicked(object sender, EventArgs e)
        {
            TaskFormModelElement model = (sender as SimpleButton).BindingContext as TaskFormModelElement;
            SignaturePadCanvasView canvas = SignatureList.FirstOrDefault(x => x.BindingContext as TaskFormModelElement == model);
            await hViewModel.ClearSignature(canvas, model);
        }
    }

viewmodel:

public async Task SaveSignature(SignaturePadCanvasView canvas, TaskFormModelElement model)
        {
            try
            {
                Stream stream = await canvas.GetImageStreamAsync(SignatureImageFormat.Png, true, false);
                model.Signature = stream;

            }
            catch (Exception e)
            {
                UserDialogs.Instance.Toast($"Błąd: {e.Message}", new TimeSpan(0, 0, 3));
            }
        }
        public async Task ClearSignature(SignaturePadCanvasView canvas, TaskFormModelElement model)
        {
            try
            {
                model.Signature = null;
            }
            catch (Exception e)
            {
                UserDialogs.Instance.Toast($"Błąd: {e.Message}", new TimeSpan(0, 0, 3));
            }
        }

Converter:

public class StreamToImageSourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                MemoryStream destination = new MemoryStream();
                using (Stream stream = (Stream)value)
                {
                    if (stream != null && stream.CanRead)
                    {
                        stream.Position = 0;
                        stream.CopyTo(destination);
                    }
                    else
                        return null;
                }
                if (destination != null)
                    destination.Position = 0;
                return ImageSource.FromStream(() => destination);
            }
            catch(Exception e)
            {
                Debug.WriteLine(e.Message);
                return null;
            }
            
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Z tym rozwiązaniem wszystko ładnie działa dopóki nie obrócę ekranu telefonu (zmiana orientacji). Gdy obrócę ekran telefonu (zmienię orientację), konsola wykrywa zmianę BindingContext. Jakim cudem?
Zrobiłem sobie mały teścik:

private void hSignature_BindingContextChanged(object sender, EventArgs e)
        {
            if((sender as SignaturePadCanvasView).BindingContext != null)
                SignatureList.Add(sender as SignaturePadCanvasView);

            Debug.WriteLine($"============================== hSignature BindingContextChanged:");
            var context = (sender as SignaturePadCanvasView).BindingContext;
            if(context == null)
                Debug.WriteLine($"============== Type: NULL");
            else
            {
                Debug.WriteLine($"============== Type: {context.GetType()}");
                Debug.WriteLine($"============== ElementType: {(context as TaskFormModelElement).ElementType}");
                Debug.WriteLine($"============== Question: {(context as TaskFormModelElement).Question}");
            }    
        }

I teraz ciekawostka. Po otwarciu okna kontrolki i konteksty wczytują się prawidłowo:
[0:] StreamToImageSourceConverter > stream NULL or CantRead
[0:] ============================== hSignature BindingContextChanged:
[0:] ============== viewModel: Invalid ElementType

[0:] StreamToImageSourceConverter > stream NULL or CantRead
[0:] ============================== hSignature BindingContextChanged:
[0:] ============== viewModel: Invalid ElementType

[0:] StreamToImageSourceConverter > stream NULL or CantRead
[0:] ============================== hSignature BindingContextChanged:
[0:] ============== viewModel: Invalid ElementType

[0:] StreamToImageSourceConverter > stream NULL or CantRead
[0:] ============================== hSignature BindingContextChanged:
[0:] ============== viewModel: Invalid ElementType

[0:] StreamToImageSourceConverter > stream NULL or CantRead
[0:] ============================== hSignature BindingContextChanged:
[0:] ============== AddedToList:
[0:] ============== ElementType: Signature
[0:] ============== Question: Podpis klienta

Ten ostatni to właśnie właściwy typ gdzie faktycznie będziemy coś pisać w kontrolce. I teraz nic nie rysując, nic nie klikając po prostu obracam ekran i mam:
[EGL_emulation] eglMakeCurrent: 0xec905720: ver 2 0 (tinfo 0xd0fac690)
[EGL_emulation] eglMakeCurrent: 0xec905720: ver 2 0 (tinfo 0xd0fac690)

Z racji, że komórka jest teraz poziomo muszę przewinąć trochę w dół i gdy mój element hSignature jest widoczny na ekranie dostaję na konsoli zmianę BindingContext
[0:] StreamToImageSourceConverter > stream NULL or CantRead
[0:] ============================== hSignature BindingContextChanged:
[0:] ============== viewModel: Invalid ElementType

[0:] StreamToImageSourceConverter > stream NULL or CantRead
[0:] ============================== hSignature BindingContextChanged:
[0:] ============== AddedToList:
[0:] ============== ElementType: Signature
[0:] ============== Question: Podpis klienta

Co może być grane?

1 użytkowników online, w tym zalogowanych: 0, gości: 1