none
关于Frame.Navigate的问题 RRS feed

  • 问题

  • 项目中有三个xaml文件:BlankPage.xaml,BlankPage1.xaml,BlankPage2.xaml;

    其中在BlankPage.xaml中,有一个Frame和一个Button,当点击Button时:

    private void Button_Click_1(object sender, RoutedEventArgs e)
            {
                if (frame.CurrentSourcePageType == Type.GetType("FrameTest.BlankPage1"))
                {
                    frame.Navigate(Type.GetType("FrameTest.BlankPage2"));
                }
                else
                {
                    frame.Navigate(Type.GetType("FrameTest.BlankPage1"));
                }
            }

    在BlankPage1.xaml中,有一个Slider,Slider绑定到对象Data:

    <Grid x:Name="grid1">
            <Slider x:Name="slider" Value="{Binding Num, Mode=TwoWay}" .../>
    </Grid>

    public static MyData data = new MyData();
            public BlankPage1()
            {
                this.InitializeComponent();
                data.Num = 50;
            }

    protected override void OnNavigatedTo(NavigationEventArgs e)

            {
                grid1.DataContext = data;
            }

    --------------------------

    其中MyData定义:

    //MyData

    public class MyData : INotifyPropertyChanged
        {
            private double _num;
            public double Num
            {
                get { return _num; }
                set
                {
                    _num = value;
                    OnChanged("Num");
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnChanged(string name)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            }
        }

    ------------------------------------

    问题来了:

    当我第一次显示时,此时Frame中的内容是BlankPage1.xaml,调节slider时没什么问题;但是,当按一下Button,此时Frame显示的是BlankPage2.xaml,然后再按一下Button,Frame重新显示BlankPage1.xaml时,再调节slider时,出现一个error "Value does not fall within the expected range"。

    2012年4月20日 3:24

答案

  • 原因是第二次载入,又进行了一次 BlankPage1 的构造和InitializeComponent,data.Num = 50。但是你的data成员是静态的,所以他的 PropertyChanged event已经在上次的绑定中获得了handler, 但是这次你的绑定还没有生效,所以当你再次赋值50到Num属性并且Call OnChanged的时候发生异常。

    如上图,第二次绑定生效后,PropertyChanged委托中包含两个方法,但第一个方法是上一次绑定时加入的,所以这次如果再Invoke她的话,会有异常发生,上一次的BlankPage1已经不存在了。

    所以不建议你使用静态成员来所为DataContext,提供给绑定。一旦绑定失效,但PropertyChanged还时会有引用会导致异常。

    当然,你的这个问题的解决方法也有,如下:

        public class MyData : INotifyPropertyChanged
        {
            private double _num;
            public double Num
            {
                get { return _num; }
                set
                {
                    _num = value;
                    OnChanged("Num");
                }
            }
    
            public void RestPropertyChanged()
            {
                if (PropertyChanged != null)
                {
                    Delegate[] list = PropertyChanged.GetInvocationList();
                    if (list != null && list.Count() > 0)
                    {
                        foreach (PropertyChangedEventHandler handler in list)
                            PropertyChanged -= handler;
                    }
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnChanged(string name)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            }
        }

    在MyData中设计一个方法去清空委托列表,然后在每次页面初始化时候调用他:

            public static MyData data = new MyData();
            public BlankPage1()
            {
                this.InitializeComponent();
                data.RestPropertyChanged();
                data.Num = 50;
            }

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月23日 10:14
    版主

全部回复

  • 原因是第二次载入,又进行了一次 BlankPage1 的构造和InitializeComponent,data.Num = 50。但是你的data成员是静态的,所以他的 PropertyChanged event已经在上次的绑定中获得了handler, 但是这次你的绑定还没有生效,所以当你再次赋值50到Num属性并且Call OnChanged的时候发生异常。

    如上图,第二次绑定生效后,PropertyChanged委托中包含两个方法,但第一个方法是上一次绑定时加入的,所以这次如果再Invoke她的话,会有异常发生,上一次的BlankPage1已经不存在了。

    所以不建议你使用静态成员来所为DataContext,提供给绑定。一旦绑定失效,但PropertyChanged还时会有引用会导致异常。

    当然,你的这个问题的解决方法也有,如下:

        public class MyData : INotifyPropertyChanged
        {
            private double _num;
            public double Num
            {
                get { return _num; }
                set
                {
                    _num = value;
                    OnChanged("Num");
                }
            }
    
            public void RestPropertyChanged()
            {
                if (PropertyChanged != null)
                {
                    Delegate[] list = PropertyChanged.GetInvocationList();
                    if (list != null && list.Count() > 0)
                    {
                        foreach (PropertyChangedEventHandler handler in list)
                            PropertyChanged -= handler;
                    }
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnChanged(string name)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            }
        }

    在MyData中设计一个方法去清空委托列表,然后在每次页面初始化时候调用他:

            public static MyData data = new MyData();
            public BlankPage1()
            {
                this.InitializeComponent();
                data.RestPropertyChanged();
                data.Num = 50;
            }

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月23日 10:14
    版主
  • Dear Bob, Very Thanks!
    2012年4月23日 10:38
  • 但是我还有一个问题:当我用同样的方法添加一个 ToggleSwitch 控件的时候,ToggleSwitch 可以很正常的工作,不会出于问题。这是为什么呢?

    page1:

    <Grid x:Name="grid1" Background="#FF5CAE36">
            <Slider x:Name="slider" Value="{Binding Num, Mode=TwoWay}" .../>
            <ToggleSwitch Header="ToggleSwitch" IsOn="{Binding Switch, Mode=TwoWay}" .../>
        </Grid>
    public sealed partial class BlankPage1 : Page
        {
            public static MyData data = new MyData();
            public BlankPage1()
            {
                this.InitializeComponent();
            }
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                grid1.DataContext = data;
            }
        }

    MyData:

    public class MyData : INotifyPropertyChanged
        {
            private double _num;
            public double Num
            {
                get { return _num; }
                set
                {
                    _num = value;
                    OnChanged("Num");
                }
            }
            private bool _switch;
            public bool Switch
            {
                get { return _switch; }
                set
                {
                    _switch = value;
                    OnChanged("Switch");
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnChanged(string name)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            }
        }

    在这种情况下,操作Slider会出现问题,而操作 ToggleSwitch 则没有问题?我很奇怪

    谢谢!


    2012年4月23日 10:56
  • 测试了很多控件,只有在Slider更新绑定值时抽取数据去PropertyChanged发生异常。不过无所谓,每次更新时清空委托链有好处,防止多余委托存在阻止GC进行回收。


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月25日 4:11
    版主