MVVM pattern
YouThis pattern can start by separatinghelp you separate business logic and UI in a clear and testable manner.
Stack Exchange network consists of 183 Q&A communities including Stack Overflow, the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.
Visit Stack ExchangeStack Internal
Knowledge at work
Bring the best of human thought and AI automation together at your work.
Explore Stack InternalYouThis pattern can start by separatinghelp you separate business logic and UI in a clear and testable manner.
You can start by separating business logic and UI
This pattern can help you separate business logic and UI in a clear and testable manner.
CalculatorStateCalculatorState and CalculatorLogicCalculatorLogic is your model/BL and it can be made shareable. Any calculation can take your model and modify it with a result or with an error message (divide by zero etc). It represents the entire I/O of your business logic (calculation on a calculator), you could expand it to support new features, such as history, with minimum refactoring of other code.
CalculatorWindow/xamlCalculatorWindow/xaml is your UI, this is the visible part of your code
CalculatorVMCalculatorVM is the glue between UI and model.
CalculatorState and CalculatorLogic is your model/BL and it can be made shareable. Any calculation can take your model and modify it with a result or with an error message (divide by zero etc). It represents the entire I/O of your business logic (calculation on a calculator), you could expand it to support new features, such as history, with minimum refactoring of other code.
CalculatorWindow/xaml is your UI, this is the visible part of your code
CalculatorVM is the glue between UI and model.
CalculatorState and CalculatorLogic is your model/BL and it can be made shareable. Any calculation can take your model and modify it with a result or with an error message (divide by zero etc). It represents the entire I/O of your business logic (calculation on a calculator), you could expand it to support new features, such as history, with minimum refactoring of other code.
CalculatorWindow/xaml is your UI, this is the visible part of your code
CalculatorVM is the glue between UI and model.
// CalculatorState and CalculatorLogic is your model/BL and it can be made shareable. Any calculation can take your model and modify it with a result or with an error message (divide by zero etc) // this is. It represents the entire I/O signature of any calculationyour business logic (calculation on a calculator), you cancould expand it to support new features, such as history etc, with minimum refactoring of other code.
CalculatorWindow/xaml is your UI, this is the visible part of your code
CalculatorVM is the glue between UI and model.
// model class public class CalculatorState : INotifyPropertyChanged /*IMPLEMENT, ON ALL PROPERTIES */ { public bool IsError{get;set;} public string ErrorMessage{get;set;} // Value is what's on the calculator screen under normal conditions public double Value {get;set;} // the calculator's memory private double? mem; public double Mem { get { return mem.GetValueOrDefault(Value); } set { mem = value; } } } // business logic public static class CalculatorLogic{ public static readonly Action<CalculatorState, double?> Add = (state,prm)=>state.Value = state.Mem + state.Value; public static readonly Action<CalculatorState, double?> Sub = (state,prm)=>state.Value = state.Mem - state.Value; } // VM component public class CalculatorCommand: ICommand<double?>{ public CalculatorState State {get;set;} public readonly Action<CalculatorState, double?> Calculate; public readonly bool IsTwoOpCommand; public CalculatorCommand(Action<CalculatorState, double?> calculate, CalculatorState state = null, bool isTwoOpCommand = true){ Calculate = calculate; State = state; IsTwoOpCommand = isTwoOpCommand; } public void Execute(double? prm){ if (State!=null){ if (Calculate!=null){ // for two-op commands without a Mem put the Value in Mem if (!IsTwoOpCommand || State.Mem.HasValue) Calculate(State); else State.Mem = State.Value; } else { State.IsError = true; State.ErrorMessage = "Null function"; } } else // throw if you wish Debug.WriteLine("Unexpected empty state"); } } // View-Model, links your UI to the model public CalculatorVM : INotifyPropertyChanged { public readonly CalculatorState State; public readonly ICommand AddCommand; public readonly ICommand SubCommand; .... public readonly ICommand NumberCommand; public CalculatorVM(CalculatorState state){ State = state; NumberCommand = new CalculatorCommand(c,p=>c.Value = c.Value*10 + p, State, false); SubCommand = new CalculatorCommand(c,p=>c.Value = c.Mem - cCalculatorLogic.Value,Sub State); AddCommand = new CalculatorCommand(c,p=>c.Value = c.Mem + cCalculatorLogic.ValueAdd, State); } } // View (UI). If you did the rest of the work your UI class should be mostly empty, // most of the setup would be done in the declarative part (XAML) via bindings // this allows you to reuse your entire business logic, unit test included // when you decide to switch platforms (desktop, mobile, server) public CalculatorWindow: Window{ public CalculatorWindow(){ BindingContext = new CalculatorVM(new CalculatorState()); InitializeComponent(); } } // CalculatorState is your model. Any calculation can take your model and modify it with a result or with an error message (divide by zero etc) // this is the entire I/O signature of any calculation, you can expand to support history etc with minimum refactoring of other code.
// model class public class CalculatorState : INotifyPropertyChanged /*IMPLEMENT, ON ALL PROPERTIES */ { public bool IsError{get;set;} public string ErrorMessage{get;set;} // Value is what's on the calculator screen under normal conditions public double Value {get;set;} // the calculator's memory private double? mem; public double Mem { get { return mem.GetValueOrDefault(Value); } set { mem = value; } } } // VM component public class CalculatorCommand: ICommand<double?>{ public CalculatorState State {get;set;} public readonly Action<CalculatorState, double?> Calculate; public readonly bool IsTwoOpCommand; public CalculatorCommand(Action<CalculatorState, double?> calculate, CalculatorState state = null, bool isTwoOpCommand = true){ Calculate = calculate; State = state; IsTwoOpCommand = isTwoOpCommand; } public void Execute(double? prm){ if (State!=null){ if (Calculate!=null){ // for two-op commands without a Mem put the Value in Mem if (!IsTwoOpCommand || State.Mem.HasValue) Calculate(State); else State.Mem = State.Value; } else { State.IsError = true; State.ErrorMessage = "Null function"; } } else // throw if you wish Debug.WriteLine("Unexpected empty state"); } } // View-Model, links your UI to the model public CalculatorVM : INotifyPropertyChanged { public readonly CalculatorState State; public readonly ICommand AddCommand; public readonly ICommand SubCommand; .... public readonly ICommand NumberCommand; public CalculatorVM(CalculatorState state){ State = state; NumberCommand = new CalculatorCommand(c,p=>c.Value = c.Value*10 + p, State, false); SubCommand = new CalculatorCommand(c,p=>c.Value = c.Mem - c.Value, State); AddCommand = new CalculatorCommand(c,p=>c.Value = c.Mem + c.Value, State); } } // View (UI). If you did the rest of the work your UI class should be mostly empty, // most of the setup would be done in the declarative part (XAML) via bindings // this allows you to reuse your entire business logic, unit test included // when you decide to switch platforms (desktop, mobile, server) public CalculatorWindow: Window{ public CalculatorWindow(){ BindingContext = new CalculatorVM(new CalculatorState()); InitializeComponent(); } } CalculatorState and CalculatorLogic is your model/BL and it can be made shareable. Any calculation can take your model and modify it with a result or with an error message (divide by zero etc). It represents the entire I/O of your business logic (calculation on a calculator), you could expand it to support new features, such as history, with minimum refactoring of other code.
CalculatorWindow/xaml is your UI, this is the visible part of your code
CalculatorVM is the glue between UI and model.
// model class public class CalculatorState : INotifyPropertyChanged /*IMPLEMENT, ON ALL PROPERTIES */ { public bool IsError{get;set;} public string ErrorMessage{get;set;} // Value is what's on the calculator screen under normal conditions public double Value {get;set;} // the calculator's memory private double? mem; public double Mem { get { return mem.GetValueOrDefault(Value); } set { mem = value; } } } // business logic public static class CalculatorLogic{ public static readonly Action<CalculatorState, double?> Add = (state,prm)=>state.Value = state.Mem + state.Value; public static readonly Action<CalculatorState, double?> Sub = (state,prm)=>state.Value = state.Mem - state.Value; } // VM component public class CalculatorCommand: ICommand<double?>{ public CalculatorState State {get;set;} public readonly Action<CalculatorState, double?> Calculate; public readonly bool IsTwoOpCommand; public CalculatorCommand(Action<CalculatorState, double?> calculate, CalculatorState state = null, bool isTwoOpCommand = true){ Calculate = calculate; State = state; IsTwoOpCommand = isTwoOpCommand; } public void Execute(double? prm){ if (State!=null){ if (Calculate!=null){ // for two-op commands without a Mem put the Value in Mem if (!IsTwoOpCommand || State.Mem.HasValue) Calculate(State); else State.Mem = State.Value; } else { State.IsError = true; State.ErrorMessage = "Null function"; } } else // throw if you wish Debug.WriteLine("Unexpected empty state"); } } // View-Model, links your UI to the model public CalculatorVM : INotifyPropertyChanged { public readonly CalculatorState State; public readonly ICommand AddCommand; public readonly ICommand SubCommand; .... public readonly ICommand NumberCommand; public CalculatorVM(CalculatorState state){ State = state; NumberCommand = new CalculatorCommand(c,p=>c.Value = c.Value*10 + p, State, false); SubCommand = new CalculatorCommand(CalculatorLogic.Sub State); AddCommand = new CalculatorCommand(CalculatorLogic.Add, State); } } // View (UI). If you did the rest of the work your UI class should be mostly empty, // most of the setup would be done in the declarative part (XAML) via bindings // this allows you to reuse your entire business logic, unit test included // when you decide to switch platforms (desktop, mobile, server) public CalculatorWindow: Window{ public CalculatorWindow(){ BindingContext = new CalculatorVM(new CalculatorState()); InitializeComponent(); } }