【发布时间】:2014-12-23 01:53:03
【问题描述】:
我为这个问题纠结了一段时间,也没有找到可行的答案,相信这与我对 WPF 缺乏了解有很大关系。
我的程序的基本架构将类似于 Visual Studio,带有可以以各种方式排列的选项卡块。为简单起见,我目前有一个无法以任何方式排列的选项卡块 - 表单包含一个包含选项卡控件的自定义控件。
现在,每个标签页的内容将是一个自定义控件,它是一个文档视图。标签页可能有不同的文档,每个文档都有特定的自定义控件和文档视图模型配对。
我的主要问题是我无法弄清楚如何在标签页控件的代码隐藏中引用标签页的视图模型。
这是 TabPane 的代码 - 自定义控件是选项卡块。它包括我从各个网站拼凑的代码,用于向选项卡添加关闭按钮(我以后可能会添加更多按钮)。
<UserControl x:Class="MyApp.TabPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyApp"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<DockPanel LastChildFill="True" >
<TabControl ItemsSource="{Binding Path=ViewModel.TabPages}" SelectedItem="{Binding Path=ViewModel.ActivePage}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<TabControl.Resources>
<DataTemplate DataType="{x:Type local:NowPlayingViewModel}" >
<local:NowPlayingControl />
</DataTemplate>
<DataTemplate DataType="{x:Type local:TypeEditorDocumentViewModel}" >
<local:TypeEditorControl />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0" Height="22">
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Caption}" />
<local:LibraryTabHeaderButton Name="tabPageCloseButton" Width="20" Height="19" Margin="6,0,0,0" Padding="0" HorizontalAlignment="Center" VerticalAlignment="Center"
Focusable="False"
Visibility="{Binding Path=AllowClose, Converter={local:BooleanToVisibilityConverter}}"
Click="tabPageCloseButton_Click">
<Path Data="M1,9 L9,1 M1,1 L9,9" Stroke="Black" StrokeThickness="2" />
</local:LibraryTabHeaderButton>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</DockPanel>
这是 TabPane 的代码隐藏。应用程序为 TabPane 分配了一个视图模型,以便该视图模型可以提供一组预先存在的选项卡(会有一些选项卡只能存在于一个 TabPane 中并且无法关闭)。关闭按钮需要告诉视图模型哪个选项卡正在关闭。任何可关闭的选项卡都可以关闭,无论该选项卡是否处于活动状态。
public partial class TabPane : UserControl
{
private TabPaneViewModel viewModel = null;
public TabPane()
{
viewModel = ApplicationManager.GetLibraryViewModel().GetNewTabPaneViewModel();
InitializeComponent();
}
public TabPaneViewModel ViewModel
{
get { return viewModel; }
}
private void tabPageCloseButton_Click(object sender, EventArgs e)
{
//Button button = (Button)sender;
}
TabPaneViewModel 非常简单。它基本上包含一个标签页的集合。
public class TabPaneViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly int id;
private ObservableCollection<ILibraryDocumentViewModel> tabPages = new ObservableCollection<ILibraryDocumentViewModel>();
private ILibraryDocumentViewModel activePage = null;
public TabPaneViewModel(int id)
{
this.id = id;
tabPages.CollectionChanged += tabPages_CollectionChanged;
}
private void tabPages_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if( !isDisposed && (e.Action == NotifyCollectionChangedAction.Add) )
ActivePage = (ILibraryDocumentViewModel)e.NewItems[0];
}
public ObservableCollection<ILibraryDocumentViewModel> TabPages
{
get { return tabPages; }
}
public ILibraryDocumentViewModel ActivePage
{
get { return activePage; }
set
{
activePage = value;
OnNotifyPropertyChanged("ActivePage");
}
}
private void OnNotifyPropertyChanged(string propertyName)
{
if( !isDisposed && (PropertyChanged != null) )
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
LibraryTabHeaderButton 是一个带有点击事件的简单自定义控件。我发现这个标题按钮不知道它与哪个选项卡相关联,所以我需要以某种方式告诉它。我还没想好怎么做。
public partial class LibraryTabHeaderButton : UserControl
{
public event EventHandler Click;
public LibraryTabHeaderButton()
{
InitializeComponent();
}
public int TabPageId { get; set; }
private void OnClick(object sender, RoutedEventArgs args)
{
LibraryTabHeaderButtonClickEventArgs newArgs = new LibraryTabHeaderButtonClickEventArgs();
newArgs.TabPageId = TabPageId;
if( Click != null )
Click(sender, newArgs);
}
}
LibraryViewModel 是文档的来源。它接收创建或打开文档的指令,执行此操作并将其添加到 TabPane。
public class LibraryViewModel
{
private List<TabPaneViewModel> tabPanes = new List<TabPaneViewModel>();
private static int nextTabPaneId = 1;
private TabPaneViewModel activeTabPane = null;
private NowPlayingViewModel nowPlayingViewModel = null;
private static int nextTabPageId = 1;
public TabPaneViewModel GetNewTabPaneViewModel()
{
TabPaneViewModel tabPane = new TabPaneViewModel(nextTabPaneId);
nextTabPaneId++;
tabPanes.Add(tabPane);
if( activeTabPane == null )
activeTabPane = tabPane;
if( nowPlayingViewModel == null )
{
// Show the now playing tab on this pane
nowPlayingViewModel = new NowPlayingViewModel(nextTabPageId);
nextTabPageId++;
tabPane.TabPages.Add(nowPlayingViewModel);
}
return tabPane;
}
public void DisplayDocument(LibraryDocumentType documentType)
{
if( activeTabPane != null )
{
ILibraryDocumentViewModel document = null;
switch( documentType )
{
case LibraryDocumentType.TypeEditor:
document = new TypeEditorDocumentViewModel(nextTabPageId);
nextTabPageId++;
break;
}
activeTabPane.TabPages.Add(document);
}
}
}
这是 ILibraryDocumentViewModel。
public interface ILibraryDocumentViewModel
{
int TabPageId { get; }
LibraryDocumentType DocumentType { get; }
string Caption { get; }
bool AllowClose { get; }
bool IsChanged { get; }
}
这是 TypeEditorControl 代码隐藏的一部分。我尝试了各种方法从这里访问视图模型,但它始终为空。
public partial class TypeEditorControl : UserControl
{
private TypeEditorDocumentViewModel viewModel = null;
public TypeEditorControl()
{
object v = this.ViewModel;
viewModel = DataContext as TypeEditorDocumentViewModel;
InitializeComponent();
viewModel = DataContext as TypeEditorDocumentViewModel;
UpdateEditMode();
}
public TypeEditorDocumentViewModel ViewModel
{
get { return viewModel; }
}
这是 TypeEditorControl xaml 的一部分。
<UserControl x:Class="MyApp.TypeEditorControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyApp"
mc:Ignorable="d"
d:DesignHeight="630">
<Grid Margin="0" Background="#FFF0F0F0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label x:Name="categoryListLbl" Content="Type Categories" Grid.Column="0" HorizontalAlignment="Left" Margin="10,5,10,0" Grid.Row="0" VerticalAlignment="Top"/>
<ListView x:Name="categoryListLvw" Margin="10,0" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Path=ViewModel.TypeCategories}" SelectionChanged="categoryListLvw_SelectionChanged" SelectionMode="Single" >
<ListView.View>
<GridView>
我认为代码已经足够了...现在,程序运行了,我可以调出各种文档,但是这些文档不显示来自视图模型的数据。
因为我创建了文档视图模型并将其添加到 TabPages ObservableCollection,所以我无法在该文档控件的代码中获得对文档视图模型的引用。当我添加 TypeEditorViewModel 时,TabPane 会自动生成一个 TypeEditorControl,但我似乎无法访问它们之间的链接。我目前的猜测是我没有在 TypeEditorControl xaml 中正确设置 DataContext(我没有尝试过 DataContext 和一个引用 Self,但都没有工作,我不知道还能尝试什么)。也许我的架构不支持我正在尝试做的事情。也许我问错了问题。
我不知道下一步该尝试什么。感谢任何阅读这篇巨大帖子的人。
【问题讨论】:
标签: wpf tabcontrol