我正在尝试使用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 { };
}
SomeTabView1
而SomeTabView2
是带有TextBlock“View1”和“View2”的简单WPF UserControl。
我需要的是Activate方法(在单独的线程中运行)将View1切换到View2 我提供的这段代码不起作用。
我该怎么做才能获得理想?感谢。
答案 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);
如果这有帮助,请告诉我....