框架画面分为上中下三层
由下面一个Grid控件完成布局
<Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="60"></RowDefinition> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="22"></RowDefinition> </Grid.RowDefinitions> </Grid>
上层为顶部菜单区域
中层为子菜单和业务画面部分
下层为状态栏和版权信息区域
下面我们分别看一下这三个部分的生成逻辑
一:顶部菜单区域
XAML代码如下:
<StackPanel x:Name="TopMenuS" Orientation="Horizontal" Background="{StaticResource HeadBG}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="10"></ColumnDefinition> <ColumnDefinition Width="142"></ColumnDefinition> <ColumnDefinition Width="10"></ColumnDefinition> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="3"></RowDefinition> <RowDefinition Height="18"></RowDefinition> <RowDefinition Height="18"></RowDefinition> <RowDefinition Height="18"></RowDefinition> <RowDefinition Height="3"></RowDefinition> </Grid.RowDefinitions> <TextBlock x:Name="helloBlock" Grid.Column="1" Grid.Row="1" Text="xxxx通信" /> <TextBlock x:Name="UserName" Text="Administrator" Grid.Column="1" Grid.Row="2" /> <TextBlock Text="欢迎使用 xxx监控系统" Grid.Column="1" Grid.Row="3" /> </Grid> <Rectangle Width="1" Fill="{StaticResource HeadSplitor}"></Rectangle> <StackPanel> <Button Style="{StaticResource ToolBtnStyle}" Content="全部关闭" Click="CloseAllClick"></Button> <Button Style="{StaticResource ToolBtnStyle}" Content="关闭当前" Click="CloseCurClick"></Button> <Button Style="{StaticResource ToolBtnStyle}" Content="关闭其他" Click="CloseOtherClick"></Button> </StackPanel> <Rectangle Width="1" Fill="{StaticResource HeadSplitor}"></Rectangle> <StackPanel> <Button Style="{StaticResource ToolBtnStyle}" Content="修改密码" Click="ChangePSWBtnClick"></Button> <Button Style="{StaticResource ToolBtnStyle}" Content="退出系统" Click="LoginOutBtnClick"></Button> <Button Style="{StaticResource ToolBtnStyle}" Content="重新登录" Click="ReLoginBtnClick"></Button> </StackPanel> <Rectangle Width="1" Fill="{StaticResource HeadSplitor}"></Rectangle> </StackPanel>
顶部菜单分为三部分
从左向右依次是
欢迎信息(GRID)
顶部菜单(动态创建)
常用按钮(StackPanel)
顶部菜单的容器是一个StackPanel
此容器有一个渐变的背景色,样式代码如下
<!--头部渐变背景--> <LinearGradientBrush x:Key="HeadBG" StartPoint="0.5 0" EndPoint="0.5 1"> <GradientStop Offset="0" Color="#FAFAFA"></GradientStop> <GradientStop Offset="0.5" Color="#D6D6D6"></GradientStop> </LinearGradientBrush>
此容器每个部分都有一个Rectangle来分割
此Rectangle也有个渐变的背景,代码如下
<!--头部分隔条渐变背景--> <LinearGradientBrush x:Key="HeadSplitor" StartPoint="0.5 0" EndPoint="0.5 1"> <GradientStop Offset="0" Color="#FAFAFA"></GradientStop> <GradientStop Offset="1" Color="#000000"></GradientStop> </LinearGradientBrush>
常用按钮的样式如下:
<!--头部的 三分栏 工具按钮样式--> <Style x:Key="ToolBtnStyle" TargetType="Button"> <Style.Setters> <Setter Property="Width" Value="90"></Setter> <Setter Property="Height" Value="20"></Setter> <Setter Property="Cursor" Value="Hand"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="Container"> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup x:Name="CommonStates"> <vsm:VisualState x:Name="MouseOver"> <Storyboard> <DoubleAnimation Storyboard.TargetName="fillColor" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="Normal" /> </vsm:VisualStateGroup> </vsm:VisualStateManager.VisualStateGroups> <Rectangle x:Name="fillColor" Opacity="0" Fill="#B5B5B5" IsHitTestVisible="False" RadiusX="1" RadiusY="1"/> <ContentPresenter x:Name="contentPresenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style.Setters> </Style>
动态创建顶部菜单的代码如下
(大家先不要深究数据是怎么来的,在以后的章节咱们会讲到数据交互的细节)
var tops = Common.ViewUtility.AllMenu .Where(m => m.ParentId == Guid.Empty) .OrderByDescending(m => m.MenuOrder); foreach (var m in tops) { var topM = new HeadBtn(); topM.DataContext = m; topM.MouseLeftButtonUp += new MouseButtonEventHandler(topM_MouseLeftButtonUp); TopMenuS.Children.Insert(2,topM); }
这里创建的HeadBtn是一个自定义的控件
(每个顶部菜单就是一个控件的实例)
该自定义控件XAML代码如下:
<StackPanel Orientation="Horizontal" Cursor="Hand"> <Grid x:Name="btn" Width="90" Height="60"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition Height="20"></RowDefinition> </Grid.RowDefinitions> <Image Source="../Images/module2.png" Height="30"></Image> <TextBlock Text="{Binding MenuName}" Grid.Row="1" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center" ></TextBlock> </Grid> <Rectangle Width="1" Fill="{StaticResource HeadSplitor}"></Rectangle> </StackPanel>
注意,这里每个顶部菜单的ICO图标不是动态的,朋友们,想让他变成动态的就自己动手吧
为了实现美观的效果
我为这个自定义控件定义了鼠标的滑入滑出事件
private void UserControl_MouseEnter(object sender, MouseEventArgs e) { var color = Color.FromArgb(255,180, 180, 180); btn.Background = new SolidColorBrush(color); } private void UserControl_MouseLeave(object sender, MouseEventArgs e) { btn.Background = new SolidColorBrush(Colors.Transparent); }
这些颜色的值,本应该作为资源放在样式文件中,我这里也没有做处理
二:底部状态条区域
此处比较简单
代码如下:
<StackPanel Width="Auto" Grid.Row="2" Background="#B5B5B5" Orientation="Horizontal" FlowDirection="RightToLeft"> <TextBlock VerticalAlignment="Center" Text="V1.0.0 Copy Right © All Rights Reserved"/> <TextBlock VerticalAlignment="Center" Text="xxxx"/> </StackPanel>
1.我没有做状态信息的内容
2.版本信息应该通过Assambly获取
三:中部区域
XAML代码如下
<Border Grid.Row="1" BorderBrush="#B5B5B5" BorderThickness="0 1 0 0"> <Grid Background="#E8E8E8" Margin="0 1 0 0" Name="CenterGrid"> <Grid.ColumnDefinitions> <ColumnDefinition Width="160"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <StackPanel> <sdk:Label Height="26" Background="#b5b5b5" Margin="0 6 0 6" FontWeight="Bold" FontSize="12" x:Name="lblMenuText" HorizontalAlignment="Center" Width="160" /> <ListBox SelectionChanged="left_panel_SelectionChanged" ItemContainerStyle="{StaticResource ListBoxItemStyleNew}" Grid.Column="0" x:Name="left_panel" Background="#E8E8E8" BorderThickness="0"> <ListBox.ItemTemplate> <DataTemplate> <ContentPresenter Content="{Binding MenuName}"></ContentPresenter> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel> <sdk:TabControl x:Name="tbControl" SelectionChanged="tbControl_SelectionChanged" Grid.Column="1" Margin="0 6 0 0"> </sdk:TabControl> </Grid> </Border>
其中Label控件显示的为顶部菜单的标题,标志着当前选中的是哪个顶部菜单
ListBox为子菜单控件
TabControl为业务画面区域
四:子菜单区域
子菜单样式相对复杂些
样式代码如下
<!--子菜单样式--> <Style TargetType="ListBoxItem" x:Key="ListBoxItemStyleNew" > <Setter Property="Padding" Value="3" /> <Setter Property="HorizontalContentAlignment" Value="Center" /> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="Background" Value="Transparent" /> <Setter Property="BorderThickness" Value="1"/> <Setter Property="TabNavigation" Value="Local" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <Grid Background="{TemplateBinding Background}"> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup x:Name="CommonStates"> <vsm:VisualState x:Name="Normal" /> <vsm:VisualState x:Name="MouseOver"> <Storyboard> <DoubleAnimation Storyboard.TargetName="fillColor" Storyboard.TargetProperty="Opacity" Duration="0" To=".35"/> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="Disabled"> <Storyboard> <DoubleAnimation Storyboard.TargetName="contentPresenter" Storyboard.TargetProperty="Opacity" Duration="0" To=".55" /> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> <vsm:VisualStateGroup x:Name="SelectionStates"> <vsm:VisualState x:Name="Unselected" /> <vsm:VisualState x:Name="Selected"> <Storyboard> <DoubleAnimation Storyboard.TargetName="fillColor2" Storyboard.TargetProperty="Opacity" Duration="0" To=".75"/> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> <vsm:VisualStateGroup x:Name="FocusStates"> <vsm:VisualState x:Name="Focused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Visibility" Duration="0"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="Unfocused"/> </vsm:VisualStateGroup> </vsm:VisualStateManager.VisualStateGroups> <Rectangle x:Name="fillColor" Opacity="0" Fill="#B5B5B5" IsHitTestVisible="False" RadiusX="1" RadiusY="1"/> <Rectangle x:Name="fillColor2" Opacity="0" Fill="#FFBADDE9" IsHitTestVisible="False" RadiusX="1" RadiusY="1"/> <ContentPresenter x:Name="contentPresenter" Cursor="Hand" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"/> <Rectangle x:Name="FocusVisualElement" Stroke="#FF6DBDD1" StrokeThickness="1" Visibility="Collapsed" RadiusX="1" RadiusY="1" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
这些样式主要是为了实现如下效果
子菜单数据绑定非常简单
(顶部菜单的单击事件将绑定子菜单)
代码如下:
void topM_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var tBTN = sender as HeadBtn; var tMenu = tBTN.DataContext as MenuM; lblMenuText.Content = tMenu.MenuName; var subs = Common.ViewUtility.AllMenu .Where(m => m.ParentId == tMenu.Id) .OrderBy(m => m.MenuOrder); left_panel.ItemsSource = subs; }
五:业务画面区域
业务画面的容器为TabControl
每个TabItem将承载一个业务画面
主要是为TabItem增加关闭按钮
XAML代码如下:
<sdk:TabItem.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Width="auto" Margin="0 0 -2 -2"> <TextBlock x:Name="tboxheader" Text="{Binding}"/> <Button Cursor="Hand" Click="CloseBTN_Click" Style="{StaticResource ListViewHeadBtnStyle}" Margin="3,-3,-6,0" Content="X" > </Button> </StackPanel> </DataTemplate> </sdk:TabItem.HeaderTemplate> </sdk:TabItem>
这个关闭按钮的样式比较特殊
<!--标签按钮--> <Style x:Key="ListViewHeadBtnStyle" TargetType="Button"> <Style.Setters> <Setter Property="Width" Value="20"></Setter> <Setter Property="Height" Value="20"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="Container"> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup x:Name="CommonStates"> <vsm:VisualState x:Name="Normal" /> <vsm:VisualState x:Name="MouseOver"> <Storyboard> <DoubleAnimation Storyboard.TargetName="fillColor" Storyboard.TargetProperty="Opacity" Duration="0" To=".35"/> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> </vsm:VisualStateManager.VisualStateGroups> <Rectangle x:Name="fillColor" Opacity="0" Fill="#B5B5B5" IsHitTestVisible="False" RadiusX="1" RadiusY="1"/> <ContentPresenter x:Name="contentPresenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style.Setters> </Style>
关闭按钮样式主要为了实现如下效果
(鼠标滑上,关闭按钮背景变灰色)
按钮的单击事件如下
private void CloseBTN_Click(object sender, RoutedEventArgs e) { var tc = this.Parent as TabControl; tc.Items.Remove(this); }