alexei.sorokin
alexei.sorokin

Reputation: 67

How to Use Ninject with MVVM and WPF

I'm struggling to build an App using WPF and MVVM pattern. In this App I have three buttons in the MainView - Camera, Boiler, Temperature Sensor. When I press a certain button, It opens additional control with information about a selected gadget in the second part of the window. I've done it, but architecture is not good, because the Main Form "knows" about certain objects. I have this code in my MainView (XAML), which should not look like this.

<Window ...>
    <Grid>
        <Grid.ColumnDefinitions>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <Button Content="Камера"
                    Command="{Binding NavigationCameraCommand}"
                    Margin="5"
                    Height="30"/>
            <Button Content="Котёл"
                    Command="{Binding NavigateBoilerCommand}"
                    Margin="5"
                     Height="30"/>
            <Button Content="Датчик температуры"
                     Command="{Binding NavigationTemperatureSensorCommand}"
                    Margin="5"
                    Height="30"/>
        </StackPanel>

        <ContentControl Grid.Column="1"  Content="{Binding CurrentViewModel}">
            <ContentControl.Resources>
                <DataTemplate DataType="{x:Type viewmodels:CameraViewModel}">
                    <views:CameraView/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type viewmodels:BoilerViewModel}">
                    <views:BoilerVIew/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type viewmodels:TemperatureSensorViewModel}">
                    <views:TemperatureSensorView/>
                </DataTemplate>
            </ContentControl.Resources>
        </ContentControl>
    </Grid>
</Window>

I need to use Ninject to handle this problem. As I understood I need to define pairs of views and models in the locator, then when the view is injected, the linked model will be automatically determined, instance of it will be created and assigned to the view context.

I have not found any examples how it can be done.

Could anybody please give an example how to do this or explain with an example(preferably with several views)?

I found this video on YouTube, but still haven't got the idea, how it will work for several views. https://www.youtube.com/watch?v=yN4SgWHwhgk

Upvotes: 0

Views: 217

Answers (1)

Ivan
Ivan

Reputation: 44

It seems like you want to get another result. If you are planning to add devices in future (generate buttons dynamically and use custom view) you need to define base interface and implement for each device (sensor, boiler, etc.) which should be made as different module (library). Module should provide some metadata, custom view and something else you want. When you start your app, container loads assemblies with implemented base interface, so you get devices and can generate buttons dynamically using metadata. Then, when necessary, you can use provided view or other provided properties or methods.

interface IDevice
{
    IDeviceMetadata Metadata { get; }
    UserControl View { get; }
}

interface IDeviceMetadata
{
    Guid Key { get; }
    string Name { get; }
    string Description { get; }
    string Version { get; }
}

class App
{
    public ICollection<IDevice> Devices { get; private set; }

    void LoadDevices()
    {
        Devices = Container.LoadFromDirectory("path_to_device_libraries(dlls)");
    }
}

class DevicesViewModel
{
    public IEnumerable<IDevice> Devices => App.Instance.Devices;

    public UserControl DeviceView { get { ... } set { ... } } // have to invoke PropertyChanged

    public ICommand ShowDeviceViewCommand => Command.Create<IDevice>(ShowDeviceView);
    
    private void ShowDeviceView(IDevice device)
    {
        DeviceView = device.View;
    }
}
<Window>
    <Grid>
        <ItemsControl ItemsSource="{Binding Devices}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Content="{Binding Path=Metadata.Name}"
                            Command="{Binding Path=DataContext.ShowDeviceViewCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
                            Margin="5" Height="30"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <ContentControl Grid.Column="1"  Content="{Binding DeviceView}"/>
    </Grid>
</Window>

Upvotes: 1

Related Questions