Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm using C#, WPF, and .NETcore3.1 to design a GUI. This is the first time I've used C#, so the project structure I have might seem unusual or suboptimal. While suggestions to make the code more C# friendly are welcome (in that I expect a number of 'use MVVM' answers), that isn't the main question I have here.
I've ended up with the following general structure :
A DLL project which does the business logic, mostly independent of the GUI
A WPF application which I want to use to display information from the DLL
Class-wise, this looks like
A class in the DLL (for the purpose of this question, in practice is a set of classes) which holds the actual business logic, and for all practical purposes you can assume will get changed asynchronously. Lets call this BusinessLogicClass
A class in the GUI project which creates a presentation layer of sorts, essentially reshaping the relevant properties of the BusinessLogicClass into formats that can be directly consumed from the XAML using Bindings. Let's call this the PresentationLayerClass. Note that this is implemented as a User Control.
The XAML GUI itself. Specifically, the XAML GUI for the custom control.
Some pseudocode :
// In the DLL
class BusinessLogicClass:
public Boolean status{ get; set; }
// In the GUI
Using DLLNamespace;
public partial class PresentationLayerClass : UserControl
private BusinessLogicClass actual;
public PresentationLayerClass(BusinessLogicClass actual){
InitializeComponent();
this.DataContext = this;
this.actual = actual;
}
public string ConnectionStatus{
get{
if (this.actual.status == true) return "LanConnect";
else return "LanDisconnect";
}
}
public string StatusColor{
get {
if (this.actual.status == true) return "Green";
else return "Red";
}
}
// In the XAML
<iconPacks:PackIconMaterial Kind="{Binding ConnectionStatus}" Margin="5" Foreground="{Binding StatusColor}"/>
Now, I understand (though have not yet worked out how to test this) that when the properties of the PresentationLayerClass are changed, the GUI will 'update itself'.
What I would like is that async changes to the status property of the BusinessLogicClass get propagated up the chain all the way to the GUI.
If necessary, for the sake of simplicity and because I don't know enough yet to explain more, assume that everything's on the same thread and the changes which are async are being triggered by the user on the GUI itself. Say a button. However, the button and this display do not and must not know anything about each other. The information must necessarily pass through the DLL, and the DLL must trigger the change to the update.
Also, the DLL should not be hardcoded to the GUI - I want to be able to run the DLL by itself or with a CLI application or another GUI.
I am hoping some combination of INotifyPropertyChanged would do the job, but I can't seem to figure out how I would set that up. INotifyPropertyChanged seems to always be discussed in the context of GUIs and Bindings.
I understand that MVVM is supposed to help simplify this sort of thing. While I have reasons to want to avoid MVVM in this particular instance, I will admit I'm not yet entirely convinced that MVVM is worth the trouble in general. I'm quite averse to the apparently implicit links popular MVVM libraries establish. I have no intention of starting a philosophical discussion on MVVM, but anyone who wishes to disabuse me of that notion may make their case as an aside - as long as you understand that MVVM isn't the subject of the question I ask here.
Not to tell you what you already know, but a lot of your questions might be answered by just reading up on the INotifyPropertyChanged interface.
INotifyPropertyChanged Summary
Normally, no object knows if another object's properties have changed unless a check is performed. This is essentially the purpose of events, to raise an alarm when something changes so other objects don't have to keep checking all the time. But there are numerous way to declare events with all kinds of different signatures.
INotifyPropertyChanged provides a simple, basic standard that can be used to tell other objects when something changes, and what property was changed. It defines the PropertyChanged event, which includes as an argument the name of the property that was changed. A type that implements INotifyPropertyChanged is responsible for explicitly raising the PropertyChanged event when a property changes, this is usually done in the set method of each property.
The WPF framework is designed to support the INotifyPropertyChanged interface. WPF data bindings check for objects that implement INotifyPropertyChanged and listen for the PropertyChanged event. When that even is raised, they check PropertyChangedEventArgs.PropertyName to see which property was changed. If that property is bound to, WPF knows to update that binding.
In WPF, there are also dependency properties. These have a lot useful features, but the most pertinent is that they also implement their own change notification, which WPF knows to look out for.
Your Implementation
The way you have PresentationLayerClass defined currently, the UI will not automatically update if either ConnectionStatus or StatusColor change. This is because those properties are not implemented as either dependency properties or INotifyPropertyChanged properties. Of course, both of those take their values from BusinessLogicClass, so let's start there.
BusinessLogicClass should implement INotifyPropertyChanged, and the status property should raise the PropertyChanged event when it is set.
Along with that, you need to add an event handler to PresentationLayerClass for that event. Example:
private void BusinessLogic_PropertyChanged(object sender, PropertyChangedEventArgs e) and attach the handler in the constructor (public PresentationLayerClass method).
Now at the PresentationLayerClass level, in the BusinessLogic_PropertyChanged method, you have to check which property of BusinessLogicClass has been changed (by using PropertyChangedEventArgs.PropertyName), and then update the values for any property of PresentationLayerClass which is dependent on that changed property. Updating the dependent properties will be different depending on which pattern you decide to use:
PresentationLayerClass uses dependency properties: You will have to explicitly set the new values for each dependent dependency property.
PresentationLayerClass uses INotifyPropertyChanged: You will have to raise the PropertyChanged event once for every dependent property. (Pro tip: use nameof instead of hard-coding the property name).
Related
I have a WPF project using the MVVM pattern. I have an interface that for this purpose I'll call IMyData which I currently have 2 implementations of myDataImplA & myDatImplB. I want my ViewModel to be able to switch between using these 2 implementations on the fly. Currently I am passing in both implementations of the interface into the ViewModel's constructor and have Boolean property in which a ToogleButton in the View is bound to determine which one to use. I feel like there is probably a better way to do this and passing in both implementations of IMyData just feel wrong. Also if I end up having a 3rd implementation of IMyData my current method of using a Boolean to determine the implementation of IMyData to use won't work.
The interface could be updated on the fly from the view, but that would break MVVM as I think that would require extra code-behind in my View.I am looking for a more scalable solution which fits within MVVM.
You're assumption this is wrong is, IMO, accurate. The whole reason to implement the IMyData should be to avoid this behavior altogether.
I'm not sure how you have the project setup but just to help you along solving this logic consider the ViewModel just an endpoint where business logic does not live. Your ViewModel should communicate to a DAL (Data Access Layer), in which case, will pump out Model information that it updates against.
Since you're using an interface the DAL should provide that interface instantiated to the ViewModel. Implement a command (ICommand) in the ViewModel that dictates state only, such as an enum. Tell the DAL which state you want. When the DAL updates your ViewModel should be listening and react to it. At this point the ViewModel then updates the IMyData property, which should be notifying listeners, and the View will automatically change (If everything is wired up correctly.).
This probably sounds confusing so I'll try to simplify a different way.
Keep the references in a one way direction at all costs.
View -> ViewModel -> DAL (DAL is considered Model in MVVM, which in your case pumps out IMyData).
Let the View bind to a command in ViewModel stating if it wants StateN for example.
ViewModel then tells DAL to switch to StateN or automatically looks for StateN models.
DAL then changes the IMyData type to the type that works in StateN.
The ViewModel is listening to DAL for changes and when the DAL says StateNChanged the ViewModel updates the IMyData property from the DAL, which fires off a notification.
The View, bound to the ViewModel, then updates based on the new data.
If this doesn't make sense, post most of the code and I'll make the modifications and show it.
The view model shouldn't have any dependency upon any implementation of the IMyData interface. It should only have a dependency upon the interface itself.
Since you want to be able to switch the implementation dynamically at runtime, you could add a property to the view model that provides a default and interchangeable implementation:
public IMyData CurrentImplementation { get; set; } = new myDataImplA();
The view, or any other component, can then switch implemenation as requrired by simply setting this property.
I have a rather strange question about passing the Observables and subscription inside the app. Most of the examples are showing tightly coupled code, which is not really what one want.
The question is quite general, but I am using WPF with MVVM framework.
Let's say I have something like this:
Right now it is done with events and on every event Classes are filtered by ID and action is taken by the method call on found class.
I am thinking about replacing event with Rx.
Subject is a stream of events (hot) and sending data all the time. Class 1,2, etc are subscribed and act on UI (for example).
Most example like Replacing C# Events are using IDisposable and I will need it in every component.
private IDisposable planeSpottedSubscription;
jetfighter.PlaneSpotted.Where(x => string.Equals(x.Name, “Eurofighter”)).Subscribe(this.OnPlaneSpotted);
But, I can't subscribe to everything from Main, because it doesn't know about all components in UserControls and items are added dynamically (Class3, Class4 of MyType). Subscribing a Facade is not a problem.
So the question sound like:
Should I pass IObservable<JetFighter> to every control (line in ctor) and make a subscription to IDisposable (sounds strange) or can I somehow reuse Observable and just add filters?
I have been writing all my MVVM application with basic design pattern generally mentioned in MVVM examples available online. The pattern that I am following is described below:
Model
This section include DTO classes with their properties and Interfaces IDataService and like:
public class Employee
{
public string EmployeeName { get; set; }
public string EmployeeDesignation { get; set; }
public string EmployeeID { get; set; }
}
public interface IDataService
{
public Task<Employee> GetEmployeeLst();
}
Proxy
This layer contains Dataservice calls which implement IDataservice like:
public class DataService : IDataService
{
public async Task<Employee> GetEmployeeLst()
{
// Logic to get employee data from HTTPClient call
}
}
ViewModel
This layer contains ViewModel and reference to Model and Proxy layer from which all data is received:
public class BaseViewModel
{
public BaseViewModel(INavigationService nav, IDataService data, IAESEnDecrypt encrypt, IGeoLocationService geoLocation, IMessageBus msgBus, ISmartDispatcher smtDispatcher)
{
}
// This also include common methods and static properties that are shared among most of the ViewModels
}
All the ViewModel inherits BaseViewModel. Each viewModel also contains Delegatecommand which is executed when UI triggers an event. Which then fetches the data from the server by calling DataService in proxy layer and perform business logic and populates the properties in ViewModel which is binded to the view. For each View there is a VM which is binded to the Datacontext of the View.
ViewModel is also responsible for starting an animation I have used trigger to start storyboard which is binded to my enums in VM for state change of these trigger as in example in: http://www.markermetro.com/2011/05/technical/mvvm-friendly-visual-state-management-with-windows-phone-7/
View
In this layer I have all my Views, Usercontrols and business logic with implementation of certain dependencies like GeoLocation Service, AES encryption, NavigationService between Views etc.
Every View has .xaml and .xaml.cs file. In .xaml.cs file I have binded the data context of the view with VM like this:
this.DataContext = App.IOConatiner.GetInstance<DashboardViewModel>();
and from here on all binding happens.
My problem is that recently I had the knowledge that this pattern is not following a SOLID design pattern which I got know in this answer of my question:
Simple Injector inject multiple dependency in BaseClass
I am trying very hard to change my design as per the suggestion given in my previous question's answer. But I am not able to get some of the things like:
Currently View Datacontext is binded to ViewModel hence all the controls are controlled by a property in VM. How would I change this to your above mentioned pattern with Processor/Service or DialogHandler?
I am using Delegatecommands which are binded to command property of UI element. Execution of these command certain action happens like animation, usercontrol is displayed. How to do it in command pattern?
How can I start changing my current implementation to accommodate all those changes with best possible approach?
First of all an answer to your question 3
How can I start changing my current implementation to accommodate all those changes with best possible approach?
This is the very first step you need to take. It is not a case of some smart refactoring of your current code. You will need to take a step back and design the application. I once read some nice blog about (re)design.
Before starting to write any code, define how many different basic types of views you will want to show to the user? E.g.:
Just show (any type of) data
Edit data
Alert user
Ask user for input
...
When you defined your different requirements, you can translate this to specific interfaces that are tailor made for the job they serve. For example a view that lets the user edit data will typically have an interface that will look something like:
public interface IEditViewModel<TEntity>
{
public EditResult<TEntity> EditEntity(TEntity entityToEdit)();
}
Once you every detail of this design in place, you must decide how you will show your views to the user. I used another interface for this to handle this task for me. But you could also decide to let a navigation service handle this kind of task.
With this framework in place, you can start coding your implementations.
Currently View Datacontext is binded to ViewModel hence all the controls are controlled by a property in VM. How would I change this to your above mentioned pattern with Processor/Service or DialogHandler?
This will not change in this design. You will still bind your view to your viewmodel and set the datacontext to the viewmodel. With a lot of views the use of an MVVM framework like Caliburn Micro will come in handy. This will do a lot of the MVVM stuff for you, based on Convention over Configuration. To start with this model, would make the learning curve even higher, so my advice to start of by hand. You will learn this way what happens under the covers of such an MVVM tool.
I am using Delegatecommands which are binded to command property of UI element. Execution of these command certain action happens like animation, usercontrol is displayed. How to do it in command pattern?
I'm not sure if the command pattern you mention here is the command pattern I advised you in the previous answer. If so, I think you need to reread this blog, because this is totally unrelated to the commands I think you mean in this question.
Animation and that sort of stuff is the responsibility of the view, not the viewmodel. So the view should handle all this stuff. XAML has a lot of ways to handle this. More than I can explain here. Some ideas: Triggers, Dependency Properties
Another option: Code behind! If the logic is purely view related IMO it is not a mortal sin to place this code in the code behind of your view. Just don't be temped to do some gray area stuff!
For commands that just perform a method call in your viewmodel, ICommand is still possible and MVVM tools like Caliburn will do this automagically...
And still: Loose the base class....
Why are you injecting all these services in your viewmodel base class if the viewmodel base class does not make use of these services himself ?
Just inject the services you need in the derived viewmodels that do need those services.
Lets say there is a Person class that holds some attributes and some other classes Address etc. (I am just giving an example of class{class{data}})
An instance of this is deserialized and loaded in the ui. The used might change the data (the name, the address or something).
If the instance is the datasource in the ui(in every control), how can I know if the data has been changed in order to notify the user to Save before exit?
You should implement the INotifyPropertyChanged interface, which basically exposes an event that Person should raise when a property changes.
The linked documentation gives an example with a DemoCustomer class which is likely to translate pretty easily into your Person class.
To avoid anoying code for every property of every class you did and will do, you can use one of AOP frameworks. This one is one of the best actually available on market now: PostSharp. And there is also a free version, which is a good news. (by the way keep an eye on licensing, in case you're gonna use it at your work)
Here you can actually find an example how to use precisely for injecting of NotifyPropertyChanged
http://www.sharpcrafters.com/solutions#ui
Hope this helps..
Alright so. I have an app with several dialogs that have a handful of events that they all respond the same way to, and all have a few methods they provide to the Presenter. These have all been pushed up into a:
public abstract class BaseFormClass : Form
and all the other forms are:
public class DerivedFormClass : BaseFormClass
I've got a model-view-presenter setup going, so the base class has a few protected EventHandler<EventArgs>, and for each one is a similarly named function which is assigned to be called for that event, and a setter exists that the presenter can assign it's own function to be used as the handler for the event. (In other words:)
protected void OnFormBeginClosing(object sender, FormClosingEventArgs e)
{
if (formClosing == null)
return;
formClosing(sender, e);
}
public EventHandler OnFormClose
{
set
{
formClosing = value;
}
}
protected EventHander<EventArgs> formClosing;
Then the presenter uses the OnFormClose setter to set it's own handler function to handle any necessary cleanups or whatever's necessary.
Now that the backstory is out of the way, the main question is, why when I make the simple change of marking the parent Form as abstract does my design view of my child Forms go from the normal design view to just spitting out a mess of HTML (well, not a mess, a single line of what appears to be the entire HTML of the form...)?
Can anyone suggest what I might be doing wrong?
I have never tried this before, but trying the same in Visual Studio 2010, I get the error The designer must create an instance of type 'WinFormsTestApp.FormA' but it cannot because the type is declared as abstract.
I suspect this means exactly what it says - in order to display your derived form, for some reason known only to itself, the designer needs to create an instance of the parent form, and obviously can't do that. Sorry, but you will probably have to redesign your hierarchy. The VS designers make a lot of assumptions about the inheritance patterns used for forms and controls, so if you stray from the standard patterns, these problems are quite common.