У меня наверняка возникнет глупый и много раз разъясненный вопрос, но простого гугления ответа на свой вопрос я не нашел. (Я только новичок в C#, так что не судите строго, ООП мне все еще немного сложно понять). У меня есть приложение WPF MVVM, внутри которого я хотел бы реализовать несколько ViewModel, для которых есть один View. Я хочу иметь возможность изменить SelectedViewModel на SelectedViewModel_One и SelectedViewModel_Two, где внутри будут реализованы одни и те же методы. (как в примере).
public class ViewModel : INotifyPropertyChanged, IDisposable
{
private bool _Disposed;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string PropertyName = null){}
protected virtual bool Set<T>(ref T field, T value, [CallerMemberName] string PropertyName = null){}
public void Dispose(){}
protected virtual void Dispose(bool Disposing){}
}
public partial class SelectedViewModel_One : ViewModel
{
public void Move(){}
}
public partial class SelectedViewModel_Two : ViewModel
{
public void Move(){}
}
internal class MainWindowViewModel : ViewModel
{
private ViewModelAbstractBaseOrSmthLikeThat selectedViewModel = null;
public ViewModelAbstractBaseOrSmthLikeThat SelectedViewModel
{
get { return selectedViewModel; }
set { Set(ref selectedViewModel, value); }
}
}
--------------
--XAML
--------------
<Window x:Class = "SampleApplication.Views.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"
mc:Ignorable = "d"
xmlns:vm = "clr-namespace:SampleApplication.ViewModels"
ResizeMode = "NoResize" WindowStartupLocation = "CenterScreen">
<Window.DataContext>
<vm:MainWindowViewModel></vm:MainWindowViewModel>
</Window.DataContext>
<Grid DataContext = "{Binding SelectedViewModel}">
<!--Components and binding to SelectedViewModel -->
</Grid>
Но как правильно организовать архитектуру приложения? Что мне нужно добавить или изменить в коде MainWindowViewModel вместо "ViewModelAbstractBaseOrSmthLikeThat"
Мне нужно работать с интерфейсами и реализовать базовый класс? Или дайте ссылку на простейший пример, как это сделать, чтобы, изменив в MainWindowViewModel - SelectedViewModel, я мог вызывать методы Move() из SelectedViewModel_One и SelectedViewModel_Two.
У меня есть несколько устройств, которые управляются через команды RS232. Все устройства имеют одинаковые функции, но команды разные. Вот почему для программы существует единый вид
Вы должны определить Move() как член общего интерфейса или как абстрактный член общего абстрактного базового класса. Теперь, если вашей целью является предоставление одного конкретного класса модели представления, вы определяете свойство SelectedViewModel интерфейса типа или абстрактного базового класса.
@ÉtienneLaneville Не могли бы вы привести очень краткий пример в ответах?
В качестве альтернативы вы можете использовать композицию, а не наследование.
@ Энди Не могли бы вы также привести очень краткий пример в ответах?





Вот пример того, как действовать с базовым классом:
public abstract class RS232DeviceViewModel : ViewModel
{
public abstract void Move();
}
public class FirstDeviceViewModel : RS232DeviceViewModel
{
public override void Move()
{
throw new NotImplementedException();
}
}
public class SecondDeviceViewModel : RS232DeviceViewModel
{
public override void Move()
{
throw new NotImplementedException();
}
}
В вашем MainWindowViewModel вы должны объявить SelectedViewModel как RS232DeviceViewModel:
public class MainWindowViewModel
{
private RS232DeviceViewModel selectedViewModel
public RS232DeviceViewModel SelectedViewModel
{
get
{
return selectedViewModel;
}
set
{
if (selectedViewModel != value)
{
selectedViewModel = value;
OnPropertyChanged();
}
}
}
}
Поля и свойства автоматически инициализируются значением типа по умолчанию. Вам не нужно делать это явно.
@Бионический Код. да, я просто сопоставил исходное объявление в коде OP.
Подход, основанный на композиции, будет иметь функциональность в классе, который будет содержать обе модели представления. Поскольку это модели представления, класс может сам быть моделью представления, но он может быть просто создан как частный член моделей представления, а его функциональность представлена как метод, функция или тому подобное. Последнее часто называют услугой. У вас может быть класс MoveCalculationService или класс SinalInterpretationService... например.
public class FooViewModel : ObservableObject
{
public Bar SomeObject {get;set;}
public Signal SomeSignal(param ....)
{
}
}
Это можно использовать в обеих моделях просмотра
public class SelectedViewModel_One
{
public FirstDeviceViewModel (FooViewModel someMovingThing )
{
SomeMovingThing = someMovingThing;
}
// Maybe bind to SomeMovingThing.SomeProperty.
public FooViewModel SomeMovingThing {get;set;}
public void Move()
{
// Maybe reference instance of FooViewModel ;
}
}
Вы можете привязаться к SomeMovingThing.SomePropertyInThat. Вы можете ссылаться на него в методах в FirstDeviceViewModel. Вы можете передать уникальные параметры или функции из SelectedViewModel_One и SelectedViewModel_Two.
Композиция полезна для изоляции функциональности в повторно используемой и дружественной к внедрению зависимостей манере.
Композиция — довольно распространенный подход.
В командах, использующих SOLID, обычно ожидается, что модели представления не содержат определенной логики. Любые команды или функции обычно берутся из DI-контейнера, а это означает отдельные классы.
Возможно, вы захотите использовать одни и те же данные в нескольких моделях представления. Для такого рода вещей вы можете внедрить свою модель службы/представления, используя внедрение зависимостей, и использовать область этого для управления, когда вы получаете новый экземпляр или существующий.
Даже если вы просто пишете модульные тесты, будет проще, если вся ваша логика разбита на небольшие фрагменты и легко изолирована.
В некоторых приложениях вам может понадобиться сочетание обоих. Очень часто базовая модель представления имеет такие свойства, как, например, IsBusy и IsDirty.
SelectedViewModel_OneиSelectedViewModel_Twoмогут наследоваться от базового класса или оба могут реализовывать интерфейс, оба подхода будут работать.