需求
在传统的编程中,当我们需要做一个导航功能时:我们需要在主窗体(MainWindow)中定义一个导航容器Frame(假如命名为MainFrame),然后定义所有的导航页面,并让这些导航页和主窗体中的MainFrame有一定的联系,最后在导航时,修改MainFrame的内容(Content)、资源(Source)...等方式实现页面切换和导航。这样的方式虽然符合一般编程逻辑,但是这种“强”关系会让我们的程序变得较为复杂而不容易扩展,更重要的是,他违背了程序设计的“开闭原则”。为了解决这个问题,就出现了IoC(控制反转)容器,弱化导航容器和导航页之间的关系。本文通过 Ninject 实现依赖注入。
为了实现这个效果,我们同样使用之前的项目场景:在我们的室内监控系统中,存在以下几个主要的页面:登录页、主页、房间页、记录页、设置页。我们使用IoC来实现页面之间的切换和其他属性控制(这里需要使用到之前的MVVM模式和值转换器)。
首先上效果:
环境
Windows 10
Visual Studio 2019
.Net Framework 4.7.2
Ninject
设计
UI设计:
功能设计:
在登录页面点击“Login”进入主页,同时显示主菜单导航。
通过主页的“Enter Chamber”进入全屏的房间页面,点击“View”重新显示主菜单导航。
通过主菜单实现页面之间的切换。
以上功能通过Ninject、MVVM和ValueConverter实现。
实现
1.自定义IoC容器
(1)通过NuGet引入Ninject程序包。
(2)根据Ninject自定义IoC容器
using Deamon.ViewModel;
using Ninject;
namespace Deamon.Util.DI
{
/// <summary>
/// 应用程序的 IoC 容器
/// </summary>
public static class IoC
{
#region 公共属性
/// <summary>
/// IoC 容器
/// </summary>
public static IKernel Kernel { get; private set; } = new StandardKernel();
#endregion
#region 配置构造
/// <summary>
/// 配置 IoC 容器,绑定所有需要的信息准备使用
/// 注意:必须在应用程序启动后立即调用,以确保可以找到所有服务
/// </summary>
public static void Setup()
{
// 绑定 ViewModel
BindViewModels();
}
/// <summary>
/// 绑定所有的单例
/// </summary>
private static void BindViewModels()
{
// 绑定到 ViewModel 的单例
Kernel.Bind<ApplicationViewModel>().ToConstant(new ApplicationViewModel());
Kernel.Bind<LoginViewModel>().ToConstant(new LoginViewModel());
Kernel.Bind<HomeViewModel>().ToConstant(new HomeViewModel());
Kernel.Bind<ChamberViewModel>().ToConstant(new ChamberViewModel());
}
#endregion
/// <summary>
/// 从指定类型的 IoC 获取服务
/// </summary>
/// <typeparam name="T">要获取的类型</typeparam>
/// <returns></returns>
public static T Get<T>()
{
return Kernel.Get<T>();
}
}
}
2.主窗体的View和ViewModel的建立,以及数据绑定
(1)ViewModel和Locator建立
using Deamon.Util.DI;
namespace Deamon.ViewModel
{
/// <summary>
/// 应用程序视图模型
/// </summary>
public class ApplicationViewModel : BaseViewModel
{
/// <summary>
/// 当前视图
/// </summary>
private ApplicationView currentView = ApplicationView.Login;
/// <summary>
/// 当前视图
/// </summary>
public ApplicationView CurrentView
{
get { return currentView; }
set
{
RaisePropertyChanged(ref currentView, value, nameof(CurrentView));
}
}
/// <summary>
/// 存在导航条
/// </summary>
private bool hasNavigationBar = false;
/// <summary>
/// 存在导航条
/// </summary>
public bool HasNavigationBar
{
get { return hasNavigationBar; }
set
{
RaisePropertyChanged(ref hasNavigationBar, value, nameof(HasNavigationBar));
}
}
#region 公共命令
public RelayCommand HomeCommand
{
get
{
return new RelayCommand(() =>
{
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Home;
});
}
}
public RelayCommand SettingsCommand
{
get
{
return new RelayCommand(() =>
{
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Settings;
});
}
}
public RelayCommand ChamberCommand
{
get
{
return new RelayCommand(() =>
{
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Chamber;
});
}
}
public RelayCommand LoginCommand
{
get
{
return new RelayCommand(() =>
{
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Login;
});
}
}
public RelayCommand RecordCommand
{
get
{
return new RelayCommand(() =>
{
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Record;
});
}
}
#endregion
}
/// <summary>
/// 应用程序视图类型
/// </summary>
public enum ApplicationView
{
/// <summary>
/// 登录
/// </summary>
Login,
/// <summary>
/// 主页
/// </summary>
Home,
/// <summary>
/// 房间
/// </summary>
Chamber,
/// <summary>
/// 记录
/// </summary>
Record,
/// <summary>
/// 设置
/// </summary>
Settings,
}
}
using Deamon.Util.DI;
namespace Deamon.ViewModel
{
public class ViewModelLocator
{
public static ViewModelLocator Locator { get; private set; } = new ViewModelLocator();
public static ApplicationViewModel ApplicationVM => IoC.Get<ApplicationViewModel>();
public static LoginViewModel LoginVM => IoC.Get<LoginViewModel>();
public static HomeViewModel HomeVM => IoC.Get<HomeViewModel>();
public static ChamberViewModel ChamberVM => IoC.Get<ChamberViewModel>();
}
}
(1)ValueConverter
using Deamon.View;
using Deamon.ViewModel;
using System;
using System.Diagnostics;
using System.Globalization;
namespace Deamon.Util.ValueConverter
{
public class ApplicationViewValueConverter : BaseValueConverter<ApplicationViewValueConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((ApplicationView)value)
{
case ApplicationView.Login:
return new LoginView();
case ApplicationView.Chamber:
return new ChamberView();
case ApplicationView.Home:
return new HomeView();
case ApplicationView.Settings:
return new SettingsView();
case ApplicationView.Record:
return new RecordView();
default:
Debugger.Break();
return null;
}
}
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
using System;
using System.Globalization;
using System.Windows;
namespace Deamon.Util.ValueConverter
{
/// <summary>
/// 一个转换器,将一个布尔值转换成 <see cref="Visibility"/> 类型
/// 如果带有参数 parameter 则是 True 表示显示,FALSE 表示隐藏
/// 如果不带参数 parameter 则是 True 表示隐藏,FALSE 表示显示
/// </summary>
public class BooleanToVisibilityCollapsedConverter : BaseValueConverter<BooleanToVisibilityCollapsedConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null)
return (bool)value ? Visibility.Collapsed : Visibility.Visible;
else
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
(3)View和数据绑定
<Window x:Class="Deamon.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Deamon"
xmlns:converter="clr-namespace:Deamon.Util.ValueConverter"
xmlns:vm="clr-namespace:Deamon.ViewModel"
mc:Ignorable="d" Background="#FF031704"
DataContext="{Binding ApplicationVM, Source={x:Static vm:ViewModelLocator.Instance}}"
Title="房间信息实时监控" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="5 0 5 5" Height="44"
Visibility="{Binding HasNavigationBar,Converter={converter:BooleanToVisibilityCollapsedConverter},ConverterParameter=True}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<WrapPanel Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0 0 50 0">
<RadioButton Command="{Binding HomeCommand}" Content="Deamon" Style="{StaticResource MainNavigation}"/>
<RadioButton Command="{Binding ChamberCommand}" Content="Chamber" Style="{StaticResource MainNavigation}" />
<RadioButton Command="{Binding RecordCommand}" Content="Record" Style="{StaticResource MainNavigation}"/>
<RadioButton Command="{Binding SettingsCommand}" Content="Settings" Style="{StaticResource MainNavigation}"/>
</WrapPanel>
<Border Grid.Column="1">
<WrapPanel VerticalAlignment="Bottom">
<Button Style="{StaticResource CoverGeometryPathBackgroundButton}" Width="23">
<PathGeometry Figures="M 101.520,0.000 L 21.240,29.699 L 0.000,16.199 L 0.000,61.919 L 21.240,49.859 L 101.520,79.380 L 101.520,0.000 Z"/>
</Button>
<Button Style="{StaticResource CoverGeometryPathBackgroundButton}" Width="23">
<PathGeometry Figures="M 30.300,278.620 L 30.300,49.417 L 224.530,177.874 L 224.575,177.806 L 224.640,177.905 L 420.180,49.512 L 420.180,278.620 L 30.300,278.620 Z M 224.318,135.392 L 65.295,30.220 L 384.493,30.220 L 224.318,135.392 Z M 450.120,0.000 L 420.180,0.000 L 0.120,0.000 L 0.120,0.190 L 0.000,0.190 L 0.000,308.860 L 0.120,308.860 L 30.300,308.860 L 420.180,308.860 L 450.120,308.860 L 450.579,308.860 L 450.579,0.000 L 450.120,0.000 Z"/>
</Button>
</WrapPanel>
</Border>
</Grid>
<Frame Grid.Row="1" x:Name="MainFrame"
Content="{Binding CurrentView, Converter={converter:ApplicationViewValueConverter}}"
NavigationUIVisibility="Hidden" Background="Transparent"/>
</Grid>
</Window>
(4)样式资源
<!-- ButtonStyles.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="CheckedLightColor">#FFFBFDFB</Color>
<SolidColorBrush x:Key="CheckedLight" Color="{StaticResource CheckedLightColor}"/>
<Color x:Key="CheckedDefaultColor">#FFD4C0F7</Color>
<SolidColorBrush x:Key="CheckedDefault" Color="{StaticResource CheckedDefaultColor}"/>
<Color x:Key="DefaultForegroundColor">#FFB0B0B0</Color>
<SolidColorBrush x:Key="DefaultForeground" Color="{StaticResource DefaultForegroundColor}"/>
<Color x:Key="HoverBackgroundColor">#FFFF0000</Color>
<Style x:Key="OnlyTextButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource CheckedLight}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontFamily" Value="Consolas"/>
<Setter Property="Padding" Value="10"/>
<Setter Property="Margin" Value="0,10"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Border x:Name="border"
CornerRadius="10"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<TextBlock Text="{TemplateBinding Content}"
Focusable="False"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{StaticResource CheckedDefault}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource DefaultForeground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 图形按钮 -->
<Style x:Key="BaseGeometryPathButton" TargetType="Button">
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource CheckedLight}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
<!-- 功能区图形按钮(Cover:可以根据Path的大小进行缩放来适应按钮) -->
<Style x:Key="CoverGeometryPathBackgroundButton" TargetType="Button" BasedOn="{StaticResource BaseGeometryPathButton}">
<Setter Property="Padding" Value="2"/>
<Setter Property="Margin" Value="2,1"/>
<Setter Property="Width" Value="{Binding ActualHeight,RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Grid x:Name="rootGrid"
Background="Transparent">
<Border Padding="2" Margin="0 2 0 0">
<Viewbox Stretch="Uniform">
<Path x:Name="s" Data="{TemplateBinding Content}" Fill="{TemplateBinding Foreground}"/>
</Viewbox>
</Border>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="{StaticResource CheckedDefaultColor}" Duration="0:0:0.01" Storyboard.TargetName="s" Storyboard.TargetProperty="Fill.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="{StaticResource CheckedLightColor}" Duration="0:0:0.01" Storyboard.TargetName="s" Storyboard.TargetProperty="Fill.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<!-- RadioButtonStyles -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="HoverBrushColor">#FFFF0000</Color>
<SolidColorBrush x:Key="HoverBrush" Color="{StaticResource HoverBrushColor}"/>
<!-- 主导航条按钮 -->
<Style x:Key="MainNavigation" TargetType="{x:Type RadioButton}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="FontSize" Value="15" />
<Setter Property="Margin" Value="5 0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Grid Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<!-- 文本内容 -->
<Grid>
<Border x:Name="border" BorderBrush="{StaticResource HoverBrush}" BorderThickness="1" Opacity="0">
</Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 这种方式也可以 -->
<TextBlock Grid.Row="0"
x:Name="myContentPresenter"
Foreground="Purple"
TextAlignment="Center"
TextWrapping="Wrap"
FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="5"
Text="{TemplateBinding Content}"/>
<Rectangle Grid.Row="1" x:Name="bottomRect" Height="3" Fill="{StaticResource HoverBrush}" Opacity="0"/>
</Grid>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Border.BorderThickness" Value="0.1" TargetName="border" />
</Trigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard Name="mouseEnterBeginStoryboard">
<Storyboard>
<ColorAnimation Storyboard.TargetName="myContentPresenter"
Storyboard.TargetProperty="Foreground.Color"
To="{StaticResource HoverBrushColor}" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetName="border"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="myContentPresenter"
Storyboard.TargetProperty="Foreground.Color"
To="Purple" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetName="border"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<!-- 选择时 -->
<EventTrigger RoutedEvent="RadioButton.Checked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="bottomRect"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<!-- 没有被选择时 -->
<EventTrigger RoutedEvent="RadioButton.Unchecked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="bottomRect"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
3.建立其他页面和ViewModel
只列举三个页面
(1)Login
<Page x:Class="Deamon.View.LoginView"
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:Deamon.View"
xmlns:vm="clr-namespace:Deamon.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="LoginView">
<Grid Background="#FFA87D12">
<Button Width="100" Height="50" VerticalAlignment="Top"
Style="{StaticResource OnlyTextButton}" Command="{Binding LoginVM.LoginCommand,
Source={x:Static vm:ViewModelLocator.Locator}}" Content="Login"/>
<TextBlock Text="LoginView 登录页" FontSize="35" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
using Deamon.Util.DI;
using System.Threading.Tasks;
namespace Deamon.ViewModel
{
public class LoginViewModel:BaseViewModel
{
/// <summary>
/// 登录
/// </summary>
public RelayCommand LoginCommand
{
get
{
return new RelayCommand(() => {
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Home;
IoC.Get<ApplicationViewModel>().HasNavigationBar = true;
});
}
}
private async Task LoginAsync()
{
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Home;
await Task.Delay(1);
}
}
}
(2)Home
<Page x:Class="Deamon.View.HomeView"
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:Deamon.View"
xmlns:vm="clr-namespace:Deamon.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="HomeView">
<Grid Background="#FF02421C">
<Button Style="{StaticResource OnlyTextButton}"
Width="200" Height="50" VerticalAlignment="Top"
Command="{Binding HomeVM.EnterChamberCommand,
Source={x:Static vm:ViewModelLocator.Locator}}" Content="Enter Chamber"/>
<TextBlock Text="HomeView 主页" FontSize="35" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
using Deamon.Util.DI;
using System.Threading.Tasks;
namespace Deamon.ViewModel
{
public class HomeViewModel : BaseViewModel
{
/// <summary>
/// 进入房间
/// </summary>
public RelayCommand EnterChamberCommand
{
get
{
return new RelayCommand( async ()=> await EnterChamberAsync());
}
}
private async Task EnterChamberAsync()
{
IoC.Get<ApplicationViewModel>().CurrentView = ApplicationView.Chamber;
IoC.Get<ApplicationViewModel>().HasNavigationBar = false;
await Task.Delay(1);
}
}
}
(3)Chamber
<Page x:Class="Deamon.View.ChamberView"
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:Deamon.View"
xmlns:converter="clr-namespace:Deamon.Util.ValueConverter"
xmlns:vm="clr-namespace:Deamon.ViewModel"
DataContext="{Binding ChamberVM, Source={ x:Static vm:ViewModelLocator.Locator}}"
Title="ChamberView">
<Grid Background="#FF033F42">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<Button Width="100" Height="35"
VerticalAlignment="Top" HorizontalAlignment="Left"
Style="{StaticResource OnlyTextButton}"
Content="T"
Command="{Binding ShowBarCommand}"
Visibility="{Binding ApplicationVM.HasNavigationBar, Source={ x:Static vm:ViewModelLocator.Locator},Converter={converter:BooleanToVisibilityCollapsedConverter}}"
/>
</Grid>
<TextBlock Grid.Row="0" Grid.RowSpan="2" Text="ChamberView 房间页" FontSize="35" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
using Deamon.Util.DI;
namespace Deamon.ViewModel
{
/// <summary>
/// 房间信息
/// </summary>
public class ChamberViewModel : BaseViewModel
{
#region 公共命令
/// <summary>
/// 显示导航栏命令
/// </summary>
public RelayCommand ShowBarCommand
{
get
{
return new RelayCommand(() =>
{
IoC.Get<ApplicationViewModel>().HasNavigationBar = true;
});
}
}
#endregion
}
}
4.启动IoC服务
using Deamon.Util.DI;
using System.Windows;
namespace Deamon
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 配置 IoC 容器
IoC.Setup();
// 启动主窗体
Current.MainWindow = new MainWindow();
Current.MainWindow.Show();
}
}
}
写在后面
通过IoC容器,我们可以通过访问IoC容器修改ApplicationView(当前应用程序的视图)的值,借助属性通知和值转换器实现页面的切换。利用单例模式,将单例Locator作为资源直接绑定到XAML中
Over
每次记录一小步...点点滴滴人生路...