frendguo's blog

填坑日志之数据绑定的那些坑~

先上个代码:

MainPage.xaml:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="{x:Bind MyData.Title, Mode=OneWay}" FontSize="50" />
    <Button Content="改变数据源的数据" Click="Button_Click" />
</Grid>

MainPage.xaml.cs:

public MyData Mydata { get; set; }
public MyData Mydata2 { get; set; }
public MainPage()
{
    this.InitializeComponent();
    Mydata2 = new MyData("新的标题");
}

private void Button_Click(object sender, RoutedEventArgs e)
{
     Mydata = Mydata2;
}

MyData.cs:

public class MyData  : INotifyPropertyChanged
{
    private string title;

    public MyData()
    {

    }
    public MyData(string title)
    {
        this.Title = title;
    }
    public string Title
    {
        get { return title; }
        set
        {
            title = value;
            OnPropertyChanged("Title");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

看起来应该是能成功的, 点击按钮, 就会出现一行"新的标题"在页面上......可是, 对的, 没错, 就是失败了!!!

于是接下来, 进入调试找问题阶段

阶段一:

首先想到的就是, 在按钮的点击事件中加个断点, 再在MyData类的Title属性中的调用OnPropertyChanged 的时候加个断点

然后观察发现, 在页面加载的时候进去了一次OnPropertyChanged 处理函数后(也就是MainPage.xaml.cs 的构造函数中, 会new 一个Data2), 后面的

Mydata = Mydata2;

并没有进到OnPropertyChanged 处理函数中, 也就是说它的属性没有改变~

阶段二:

仔细一想, 就是这样子的呀,

 Mydata = Mydata2;

只是把Mydata2 的引用赋值给了Mydata, 所以原来的数据并没有什么改变. 如果还不懂, 那就看引用类型

然后又继续想解决办法, 突然想起了深拷贝和浅拷贝 (关于深拷贝和浅拷贝可以看这里: http://www.cnblogs.com/xugang/archive/2010/09/09/1822555.html)

于是我就将MainPage.xaml.cs 中的 按钮单击事件改了:

private void Button_Click(object sender, RoutedEventArgs e)
{
    using (MemoryStream ms = new MemoryStream())
    {
        XmlSerializer serializer = new XmlSerializer(Mydata2.GetType());
        serializer.Serialize(ms, Mydata2);
        ms.Seek(0, SeekOrigin.Begin);
        Mydata = (MyData)serializer.Deserialize(ms);
    }
}

想着借着序列化和反序列化, 不就可以实现对象的深拷贝了吗, 是不是这样就可以了?  

显然, 答案是否定的!!!

通过调试发现, 当反序列化的时候, 确实会生成一个对象, 并且将对象的引用赋值给Mydata , 这个时候, 由于此时改变的并不是Mydata中的属性, 而是Mydata , 而Mydata 的改变并不会触发通知, 所以, 还是失败了~

阶段三:

通过上面的调试发现, 我们一直改变的都是Mydata 的值, 而不是Mydata 中属性的值, 所以, 一直不能触发通知. 于是, 针对这个, 可以新建个类将MyData 类封装起来, 再实现INotifyPropertyChanged 接口

public class ModelView : INotifyPropertyChanged
{
    private MyData myData;

    public MyData MyData
    {
        get { return myData; }
        set
        {
            myData = value;
            OnPropertyChanged("MyData");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

再将MainPage.xaml 和 MainPage.xaml.cs 中的按钮单击事件 稍作更改

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="{x:Bind modelView.MyData.Title, Mode=OneWay}" FontSize="50" />
    <Button Content="改变数据源的数据" Click="Button_Click" />
</Grid>
private void Button_Click(object sender, RoutedEventArgs 
{
      modelView.MyData = Mydata2;
}

再次尝试, 就成功啦~附个图有图有真相:

Happy Coding!o(* ̄▽ ̄*)o

Add comment

Loading