如何在另一个线程的视图之间切换?

时间:2011-09-20 08:46:58

标签: wpf mvvm view

我正在尝试使用MVVM,所以我有:

主窗口:

<Window x:Class="tbtest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TabControl ItemsSource="{Binding Tabs}" IsSynchronizedWithCurrentItem="True" Grid.Row="0"/>
        <Button Command="{Binding AddNewTab}" Grid.Row="1">Add new tab</Button>
    </Grid>
</Window>

和ViewModel:

public class VM
{
    public VM()
    {
        Tabs = new ObservableCollection<Object>();
        AddNewTab = new DelegateCommand<object>(ExecAddNewTab);
    }

    private void ExecAddNewTab(object obj)
    {
        var tab = new SomeTab();
        Tabs.Add(tab.View);
        ThreadPool.QueueUserWorkItem(item => tab.Activate());
    }

    public ObservableCollection<Object> Tabs { get; private set; }
    public DelegateCommand<object> AddNewTab { get; private set; }
}

每个标签都是单独的ViewModel with View:

public class SomeTab : ITab, INotifyPropertyChanged
{
    public SomeTab()
    {
        View = new SomeTabView1 {DataContext = this};
    }

    public string Header { get { return "Header"; } }
    private object _view;
    public object View
    {
        get { return _view; }
        private set
        {
            _view = value;
            PropertyChanged(this, new PropertyChangedEventArgs("View"));
        }
    }

    public void Activate()
    {
        Thread.Sleep(2000);

        // Some work

        Application.Current.Dispatcher.BeginInvoke(new Action<SomeTab>(vm =>
                                                                   {
// switching views, how can it be done?
                                                                          vm.View = new SomeTabView2 { DataContext = this };

                                                                   }), this);
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

SomeTabView1SomeTabView2是带有TextBlock“View1”和“View2”的简单WPF UserControl。

我需要的是Activate方法(在单独的线程中运行)将View1切换到View2 我提供的这段代码不起作用。

我该怎么做才能获得理想?感谢。

2 个答案:

答案 0 :(得分:1)

您的ViewModel不应与Views

一起使用

相反,让您的MainViewModel包含SelectedTab属性并将其绑定到TabControl,然后切换标签,只设置SelectedTab。使用DataTemplates告诉WPF如何绘制每个ViewModel

MainWindow XAML

<Window x:Class="tbtest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local=clr-namespace:MyNamespace>

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:SomeTabViewModel1}">
            <local:SomeTabView1 /> 
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:SomeTabViewModel2}">
            <local:SomeTabView2 /> 
        </DataTemplate>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:SomeTabViewModel3}">
            <local:SomeTabView3 /> 
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TabControl ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}"
                    IsSynchronizedWithCurrentItem="True" Grid.Row="0"/>
        <Button Command="{Binding AddNewTab}" Grid.Row="1">Add new tab</Button>
    </Grid>
</Window>

MainWindow ViewModel

public class VM
{
    public VM()
    {
        Tabs = new ObservableCollection<Object>();
        AddNewTab = new DelegateCommand<object>(ExecAddNewTab);
    }

    private void ExecAddNewTab(object obj)
    {
        var tab = new SomeTab();
        Tabs.Add(tab);
        SelectedTab = tab;

        // This should only run whatever code is needed to initialize the
        // ViewModel. It should have nothing to do with views
        ((ITab)tab).Activate();
    }

    public object SelectedTab { get; private set; }
    public ObservableCollection<Object> Tabs { get; private set; }
    public DelegateCommand<object> AddNewTab { get; private set; }
}

答案 1 :(得分:0)

上述问题是由于您的代码“假设”它将通过简单地使用Tab来选择\聚焦新创建的IsSynchronizedWithCurrentItem项目。

您需要通过CollectionView支持此字段。您还应使用此CollectionView本身添加新标签。

E.g。

假设您有一个名为ListCollectionView的{​​{1}},其源集合为TabsView可观察集合...

Tabs绑定到TabControl.ItemsSource,然后绑定...

TabsView

使用此...

   var tab = new SomeTab();
   Tabs.Add(tab.View); 

如果这有帮助,请告诉我....