我是WPF的新手,试图建立一个项目,该项目遵循Josh Smith描述模型 - 视图 - 视图模型设计模式的优秀文章的建议.
使用Josh的示例代码作为基础,我创建了一个包含许多"工作区"的简单应用程序,每个工作区由TabControl中的选项卡表示.在我的应用程序中,工作空间是一个文档编辑器,它允许通过TreeView控件操作分层文档.
虽然我已成功打开多个工作区并在绑定的TreeView控件中查看其文档内容,但我发现TreeView在选项卡之间切换时"忘记"其状态.例如,如果Tab1中的TreeView部分展开,则在切换到Tab2并返回到Tab1后,它将显示为完全折叠.此行为似乎适用于所有控件的控件状态的所有方面.
经过一些实验,我意识到我可以通过将每个控件状态属性显式绑定到底层ViewModel上的专用属性来保留TabItem中的状态.然而,这似乎是很多额外的工作,当我只是希望我的所有控件在工作区之间切换时记住它们的状态.
我想我错过了一些简单的东西,但我不知道在哪里寻找答案.任何指导都将非常感谢.
蒂姆,谢谢
更新:
根据要求,我将尝试发布一些演示此问题的代码.但是,由于作为TreeView基础的数据很复杂,我将发布一个展示相同symtoms的简化示例.这是主窗口中的XAML:
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Docs}">
<TabControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Path=Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<view:DocumentView />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Run Code Online (Sandbox Code Playgroud)
上面的XAML正确绑定到DocumentViewModel的ObservableCollection,其中每个成员都通过DocumentView呈现.
为了简化这个例子,我从DocumentView中删除了TreeView(如上所述),并将其替换为包含3个固定标签的TabControl:
<TabControl>
<TabItem Header="A" />
<TabItem Header="B" />
<TabItem Header="C" />
</TabControl>
Run Code Online (Sandbox Code Playgroud)
在这种情况下,DocumentView和DocumentViewModel之间没有绑定.运行代码时,内部TabControl在切换外部TabControl时无法记住其选择.
但是,如果我显式绑定内部TabControl的SelectedIndex属性...
<TabControl SelectedIndex="{Binding Path=SelectedDocumentIndex}">
<TabItem Header="A" />
<TabItem Header="B" />
<TabItem Header="C" />
</TabControl>
Run Code Online (Sandbox Code Playgroud)
...到DocumentViewModel上的相应虚拟属性...
public int SelecteDocumentIndex { get; set; }
Run Code Online (Sandbox Code Playgroud)
...内部标签能够记住它的选择.
我知道我可以通过将这种技术应用到每个控件的每个视觉属性来有效地解决我的问题,但我希望有一个更优雅的解决方案.
我有一个TabControl,其中每个Tab及其内容都数据绑定到ObservableCollection:
<TabControl ItemsSource="{Binding Path=.}">
<TabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=propertyValue}" />
</DataTemplate>
</TabControl>
</TabControl>
Run Code Online (Sandbox Code Playgroud)
如果我单击选项卡1,然后在文本框中键入内容并单击选项卡以使TextBox失去焦点,我键入文本框的新数据将提交给ObservableCollection项.
但是,如果我在TestBox中键入数据然后立即单击另一个选项卡,则永远不会提交数据.另外,当我回到数据时,它不再设置为我输入的内容.
任何人都知道在更改当前选项卡之前强制提交数据的方法吗?
更新和修复
我做了什么是连接SelectionChanged事件:
private void tabData_SelectionChanged(object sender, SelectionChangedEventArgs e) {
theTabControl.Focus();
}
Run Code Online (Sandbox Code Playgroud)
在TabControl上调用Focus()会使TextBox失去焦点并提交数据.我这样做是因为我有其他控件 - 比如DatePicker - 它们表现出类似的行为.这是一个很好的选择.
我已经阅读了帖子@ 如何阻止Wpf Tabcontrol在Tab更改上卸载Visual Tree,但是我无法让它工作,我一定错过了什么.请帮忙.谢谢
我正在使用来自示例项目的类TabControlEx @ http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-from-destroying-its -children.aspx
<Window x:Class="MyMainwindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sharedC="clr-namespace:myShared.Converter;assembly=myShared"
xmlns:mainTab="clr-namespace:MyWPFLib.View" Title="{Binding Title}"
Height="600" Width="850" Top="223" Left="164" ResizeMode="CanResize" Closing="WindowClosing"
WindowStyle="ToolWindow">
<Grid>
<mainTab:TabControlEx IsSynchronizedWithCurrentItem="True"
Grid.Row="0"
Margin="10 0 5 5"
x:Name="MainTabRegion"
TabStripPlacement="Top"
SelectedIndex="{Binding Tabs.SelectedIndex}"
ItemsSource="{Binding Tabs.TabItems}"
>
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header">
<Setter.Value>
<Binding Path="Header"/>
</Setter.Value>
</Setter>
<Setter Property="Visibility"
Value="{Binding IsVisible, Mode=OneWay, Converter={StaticResource boolToVis}}"/>
</Style>
</TabControl.ItemContainerStyle>
</mainTab:TabControlEx>
</Grid>
</Window>
Datasource for the maintab is Tabs.TabItems
public ObservableCollection<ITabControl> TabItems
{
get
{
return _items;
}
}
Run Code Online (Sandbox Code Playgroud)
每个TabItem都是用它构建的
<DataTemplate …Run Code Online (Sandbox Code Playgroud)