将 XAMLCropControl ImageSource 绑定到 ViewModel 中的 ImageSource 属性 失败

Binding XAMLCropControl ImageSource to ImageSource property in ViewModel failed

我正在使用 XamlCropControl 并尝试在我的视图模型中绑定它的 ImageSource 属性。

    private ImageSource source;
    public ImageSource Source
    {
        get { return source; }
        set { SetProperty(ref source, value); }
    }

图片是从用户图库中挑选的 StorageFile

    private async void setImageSource()
    {
        if (ImageStorageFile != null)
        {
            var imageProperties = await ImageStorageFile.Properties.GetImagePropertiesAsync();
            WriteableBitmap wb = new WriteableBitmap((int)imageProperties.Width, (int)imageProperties.Height);
            IRandomAccessStream fileStream = await ImageStorageFile.OpenAsync(FileAccessMode.Read);
            wb.SetSource(fileStream);
            Source = wb;
            Show();
        }
    }

在我的 XAML 我有以下内容

  <xamlcrop:CropControl Grid.Row="1" x:Name="cropControl" ImageSource="{Binding Source}" DesiredAspectRatio="{Binding AspectRatio, Mode=TwoWay}" />

但是图像没有显示。但是如果我使用示例中的路径,例如

 <xamlcrop:CropControl x:Name="Crop" ImageSource="ms-appx:///Assets/wrench.jpg" />

有效。我在 CropControl.cs 中设置了一个断点,实际上 Writeablebitmap 被传递给了依赖项 属性 但它没有显示。我错过了什么?

假设您已正确完成所有关于数据绑定的事情,那么根据您提供的 XamlCropControl 的开源代码,您已将 WriteableBitmap 设置为 SourceCropControl

可以参考CropControl的源码,在它的Setup()DoFullLayout()方法中,是用来在这个控件上渲染图片源的,代码如下这个:

public void Setup()
{
    // Check for loaded and template succesfully applied
    if (!_isLoaded ||
        !_isTemplateApplied)
    {
        return;
    }

    _image.Source = ImageSource;
    var bi = _image.Source as BitmapImage;
    if (bi != null)
    {
       ...
    }
}

 private void DoFullLayout()
 {
     if (!_isTemplateApplied)
     {
         return;
     }

     var bi = _image.Source as BitmapImage;
     if (bi == null)
     {
         return;
     }
     ...
}

正如你在这里看到的,它使用 as 将图像源投射到 BitmapImage,因为你的源是 WriteableBitmap,这里它将无法投射并且 return 空。这是您遇到问题的第一个可能原因。如果您坚持要使用WriteableBitmap,则需要修改这些代码,将BitmapImage 更改为您的WriteableBitmap

另一个可能的问题是它的 Setup 方法:

var bi = _image.Source as WriteableBitmap;
if (bi != null)
{
    bi.ImageOpened += (sender, e) =>
    {
        DoFullLayout();
        if (this.ImageOpened != null)
        {
            this.ImageOpened(this, e);
        }
    };
}

如果您将 BitmapImage 作为源传递,当我使用 UriSource 生成此 BitmapImage 时,一切都会正常进行。但是当我使用 SetSource 将文件流设置为这个 BitmapImage 时,ImageOpened 事件将神奇地 NOT 在我身边触发,我'我不确定这里会发生什么。但是你使用的是WriteableBitmap,没有WriteableBitmapImageOpend事件,无论如何你都需要修改这段代码。例如,我现在将其更改为:

var bi = _image.Source as WriteableBitmap;
if (bi != null)
{
    //bi.ImageOpened += (sender, e) =>
    //{
    //    DoFullLayout();
    //    if (this.ImageOpened != null)
    //    {
    //        this.ImageOpened(this, e);
    //    }
    //};
    try
    {
        DoFullLayout();
    }
    catch (Exception e)
    {
        Debug.WriteLine(e.Message);
    }
}

现在可以了,你可以自己处理异常了。

既然你没有提到你用哪个模板开发UWP应用程序,我这里只是用标准方法来提供一个演示,以防你在数据绑定方面有问题:

主页xaml:

<Page.DataContext>
    <local:MainPageViewModel x:Name="ViewModel" />
</Page.DataContext>

...
<Border Grid.Row="0" BorderBrush="Red" BorderThickness="1">
    <xamlcrop:CropControl x:Name="Crop" ImageSource="{Binding Source}" DesiredAspectRatio="1.0" />
</Border>
<Button Content="Pick Image" Command="{Binding SetImageSource}" Grid.Row="1" />

MainPageViewModel:

public class MainPageViewModel : INotifyPropertyChanged
{
    public MainPageViewModel()
    {
        source = null;
    }

    public ICommand SetImageSource
    {
        get
        {
            return new CommandHandler(() => this.setImageSource());
        }
    }

    public async void setImageSource()
    {
        StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/wrench.jpg"));
        if (file != null)
        {
            using (var stream = await file.OpenReadAsync())
            {
                WriteableBitmap wb = new WriteableBitmap(960, 1200);
                wb.SetSource(stream);
                Source = wb;
            }   
        }
        else
        {
        }
    }

    private ImageSource source;

    public ImageSource Source
    {
        get { return source; }
        set
        {
            if (value != source)
            {
                source = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        if (this.PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我的CommandHandler是这样简单的:

public class CommandHandler : ICommand
{
    public event EventHandler CanExecuteChanged;

    private Action _action;

    public CommandHandler(Action action)
    {
        this._action = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        this._action();
    }
}