简体   繁体   中英

WPF MVVM with multiple model instances

There are lots of MVVM examples out there but I can't apply one to my case because I have several instances of the same classes . Also, I need to manipulate the models directly so I can subscribe some Observer's to their Observable's.

I've simplified my case for the question. I've three classes from my model : Lamp, Switch and Grid. They can interact thanks to an Observer/Observable mechanism. Basically, activating a switch switches on/off all the lamps connected to the same grid.

I want to make a window which shows a specific usage of these classes. There should Button's bound to Switch's and TextBlock's bound to Lamp's.

How can I bind each instance to the UI component I've prepared for it ?

Here is a simple case I'd like to build an UI for :

Grid entranceGrid = new Grid("Entrance Grid");
Lamp hallLamp = new Lamp("Hall Lamp");
Lamp stairsLamp = new Lamp("Stairs Lamp");
Switch downSwitch = new Switch("Downstair Switch");
Switch upSwitch = new Switch("Upstair Switch");

downSwitch.Subscribe(entranceGrid);
upSwitch.Subscribe(entranceGrid);
entranceGrid.Subscribe(hallLamp);
entranceGrid.Subscribe(stairsLamp);

// Below are four instances I'd like to bind to some UI component.
LampViewModel hallLampVM = new LampViewModel(hallLamp);
LampViewModel stairsLampVM = new LampViewModel(stairsLamp);
SwitchViewModel downSwitchVM = new downSwitchVM(downSwitch);
SwitchViewModel upSwitchVM = new downSwitchVM(upSwitch);

Here is my full code if you want to play with it (VS 2017)

This answer is ugly but it does work and maybe it will help someone to come with something better.

I will implement this solution for the time being. Maybe, I'll just work on something better to handle the NotifyPropertyChanged.

The window XAML :

<Window.DataContext>
    <vm:MainViewModel />
</Window.DataContext>
<Grid Margin="0,0,0,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <RowDefinition Height="3*" />
        <RowDefinition Height="2*" />
    </Grid.RowDefinitions>
    <GroupBox Grid.Row="0" Header="Entrée">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Text="Hall d'entrée"/>
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Escalier"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TxtHallLamp}"/>
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding TxtStairsLamp}"/>

            <Button Grid.Row="0" Grid.Column="2" Content="rez" Command="{Binding DownSwitchCommand}"/>
            <Button Grid.Row="1" Grid.Column="2" Content="1er" Command="{Binding UpSwitchCommand}"/>
        </Grid>
    </GroupBox>
    [Few lines you don't need]
</Grid>

The ViewModelBase along the MainViewModel :

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public bool NotifyPropertyChanged<T>(ref T variable, T value, [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(variable, value)) return false;

        variable = value;
        NotifyPropertyChanged(propertyName);
        return true;
    }
}


class MainViewModel : ViewModelBase
{
    private Lamp _HallLamp, _StairsLamp;
    private Switch _DownSwitch, _UpSwitch;

    private IRelayCommand _DownSwitchCommand, _UpSwitchCommand;

    public MainViewModel()
    {
        #region entrance

        Grid entranceGrid = new Grid("Entrance Grid");
        _HallLamp = new Lamp("Hall Lamp");
        _StairsLamp = new Lamp("Stairs Lamp");
        _DownSwitch = new Switch("Downstair Switch");
        _UpSwitch = new Switch("Upstair Switch");

        _DownSwitch.Subscribe(entranceGrid);
        _UpSwitch.Subscribe(entranceGrid);
        entranceGrid.Subscribe(_HallLamp);
        entranceGrid.Subscribe(_StairsLamp);

        #endregion // entrance
    }

    private string LampToTxt(Lamp lamp)
    {
        return lamp.Light ? "ON" : "OFF";
    }

    public string TxtHallLamp
    {
        get
        {
            return LampToTxt(_HallLamp);
        }
    }

    public string TxtStairsLamp
    {
        get
        {
            return LampToTxt(_StairsLamp);
        }
    }

    private void NotifyEntranceGridPropertyChanged()
    {
        NotifyPropertyChanged(nameof(TxtHallLamp));
        NotifyPropertyChanged(nameof(TxtStairsLamp));
    }

    public IRelayCommand DownSwitchCommand
    {
        get
        {
            return _DownSwitchCommand ?? (_DownSwitchCommand = new RelayCommand(
                () => {
                    _DownSwitch.Press();
                    NotifyEntranceGridPropertyChanged();
                },
                () => true));
        }
    }

    public IRelayCommand UpSwitchCommand
    {
        get
        {
            return _UpSwitchCommand ?? (_UpSwitchCommand = new RelayCommand(
                () => {
                    _UpSwitch.Press();
                    NotifyEntranceGridPropertyChanged();
                },
                () => true));
        }
    }
}

And the interface IRelayCommand along the class RelayCommand :

public interface IRelayCommand : ICommand
{
    void RaiseCanExecuteChanged();
}

class RelayCommand : IRelayCommand
{

    private Action _Execute;
    private Func<bool> _CanExecute;
    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action Execute) : this(Execute, null)
    {

    }

    public RelayCommand(Action Execute, Func<bool> CanExecute)
    {
        if (Execute == null)
            throw new ArgumentNullException();

        _Execute = Execute;
        _CanExecute = CanExecute;
    }

    public bool CanExecute(object parameter)
    {
        return (_CanExecute == null) ? true : _CanExecute();
    }

    public void Execute(object parameter)
    {
        _Execute();
    }

    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM