WPF IoC容器Ninject的应用——室内监控可视化

需求

在传统的编程中,当我们需要做一个导航功能时:我们需要在主窗体(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

每次记录一小步...点点滴滴人生路...

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值