简体   繁体   English

具有多个模型实例的WPF MVVM

[英]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 . 那里有很多MVVM例子,但是我无法将一个例子应用于我的案例,因为我有几个相同类的实例 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. 我的模型中有三个类:Lamp,Switch和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. Button应该绑定到Switch,而TextBlock应该绑定到Lamp。

How can I bind each instance to the UI component I've prepared for it ? 如何将每个实例绑定到已为其准备的UI组件?

Here is a simple case I'd like to build an UI for : 这是一个简单的案例,我想为构建一个UI:

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) 如果您想使用它,这是我的完整代码(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. 也许,我将做一些更好的事情来处理NotifyPropertyChanged。

The window XAML : 窗口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 : 沿着MainViewModel的ViewModelBase:

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 : 和接口IRelayCommand沿着类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);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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