XAML:用户控件不将更改传播到绑定源

时间:2012-12-23 23:18:00

标签: c# xaml binding user-controls windows-runtime

我正在为Metro UI开发ImagePicker用户控件。其原理很简单:它显示图像,当点击图像时,打开文件对话框,允许更改当前图像。为此,用户控件只是公开包装图像绑定到的ImageSource属性。

<local:ImagePicker Source="{Binding PictureUri, Mode=TwoWay}"/>

启动时,绑定工作正常,并显示我的视图模型提供的PictureUri属性中的图片。问题是,当我点击图像并选择一个新图像时,会显示新图像,但我的视图模型中的绑定值不会更新,尽管有TwoWay绑定模式。我相信这个问题来自我的用户控制代码,但我不明白为什么当它实际传播到包装图像时,值不会传播到视图模型上...

所以这是XAML部分。

<UserControl x:Name="ImagePickerUserControl"
    x:Class="ImageUserControl.ImagePicker"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid x:Name="ImagePickerRootGrid" Background="Gray">
        <Image Source="{Binding Source, ElementName=ImagePickerUserControl}"/>
    </Grid>

</UserControl>

代码部分,对不起,但我相信一切都很重要。

public sealed partial class ImagePicker : UserControl
{
    public ImagePicker()
    {
        this.InitializeComponent();

        // Hookup event to handle when the control is tapped
        this.Tapped += ImagePicker_Tapped;
    }

    public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register("Source", typeof(ImageSource), typeof(ImagePicker),
        new PropertyMetadata(null, new PropertyChangedCallback(ImagePicker.OnSourceChanged)));

    public ImageSource Source
    {
        get
        {
            return (ImageSource)this.GetValue(ImagePicker.SourceProperty);
        }

        set
        {
            this.SetValue(ImagePicker.SourceProperty, value);
        }
    }

    private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // Update Visual State
    }

    private async void ImagePicker_Tapped(object sender, TappedRoutedEventArgs e)
    {
        // Pick up a new picture
        FileOpenPicker filePicker = new FileOpenPicker();
        filePicker.FileTypeFilter.Add(".jpg");
        filePicker.FileTypeFilter.Add(".jpeg");
        filePicker.FileTypeFilter.Add(".png");
        var pngFile = await filePicker.PickSingleFileAsync();

        // If the user picked up a file
        if (pngFile != null)
        {
            BitmapImage bitmap = new BitmapImage();
            await bitmap.SetSourceAsync(await pngFile.OpenReadAsync());

            // Update the source image
            this.Source = bitmap;
        }
    }
}

我认为这个问题只是我的错误,但我无法理解这里发生的事情。如果您想尝试运行项目并更好地查看代码,我上传并在SkyDrive上共享它:ImageUserControl

感谢您耐心阅读这么长的帖子。

2 个答案:

答案 0 :(得分:1)

双向绑定不起作用,因为您的依赖项属性的类型为ImageSource,而您的视图模型属性的类型为Uri。绑定无法将ImageSource转换为Uri,因此未设置该值。您需要在用户控件上使用类型为Uri的属性才能使双​​向绑定工作。

编辑:

如果您只将依赖项属性更改为其他类型,则如果用户选择您的应用程序在没有选择器的情况下无法访问的文件,则内部图像将不再显示。您的应用只能使用返回的StorageFile访问此类文件,但不能使用其绝对路径。在控件内部,你可以通过拥有两个依赖属性来解决这个问题:ImageSource就像现在用于显示内部图像一样,Uri用于返回路径。由于你将绑定到第二个,你需要添加一个回调,当'Uri'从外面改变时,它将设置ImageSource属性。

根据您对用户控件之外的结果的处理方式,这对您来说可能还不够好。如果您想访问该文件,则需要返回StorageFile或将文件放入FutureAccessList并返回令牌。

答案 1 :(得分:1)

如果在运行代码时查看输出窗口,您将看到以下内容: -

  

mscorlib.dll中出现'System.ArgumentException'类型的第一次机会异常

     

错误:无法将目标值保存到源。 BindingExpression:Path ='PictureUri'DataItem ='ImageUserControl.PictureViewModel'; target元素是'ImageUserControl.ImagePicker'(Name ='ImagePickerUserControl'); target属性是'Source'(类型'ImageSource')。

这是因为您将ImageSource绑定到URI的属性。最简单的解决方法是将imagepicker的源代码更改为uri,如下所示: -

using System.ComponentModel;
using Windows.UI.Xaml.Media;

namespace ImageUserControl
{
    public class PictureViewModel : INotifyPropertyChanged
    {
        private ImageSource pictureUri;

        public ImageSource PictureUri 
        { 
            get
            {
                return this.pictureUri;
            }

            set 
            {
                if (this.pictureUri != value)
                {
                    this.pictureUri = value;
                    this.RaisePropertyChanged("PictureUri");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

然后,您可以按如下方式修改视图模型定位器: -

using System;
using Windows.UI.Xaml.Media.Imaging;

namespace ImageUserControl
{
    public class ViewModelLocator
    {
        static ViewModelLocator()
        {}

        public PictureViewModel Main
        {
            get
            {
                return new PictureViewModel
                {                    
                    PictureUri = new BitmapImage(new Uri("ms-resource:/Files/Assets/eels.jpg"))
                };
            }
        }
    }
}

然后事件会像你期望的那样激发。如果确实需要图像的URI,则必须将其添加为附加属性。

(使用Eels专辑封面作为测试图像的加分点。)

相关问题