WPF MVVM:奇怪的绑定行为 [英] WPF MVVM: Strange Binding behavior

查看:157
本文介绍了WPF MVVM:奇怪的绑定行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 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>

<! - 使用Ivan Krivyakov的附加行为 - >
< TabControl ikriv:TabContent.IsCached =True
TabStripPlacement =TopItemsSource ={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 需要连续地递归地从一些外部源(从$ code> ViewModel 中完成)读取数据,然后将数据传递给 View (通过绑定)来显示。即使到此为止,一切都正在工作。但是,当选项卡不可见时,我不希望它运行,因为没有任何意义。



要做到这一点, MyTab1VM 需要知道相关的View( MyTab1 )是否是所选标签。因此,我连线:

  MyTab1:
< Style TargetType =TabItem>
< Setter Property =IsSelectedValue ={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为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
{
(发件人为System.Windows.Threading.DispatcherTimer).Stop();
}
}

不幸的是,此设置仅在设置 this.IsSelected = true; 手动在 MyTab1VM 的构造函数中。我们已经设置了断点,并确认 IsSelected的绑定



/ code>正在运行。即使定时器正在运行,并且正在调用 ExternalSource.GetData()。但是 this.MyData = ExternalSource.GetData(); 不会触发从ViewModel到View的更改。



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

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

解决方案

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



我决定把它移到一个新的问题,因为它看起来很可能与绑定无关。



编辑



似乎我是对的,这个是这个问题的根源。因为这个问题也解决了,所以也是这样。


I have a UserControl that contains a 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>
...

Of course, in MyUCVM, I have TabList. Now, up to this point, everything works fine.

The problem starts when one of the tabs (e.g. MyTab1) in the TabControl needs to continuously and recursively read data from some external source (done in the ViewModel of course), and pass that data to View (via Binding) to display. Even up to this point everything is working. However, I do not want that to run when the tab is not visible, because there is no point to do that.

To do that, MyTab1VM needs to know if the associated View (MyTab1) is the selected tab. Therefore, I wired this up:

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();
    }
}

Unfortunately, this setup only works when I set this.IsSelected = true; manually in the MyTab1VM's constructor. Leaving that out in the constructor, the data do not get shown in the view.

I have set breakpoints and confirmed that the binding for IsSelected is running correctly. Even the timer is running, and ExternalSource.GetData() is being called. But this.MyData = ExternalSource.GetData(); is not triggering the change from the ViewModel to the View.

The most puzzling part is that the same binding is triggered if IsSelected is set to true from the constructor.

Anyone out there knows what happened here?

解决方案

I managed to do some fruitful troubleshooting on my own. I made a breakpoint in SetupToGetData() and I put this.GetHashCode() in my debugging watchlist. When I manually set this.IsSelected = true in the constructor, I realized that the SetupToGetData() method is called twice, with two different hash values. Planting another breakpoint in the constructor also showed that the constructor is called when I switch to this tab.

I have decided to move this to a new question, because it looks highly possible that the problem has nothing to do with binding.

Edit

Seems like I was right that this is the root of this problem. As that question is solved, so is this as well.

这篇关于WPF MVVM:奇怪的绑定行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆