如何在Xamarin.Forms中为Image控件设置双向绑定?

时间:2018-04-05 13:56:06

标签: c# xamarin.forms xamarin.ios

我有一张带图片的自定义视图MyPhotoView。我已经加载了图像。现在我想在继续按钮点击时更改图像。我尝试使用TwoWay Binding并设置Binding属性的值,但单击Continue按钮时图像不会改变。

上一页按钮单击 //从上一页的ViewModel调用(从图库中获取图像)

List<Image> currentOriginalImages = new List<Image>();
foreach (var item in _pictureSources)
{
    currentOriginalImages.Add(new Image() { Source = item });
}
var viewModel = new CropPhotoViewModel(currentOriginalImages);

XAML中使用的自定义控件MyPhotoView

public class MyPhotoView : View
{
    public static readonly BindableProperty CropTopLeftXProperty =
        BindableProperty.Create(nameof(CropTopLeftX), typeof (float), typeof (MyPhotoView), 0f, BindingMode.TwoWay);

    public static readonly BindableProperty CropTopLeftYProperty =
        BindableProperty.Create(nameof(CropTopLeftY), typeof (float), typeof (MyPhotoView), 0f, BindingMode.TwoWay);

    public static readonly BindableProperty CropWidthProperty =
        BindableProperty.Create(nameof(CropWidth), typeof (float), typeof (MyPhotoView), 0f, BindingMode.TwoWay);

    public static readonly BindableProperty CropHeightProperty =
        BindableProperty.Create(nameof(CropHeight), typeof (float), typeof (MyPhotoView), 0f, BindingMode.TwoWay);

    public static readonly BindableProperty OriginalImageProperty =
        BindableProperty.Create(nameof(OriginalImage), typeof (Image), typeof (MyPhotoView),null,BindingMode.TwoWay);

    public float CropTopLeftX
    {
        get { return (float) GetValue(CropTopLeftXProperty); }
        set { SetValue(CropTopLeftXProperty, value); }
    }

    public float CropTopLeftY
    {
        get { return (float) GetValue(CropTopLeftYProperty); }
        set { SetValue(CropTopLeftYProperty, value); }
    }

    public float CropWidth
    {
        get { return (float) GetValue(CropWidthProperty); }
        set { SetValue(CropWidthProperty, value); }
    }

    public float CropHeight
    {
        get { return (float) GetValue(CropHeightProperty); }
        set { SetValue(CropHeightProperty, value); }
    }

    public Image OriginalImage
    {
        get { return (Image) GetValue(OriginalImageProperty); }
        set { SetValue(OriginalImageProperty, value); }
    }
}

XAML:

<?xml version="1.0" encoding="UTF-8"?>
<local:ContentPageWithCustomBackButton
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MyProject;assembly=MyProject"
    x:Class="MyProject.CropPhotoPage"
    Title="Upload Photo">
    <ContentPage.ToolbarItems>
        <ToolbarItem Icon="icon-nav-back" Order="Primary" Priority="0" Command="{Binding GoBackCommand}" />
    </ContentPage.ToolbarItems>
    <ContentPage.Content>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="auto" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="auto" />
            </Grid.ColumnDefinitions>

            <local:MyPhotoView Grid.Row="0" Grid.ColumnSpan="3"
                                 x:Name="MyPhotoView"
                                 OriginalImage="{Binding OriginalImage}"
                                 CropTopLeftX="{Binding CropTopLeftX}"
                                 CropTopLeftY="{Binding CropTopLeftY}"
                                 CropWidth="{Binding CropWidth}"
                                 CropHeight="{Binding CropHeight}" />

            <Button Grid.Row="1" Grid.Column="0" Margin="20,0,0,19"
                    Text="Cancel" Clicked="CancelClicked" />

            <Button Grid.Row="1" Grid.Column="2" Margin="0,0,20,19" Clicked="ContinueClicked"
                      Text="Continue" Command="{Binding ContinueCommand}" />

        </Grid>
    </ContentPage.Content>
</local:ContentPageWithCustomBackButton>

CS页

public partial class CropPhotoPage : ContentPageWithCustomBackButton
{
    public CropPhotoPage()
    {
        InitializeComponent();
        BindingContext = App.Locator.CropPhoto;
    }

    public CropPhotoPage(CropPhotoViewModel bindingContext)
    {
        InitializeComponent();
        BindingContext =  bindingContext;
    }

    private void CancelClicked(object sender, EventArgs e)
    {
        MyPhotoView.ResetCrop();
    }

    private void ContinueClicked(object sender, EventArgs e)
    {
        MyPhotoView.ApplyCrop();
    }
}

视图模型

public CropPhotoViewModel(List<Image> images)
{
    ContinueCommand = new RelayCommand(async () => await ContinueCommandExecute());
    _images = images;
    OriginalImage = images[0];  
}
public Image OriginalImage { get; set; }
public List<Image> _images { get; set; }
public float CropTopLeftX { get; set; }
public float CropTopLeftY { get; set; }
public float CropWidth { get; set; }
public float CropHeight { get; set; }
public int CurrentImageCounter { get; set; }

private async Task ContinueCommandExecute()
{
    //crop the image
    if(CurrentImageCounter == _images.Count)
        return;
    else
    {
        var croppedImage = _cropImageService.CropImageWithRect(_images[CurrentImageCounter],
               new Rectangle(CropTopLeftX, CropTopLeftY, CropWidth, CropHeight));
        CurrentImageCounter++;

        //I want to change image here but it doesn't change even it is 2 way binding
        OriginalImage = _images[CurrentImageCounter];
        //I also want to set all 4 parameter of CropTopLeftX/Y/Width/Heigh here
    }
}

我已点击OriginalImage按钮将下一张图片设置为Continue属性但不会更改。我还想设置CropTopLeftX / Y / Width / Heigh

的所有4个参数

有人可以指导我吗?

2 个答案:

答案 0 :(得分:0)

您的viewmodel需要实现INotifyPropertyChanged接口:

YourViewModel : BaseClass, INotifyPropertyChanged 

(...)

public event PropertyChangedEventHandler PropertyChanged = delegate { };

public void OnPropertyChanged([CallerMemberName]string propertyName="")
{
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

然后你需要通知所有被绑定到你的财产的物品关于它被改变的事实:

Image _originalImage;
public Image OriginalImage
{
    get => _originalImage;
    set
    {
         _originalImage = value;
         OnPropertyChanged();
    }
}

您需要在绑定到的每个属性集中调用OnPropertyChanged方法。

答案 1 :(得分:0)

它没有变化,因为你没有通知变化。您的ViewModel需要从 public ActionResult NoAccess() { Response.TrySkipIisCustomErrors = true; Response.StatusCode = 403; return View(); } 继承并在属性的setter中实现通知。

INotifyPropertyChanged