开发工具与关键技术:Microsoft Visual Studio 2017、WPF
这是一个用ListBox控件配合ViewModel做的一个子页面标题栏,但是跟别的选项卡不一样的地方是我的选项卡没有关闭按钮,我的选项卡是点击某一个选项卡的时候页面进行跳转同时它后面的选项卡就会自动删掉。
ListBox选项卡效果图:
(图一)
(图二)
主页面xaml 代码:
<!--选项卡 显示部分-->
<ListBox x:Name="TopNavigationTitle" ScrollViewer.VerticalScrollBarVisibility="Hidden" ItemsSource="{Binding mainModel,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="50,0,20,0" Background="Transparent" BorderThickness="0" ItemContainerStyle="{StaticResource ListBoxTitleStyle}" ScrollViewer.CanContentScroll="False">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding TitleText}" x:Name="BtnTitleName" Margin="-4" Height="32" FocusVisualStyle ="{x:Null}"
Command="{Binding DataContext.BtnTitleNameClicked,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBox}}"
CommandParameter="{Binding ElementName=BtnTitleName,Path=Content}" Style="{StaticResource BtnListBoxTitleStyle}"></Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--Page 显示部分-->
<Grid Grid.Row="1" Margin="40,0,40.2,1.6">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Frame x:Name="PageContent" NavigationUIVisibility="Hidden" ContentRendered="PageContent_ContentRendered" FocusVisualStyle="{x:Null}"/>
</Grid>
主页面xmal.cs 部分:
(1) 首先实例化所有子页面,只是实例化而已,并没有显示出来,后面有封装一个方法来显示,就不要一个一个显示;
(2) 声明一个存放用户浏览过的页面和页面对应的标题的Hashtable(哈希表)
主页面xmal.cs 代码:
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
#region 实例化导航页面
/// <summary>
/// 传统诊断页面
/// </summary>
private TraditionDiagnosePage traditionDiagnosePage;
/// <summary>
/// 诊断选择页面
/// </summary>
private DiagnoseSelectPage diagnoseSelectPage;
#endregion
#region 变量
/// <summary>
/// 存放用户浏览过的页面和页面对应的标题
/// </summary>
Hashtable hashtable = new Hashtable();
#endregion
public MainWindow()
{
#region 实例化子页面
traditionDiagnosePage = new TraditionDiagnosePage(this); //传统诊断页面
diagnoseSelectPage = new DiagnoseSelectPage(this); //诊断选择页面
#endregion
//接收主页面导航标题信息
Messenger.Default.Register<string>(this, MessageCenter.MSG_MAINWINDOWTITLE_UPDATE, UpdateTitle);
//接收需要删除的标题名称集合信息
Messenger.Default.Register<List<string>>(this, MessageCenter.MSG_REMOVEMAINWINDOWTITLE_UPDATE, RemoveTitleName);
}
/// <summary>
/// 主页面内容区页面嵌套(需个子页面调用)
/// </summary>
/// <param name="page">页面</param>
/// <param name="pageTittle">页面标题</param>
public void ShowPage(Page page, string pageTittle)
{
//隐藏主页面中心区域背景
MainContentDropGrid.Visibility = Visibility.Collapsed;
//显示作为参数传过来的page
PageContent.Content = page;
//判断标题栏的标题数量是否只有一个,是的话就证明用户已经进入另一个模块
if (TopNavigationTitle.Items.Count < 2)
{
//清空浏览记录
hashtable.Clear();
}
//往集合中添加作为参数传过来的page页面和pageTittle标题
hashtable.Add(pageTittle, page);
}
/// <summary>
/// 更新标题栏
/// </summary>
/// <param name="pageTitleName"></param>
public void UpdateTitle(string pageTitleName)
{
foreach (var item in hashtable.Keys)
{
if ((string) item == pageTitleName)
{
PageContent.Content = hashtable[item];
}
}
}
/// <summary>
/// 点击任意一个标题,删除它后面的全部标题和相关页面
/// </summary>
/// <param name="removeTitleNameList">需要删除的标题名称集合</param>
private void RemoveTitleName(List<string> removeTitleNameList)
{
foreach (string item in removeTitleNameList)
{
if (hashtable.ContainsKey(item))
{
hashtable.Remove(item);
}
}
}
/// <summary>
/// 嵌套内容改变时发生的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PageContent_ContentRendered(object sender, EventArgs e)
{
//判断当前导航日记条目中是否存在前一项或后一项
if (PageContent.CanGoBack || PageContent.CanGoForward)
{
//清除上一条日记条目
PageContent.RemoveBackEntry();
}
}
}
主页面Model部分有两样东西:
(1) 声明一个枚举类,把所有子页面的名称写进去:
(2) 声明一个用于主页面ListBox选项卡的ListBox. ItemTemplate 内容绑定,也就是子页面标题:
public class MainModel: ObservableObject
{
/// <summary>
/// 页面类型
/// </summary>
private E_PageType pageType;
public E_PageType PageType
{
get { return pageType; }
set
{
pageType = value;
RaisePropertyChanged(() => pageType);
}
}
/// <summary>
/// 页面类型
/// </summary>
public enum E_PageType
{
#region 传统诊断
/// <summary>
/// 诊断选择页面
/// </summary>
DiagnoseSelectPage,
/// <summary>
/// 传统诊断页面
/// </summary>
TraditionDiagnosePage
#endregion
}
/// <summary>
/// 子页面标题栏
/// </summary>
private String titleText;
public String TitleText
{
get { return titleText; }
set
{
titleText = value;
RaisePropertyChanged(() => titleText);
}
}
}
主页面ViewModel 代码:
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
Messenger.Default.Register<MainModel>(this, MessageCenter.MSG_PAGESWITCHING_INFO, PageSwitching);//接收页面切换信息
//接收回退页面消息
Messenger.Default.Register<string>(this, MessageCenter.MSG_ROLLBACKPAGE_INFO, BackParentPage);
#region 初始化顶级页面类型(顶级页面就是模块首个页面)
pageTypelist.Add(E_PageType.TraditionDiagnosePage);
#endregion
#region 初始化 键:页面类型 值:页面显示消息
keyValuePairs.Add(E_PageType.TraditionDiagnosePage, MessageCenter.MSG_TRADITIONALDIAGNOSIS_SHOW);
keyValuePairs.Add(E_PageType.DiagnoseSelectPage, MessageCenter.MSG_DIAGNOSESELECT_SHOW);
#endregion
DispatcherHelper.Initialize();
ConnectServer.VCI_ControlCenter.DiagnoseReturnData += VCI_ControlCenter_DiagnoseReturnData;
NetUtil.ConnectionChangedEvent += NetUtil_ConnectionChangedEvent;
}
~MainViewModel()
{
ConnectServer.VCI_ControlCenter.Close();
}
#region 属性
ObservableCollection<MainModel> MainModel = new ObservableCollection<MainModel>();
public ObservableCollection<MainModel> mainModel
{
get { return MainModel; }
set
{
MainModel = value;
RaisePropertyChanged("mainModel");
}
}
private MainModel newMainModel = new MainModel();
/// <summary>
/// 顶级页面类型集合
/// </summary>
List<E_PageType> pageTypelist = new List<E_PageType>();
/// <summary>
/// 键:页面类型 值:页面显示消息
/// </summary>
Dictionary<E_PageType, string> keyValuePairs = new Dictionary<E_PageType, string>();
#endregion
#region 消息传递事件
/// <summary>
/// 接收消息绑定标题栏
/// </summary>
/// <param name="obj"></param>
private void PageSwitching(MainModel obj)
{
PageSwitchingPackage(obj);
}
/// <summary>
/// 接收消息绑定标题栏封装
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[LogRecord(IsMainPage = true)]
public virtual ObservableCollection<MainModel> PageSwitchingPackage(MainModel obj)
{
//判断传过来的页面类型是否属于顶级页面类型
if (pageTypelist.Contains(obj.PageType))
{
//清空标题栏
mainModel.Clear();
}
else
{
//若不属于顶级页面类型则在添加标题前添加"->"
mainModel.Add(new MainModel() { TitleText = "->" });
}
mainModel.Add(new MainModel() { TitleText = obj.TitleText, ClickTime = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") });
//发送消息切换页面
Messenger.Default.Send<string>(obj.TitleText, keyValuePairs[obj.PageType]);
return mainModel;
}
/// <summary>
/// 标题栏按钮点击事件
/// </summary>
public virtual void ShowTitleNamePage(string titleName)
{
List<int> removeTitleNameIndex = new List<int>();
List<string> removeTitleNameList = new List<string>();
int titleNameIndex = mainModel.IndexOf(mainModel.Where(x => x.TitleText == titleName).FirstOrDefault());
for (int i = titleNameIndex + 1; i <= mainModel.Count(); i++)
{
if (i == mainModel.Count())
{
break;
}
string removeTitleName = mainModel[i].TitleText;
removeTitleNameList.Add(removeTitleName);
removeTitleNameIndex.Add(i);
}
for (int i = 0; i < removeTitleNameIndex.Count(); i++)
{
mainModel.RemoveAt(titleNameIndex + 1);
}
Messenger.Default.Send<List<string>>(removeTitleNameList, MessageCenter.MSG_REMOVEMAINWINDOWTITLE_UPDATE);
Messenger.Default.Send<string>(titleName, MessageCenter.MSG_MAINWINDOWTITLE_UPDATE);
}
#endregion
}
接下来是在某个页面中点击按钮需要页面跳转时,ViewModel代码:
/// <summary>
/// 传统诊断车型选择ViewModel
/// </summary>
public class TraditionDiagnoseViewModel : ViewModelBase
{
public TraditionDiagnoseViewModel()
{
}
private MainModel newMainModel = new MainModel();
#region 诊断车型
private RelayCommand<string> diagnoseMotorcycleTypeClicked;
/// <summary>
/// 单击命令(诊断车型)
/// </summary>
public RelayCommand<string> DiagnoseMotorcycleTypeClicked
{
get
{
if (diagnoseMotorcycleTypeClicked == null)
diagnoseMotorcycleTypeClicked = new RelayCommand<string>((btnContent) => DiagnoseMotorcycleTypeClick(btnContent));
return diagnoseMotorcycleTypeClicked;
}
set { diagnoseMotorcycleTypeClicked = value; }
}
/// <summary>
/// 单击执行函数(诊断车型)
/// </summary>
private void DiagnoseMotorcycleTypeClick(string pageTitle)
{
newMainModel.TitleText = pageTitle;
newMainModel.PageType = E_PageType.DiagnoseSelectPage;
//发送页面切换信息到主页面后台 (MainViewModel)
Messenger.Default.Send<MainModel>(newMainModel, MessageCenter.MSG_PAGESWITCHING_INFO);
}
#endregion
}
然后是需要跳转到的那个页面的 xaml.cs代码:
/// <summary>
/// DiagnoseSelectPage.xaml 的交互逻辑
/// </summary>
public partial class DiagnoseSelectPage : Page
{
private MainWindow mainWindow;
public DiagnoseSelectPage(MainWindow parent)
{
InitializeComponent();
mainWindow = parent;
Messenger.Default.Register<string>(this, MessageCenter.MSG_DIAGNOSESELECT_SHOW, Show);
}
public void Show(string diagnoseSelect)
{
mainWindow.ShowPage(this, diagnoseSelect);
}
}
这样就能实现ListBox选项卡的效果了。