I'm currently developing an app in WPF using the MVVM pattern ( without framework ). I use VS2019,
Each view is an UserControl
The app is connected to a local database MySQLLite.
When I start my program, I have an user connection. When the user connection is successful, it loads my object "Engine" in my global class "BaseViewModel" ( inherit all ViewModel ).
In WinForm when I create a new Form(View), I just transfer my Engine class in parameters and I save the Engine locally in my Form ( not null ) and then I have access to my value in Engine like User.
What is the best way to do it in WPF using MVVM?
I try to transfer the Engine to my ViewModel when I create but it always overwrites it later with null. Because it opens the view without parameter later and calls my constructor without parameter.
You can create Data Access Level class to load your engine from DB. For example, it will be named EngineDataAccess and it will have GetEngine() method. Next in your EngineViewModel you can implement something like this:
private readonly Engine engine;
public string EngineName
{
get {return engine.Name; }
set {engine.Name = value; OnPropertyChanged("EngineName");}
}
public EngineViewModel(EngineDataAccess engineDataAccess)
{
engine = engineDataAccess.GetEngine();
}
In your code:
EngineDataAccess engineDataAccess = new EngineDataAccess();
EngineViewModel engineViewModel = new EngineViewModel(engineDataAccess);
form.DataContext = engineViewModel;
What is not very good in this way and how it can be done better:
1) getting engine from db on creating view model process => it will be better to do it on Load view model (MVVM load data during or after ViewModel construction?)
2) using EngineDataAccess class instead of interface
3) binding datacontext in the codebehind => better to use IoC
Also, I would recommend you to use some MVVM framework like Galasoft.MVVM. It does MVVM using simpler.
Related
I'm creating a note taking application with multiple windows to gather information. How do I save the values of multiple text boxes in multiple windows to variables on button click?
I have successfully implemented it in the MainWindow and I'm having trouble extending the paradigm across multiple windows. I have a feeling it's associated with not assigning a name to the instance of the second window, but I'm not sure how it works.
This is the code for the function that works in the MainWindow:
public static void CopyText()
{
string srText = ((MainWindow)Application.Current.MainWindow).srBox.Text;
string contactText = ((MainWindow)Application.Current.MainWindow).contactBox.Text;
string usernameText = ((MainWindow)Application.Current.MainWindow).usernameBox.Text;
string generalText = ((MainWindow)Application.Current.MainWindow).generalBox.Text;
string copyText = "";
Clipboard.SetDataObject(copyText);
}
This is the code for the function in the CreditWindow that does not work:
public static void CopyCreditText()
{
string srText = ((MainWindow)Application.Current.MainWindow).srBox.Text;
string usernameText = ((MainWindow)Application.Current.MainWindow).usernameBox.Text;
string buyerText = ((CreditWindow)Application.Current.MainWindow).buyerBox.Text;
string itemText = ((CreditWindow)Application.Current.MainWindow).itemBox.Text;
string amountText = ((CreditWindow)Application.Current.MainWindow).amountBox.Text;
string typeText = ((CreditWindow)Application.Current.MainWindow).typeBox.Text;
string reasonText = ((CreditWindow)Application.Current.MainWindow).reasonBox.Text;
string copyText = "";
Clipboard.SetDataObject(copyText);
}
When I run this code, I get an error from Visual Studio on this line:
string buyerText = ((CreditWindow)Application.Current.MainWindow).buyerBox.Text;
that states "Unable to cast object of type 'MSONotes.MainWindow' to type 'MSONotes.CreditWindow'.
The error is because Application.Current.MainWindow is just that, the main window of the entire application. It doesn't change no matter how many child windows exist. It won't become a CreditWindow.
Unless the windows have references to each other, you should handle this kind of data passing well behind the view layer. I lied, you should do that regardless, but references would allow you to keep cheating. In a properly architected application:
Those text boxes would be bound to fields in a view model
The button click would push the values to some model (shared by the other window's VMs, ideally using dependency injection)
Via an event or similar the service would notify all VMs that new data was available
They would update their own appropriate fields
The other windows view's would update automatically due to the VM update.
Basically, you are going to need to do WPF properly (with MVVM) and not hack it together like that.
I created a WPF MVVM project in Devexpress with the Scaffolding wizard and everything created works fine, I even modified the grids so they call the SaveCommand on RowUpdated.
Now I'm trying to insert new registers programmatically and my strategy was to get and instance of the CollectionViewModel from my model and use its Save method sending an object from the same model as a parameter.
I was reading this guide but still couldn't find what i'm doing wrong.
This is the code
Transaction transaction = new Transaction();
transaction.IdClient = 1;
TransactionCollectionViewModel tcvm = TransactionCollectionViewModel.Create(UnitOfWorkSource.GetUnitOfWorkFactory());
tcvm.Save(transaction);
and it gives me this error on the variable tcvm when calling the Save function
System.NullReferenceException was unhandled by user code
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=DevExpress.Mvvm.v16.1.DataModel
StackTrace:
at DevExpress.Mvvm.DataModel.RepositoryExtensions.<>c__DisplayClass1_0`3.<GetProjectionPrimaryKey>b__0(TEntity x)
at DevExpress.Mvvm.DataModel.RepositoryExtensions.GetProjectionValue[TEntity,TProjection,TEntityResult,TProjectionResult](TProjection value, Func`2 entityFunc, Func`2 projectionFunc)
at DevExpress.Mvvm.DataModel.RepositoryExtensions.GetProjectionPrimaryKey[TEntity,TProjection,TPrimaryKey](IRepository`2 repository, TProjection projectionEntity)
at DevExpress.Mvvm.DataModel.RepositoryExtensions.FindExistingOrAddNewEntity[TEntity,TProjection,TPrimaryKey](IRepository`2 repository, TProjection projectionEntity, Action`2 applyProjectionPropertiesToEntity)
at DevExpress.Mvvm.ViewModel.CollectionViewModelBase`4.Save(TProjection projectionEntity)
at Template.ViewModels.ClientCollectionViewModel..ctor(IUnitOfWorkFactory`1 unitOfWorkFactory) in C:\Users\edwin\Source\Workspaces\INVASST\Template\Template\ViewModels\Client\ClientCollectionViewModel.cs:line 37
at ClientCollectionViewModel_97cd3897_6fb7_469b_9928_d61260161e61..ctor(IUnitOfWorkFactory`1 unitOfWorkFactory)
InnerException:
I'm using Devexpress 16.1.5 and the project is using the HybridApp template
... my strategy was to get an instance of the CollectionViewModel from my model and use its Save method sending an object from the same model as a parameter.
I'm afraid this strategy is wrong. You should use the New() command provided by the corresponding CollectionViewModel (bound to the Grid).
The real usage depends on your specific needs. In a simple case, you can just add a button above the grid and then bind the New command to this button.
Related DX support thread : Batch Insert/ Edit with Master/Detail
I'm using WPF MVVM trying to figure out what would be the best way to reload my ViewModel (entire View would work as well I suppose).
The data behind my Model is parsed out of a series of flat files stored within a directory. The location of the directory is saved in the .Settings file and can be the user via a popup window.
If the user changes updates the directory they want to use, how can I recreate my ViewModel so that the data being used is what is in the new directory?
I guess it would be akin to changing to a different database while the app is running if that is what I had as a datastore. Currently I show a message asking the user to restart the application.
Here is the important code:
public SignalViewModel()
{
_trafficSignals = new ObservableCollection<TrafficSignal>(DataAccess.TrafficSignalRepository.GetTrafficSignals());
}
public static List<TrafficSignal> GetTrafficSignals()
{
string dataStore = Properties.Settings.Default.SaveLocation;
var signals = new List<TrafficSignal>();
if (Directory.Exists(dataStore))
{
var files = Directory.GetFiles(dataStore, "CP*.SAV");
Array.Sort(files);
foreach (var file in files)
{
signals.Add(LoadFile(file));
}
}
return signals;
}
I would solve this problem with two events:
Implement the INotifyPropertyChanged in the settings.
In the TrafficSignalRepository I would then implement an event (e.g. SaveLocationChanged) which you raise after the PropertyChanged event of the settings was raised for the SaveLocation-Property
Then you can register for the SaveLocationChanged event inside of the ViewModel. In the registered event handler you just call GetTrafficSignals() again, assign the new value to the field and raise the NotifyPropertyChanged event of the ViewModel. The rest should be done for you automatically by data binding.
You can reload the saved settings using
Properties.Settings.Default.Reload();
And then call GetTrafficSignals() again?
Although having said that, I have had some issues in using the Reload method in the past...
I am building an application, which has a form where the user can configure all his settings. When the application is loaded, the previously configured settings should reflect to the GUI (The UI should be consistent to the saved settings).
What I am currently doing is creating the settings on the project properties and I have a LoadSettings() method, which gets the values and outputs them to each component on the UI.
The thing is that this is getting VERY messy, and I don't like it at all.
So, that got me wondering, what are the correct approaches to achieve what I want, but yet getting high quality code? Any patterns for that?
private void LoadConfigs()
{
checkBoxStartOnStartup.Checked = ExistKeyValue(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "Wallbase Downloader");
checkBoxCheckWallbaseOnline.Checked = Settings.Default.CheckWallbaseOnlineStartup;
comboBoxResolution.SelectedIndex = comboBoxResolution.FindStringExact(Settings.Default.Resolution == string.Empty
? GetScreenResolution()
: Settings.Default.Resolution);
comboBoxCondition.SelectedIndex = Settings.Default.ConditionIndex;
textBoxWallpaperFolders.Text = Settings.Default.WallpaperFolder;
numericChangeInterval.Text = Convert.ToString(Settings.Default.ChangeIntervalValue);
comboBoxChangeInterval.SelectedIndex = Settings.Default.ChangeIntervalIndex;
numericCheckInterval.Text = Convert.ToString(Settings.Default.CheckIntervalValue);
comboBoxCheckInterval.SelectedIndex = Settings.Default.CheckIntervalIndex;
numericWallpapersToLookFor.Text = Settings.Default.WallpapersToLookFor.ToString();
}
Well, WinForms are not the cleanest framework around...
What you could do is to load all settings when your application starts up and store them in some storage that is available to all forms, e.g. in a static property in a helper settings class.
You can then access that static property from each form when it loads and make all necessary changes to the form based on the settings.
You can use a Hashtable and use English strings for key to make your code really readable. Then serialize it to file on exit and deserialize it back when application loads. Save the serialized file to some common location so that you do not lose it.
This is more of a theory question than anything else, can't really show much in the way of code. However the topic is C# WPF.
So I have an application beginning with a main menu where I set a bunch of variables and load in some data from files and so on ready to begin the main program. I want this to be available to the main window that is going to be opened up.
Currently in my application I have it all set up by data context, an attempt at MVVM which I am very new to. This data context is linked to the menu window and allows me to set all the necessary data.
But how do I go about making that available to the new window. As far as I am aware a new data context is like a new object but in xaml, so if I were to create a data context in the next window all the information from it would be different?
I have always had trouble with things like this in all my learning of programming, I have data in one place but there is no sound way to link it across multiple windows/classes/objects etc.
Currently I have 4 files:
1 Holding all the data classes
2 My 'ViewModel' where I have the objects of these classes, property
updates, not too much
3 My menu with the bindings and data context and its code behind which is empty apart from some button
clicks
4 The main window that is going to use the data <-- this is my problem
First of all is this a correct approach, is there something different I should be thinking about? Can I implement a data context across two windows?
So I set up the context in the mainmenu window:
<Window.DataContext>
<local:WindowFunction x:Name="Interface"/>
</Window.DataContext>
And this links in my between file which has objects of the data.
class WindowFunction
{
protected PortInfo settings = new PortInfo();
protected FileInfo import = new FileInfo();
protected ObservableCollection<PersonName> lanes = new ObservableCollection<PersonName>();
public ObservableCollection<PersonName> Lane
{
get { return lanes; }
set { lanes = value; }
}
public PortInfo Settings
{
get { return settings; }
set { settings = value; }
}
public FileInfo Import
{
get { return import; }
set { import = value; }
}
}
I want to be able to call the information from this in another window. But if I create a new context the property paths will be pointing somewhere else surely?