WPF MVVM:奇怪的绑定行为

时间:2016-05-06 09:11:46

标签: wpf mvvm data-binding tabcontrol

我的UserControl包含TabControl

<UserControl x:Class="Test.MyUC"
....
         xmlns:vm="clr-namespace:Test.ViewModels"
         xmlns:ikriv="clr-namespace:IKriv.Windows.Controls.Behaviors"

...

<UserControl.Resources>
    <vm:MyUCVM x:Key="VM" />
</UserControl.Resources>

<UserControl.DataContext>
    <StaticResourceExtension ResourceKey="VM" />
</UserControl.DataContext>

<!-- Using Ivan Krivyakov's Attached Behavior -->
<TabControl ikriv:TabContent.IsCached="True"
            TabStripPlacement="Top" ItemsSource="{Binding TabList}" IsSynchronizedWithCurrentItem="True">
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type vm:MyTab1VM}">
            <v:MyTab1/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:MyTab2VM}">
            <v:MyTab2/>
        </DataTemplate>
    </TabControl.Resources>
...

当然,在MyUCVM中,我有TabList。现在,到目前为止,一切正常。

TabControl中的一个标签(例如MyTab1)需要连续并递归地从某个外部源(当然在ViewModel中完成)读取数据时,问题就开始了,并传递了该数据到View(通过Binding)来显示。即使到目前为止,一切都在发挥作用。但是,我不希望在标签不可见时运行,因为没有必要这样做。

为此,MyTab1VM需要知道关联的视图(MyTab1)是否为选定的标签。因此,我把它连接起来了:

MyTab1:
<Style TargetType="TabItem">
    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=OneWayToSource}" />
</Style>

MyTab1VM
public static readonly DependencyProperty IsSelectedProperty =
    DependencyProperty.Register("IsSelected",
    typeof(bool),
    typeof(MyTab1VM),
    new PropertyMetadata(false, new PropertyChangedCallback(IsSelectedChanged))
    );

public bool IsSelected
{
    get
    {
        return (bool) GetValue(IsSelectedProperty);
    }
    set
    {
        SetValue(IsSelectedProperty, value);
    }
}
public static void IsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.Property == IsSelectedProperty)
    {
        MyTab1VM vm = d as MyTab1VM ;

        vm.SetupToGetData();
    }
}
private void SetupToGetData()
{
    if (this.IsSelected)
    {
        System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(100);
        timer.Tick += timer_Tick;
        timer.Start();
    }
}
private void timer_Tick(object sender, EventArgs e)
{
    if (this.IsSelected)
        this.MyData = ExternalSource.GetData();
    else
    {
        (sender as System.Windows.Threading.DispatcherTimer).Stop();
    }
}

不幸的是,只有在this.IsSelected = true;的构造函数中手动设置MyTab1VM时,此设置才有效。将其留在构造函数中,数据不会显示在视图中。

我已设置断点并确认IsSelected的绑定正确运行。即使计时器正在运行,也会调用ExternalSource.GetData()。但this.MyData = ExternalSource.GetData();并未触发从ViewModel到View的更改。

最令人费解的是,如果IsSelected从构造函数设置为true,则会触发相同的绑定。

那里的任何人都知道这里发生了什么?

1 个答案:

答案 0 :(得分:0)

我设法自己做了一些富有成效的故障排除。我在SetupToGetData()中创建了一个断点,并将this.GetHashCode()放入调试监视列表中。当我在构造函数中手动设置this.IsSelected = true时,我意识到SetupToGetData()方法被调用两次,有两个不同的哈希值。在构造函数中植入另一个断点也表明当我切换到此选项卡时会调用构造函数。

我决定将其移至新的question,因为该问题很可能与绑定无关。

修改

似乎我是对的,this是这个问题的根源。随着这个问题的解决,这也是如此。