I'm pretty new to WPF, and now I stumbled on something for which I could not find the answer anywhere on the internet. I have the following problem:
Within the same solution, I have 2 projects. One is an application that represents a production process, called MaintenancePlanner. The other is a GUI called MaintenancePlannerGUI.
What I want to achieve is the following: upon pressing a button, the simulation of my production process starts (which takes place in MaintenancePlanner). Then, in the MaintenancePlannerGUI, I have for example a progressbar. The value of the progressbar should change according to the value of the property of an object within the MaintenancePlanner simulation.
Therefore, I need to bind this somehow. However, I don't understand how to do this. I make use of the MVVM structure. So my structure looks like follows:
- MaintenancePlanner
- AssemblyFab.cs
- AssemblyLine.cs
- ShellModel.cs (something like Program.cs, but specifically to be used for MaintenancePlannerGUI only)
- MaintenancePlannerGUI
- Views
- ShellViewModel.cs
- ViewModels
- ShellView.xaml
- Views
Now, AssemblyLine for example contains a property Speed
. Note that multiple instances of AssemblyLine are attached to AssemblyFab, in the form of a List<AssemblyLine> AssemblyLines
.
In ShellView.xaml I have a progressbar:
<ProgressBar Width="10" Height="45" Margin="0,5,10,0" Orientation="Vertical" Minimum="0" Maximum="50" Value="{Binding ???}"/>
In ShellViewModel.cs I create an instance of the MaintenancePlanner simulation AssemblyFabSim
by creating an instance of ShellModel.cs from MaintenancePlanner where the whole AssemblyFab and its constituents are created, like this:
AssemblyFabSim = new ShellModel();
Now, I tried something very crude like:
Value="{Binding AssemblyFabSim.AssemblyFab.AssemblyLines[0].Speed}
But that obviously didn't work. Another idea that came to my mind is to make use of the NotifyPropertyChanged Methods.
So in that case, I could create a property in ShellViewModel.cs named for example test
and bind that to my progressbar. Then I could update test by getting a notification if the property changed in the ShellModel.cs. But then I also need to monitor the changes in AssemblyFab and AssemblyLine from within ShellModel.cs, so to propagate the change from AssemblyLine to AssemblyFab to ShellModel to ShellViewModel to the View. And I am a little bit confused about this approach.
private void ShellModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Speed")
{
test = AssemblyFabSim.AssemblyFab.AssemblyLines[0].MouldCleanInterval;
}
}
I was wondering whether this is indeed the way to go, and if so, how to do this exactly? Or are there perhaps other simpler ways? Thanks in advance!
Edit 1
My ShellViewModel.cs now contains the following, as ShellViewModel inherits the INotifyPropertyChanged class like this ShellViewModel : INotifyPropertyChanged
public ShellModel AssemblyFabSim { get; set; }
AssemblyFabSim.PropertyChanged = ShellModel_PropertyChanged;
private void ShellModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "TestSpeed")
{
test = AssemblyFabSim.AssemblyFab.AssemblyLines[0].Speed;
}
}
private double _test;
public double test
{
get { return _test; }
set
{
_test = value;
NotifyOfPropertyChange();
}
}
And I now bind my progressbar value like Value="{Binding test}
. Then ShellModel.cs also will inherit INotifyPropertyChanged
and I add:
public void UpdateSpeed()
{
try
{
TestSpeed = AssemblyFab.AssemblyLines[0].Speed;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
NotifyOfPropertyChange(nameof(TestSpeed));
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
And the UpdateSpeed() method is called from within the Assemblyline.
New problem: The value of the processbar gets updated, but only after the simulation is finished I see the result. That is, the GUI just freezes until the simulation stops and it then shows the last value of Speed.
CodePudding user response:
If your shellViewModel has a property like
public ShellModel AssemblyFabSim {get;}
you should be able to bind to it, and if all the other properties in the path is correct they should also work. But as far as I know, bindings does not support indexers, so I do not think AssemblyLines[0]
will work.
Wpf does not care about what projects classes are defined in, only that it has a object to bind to, and the properties are correctly named. Usually everything should also implement INotifyPropertyChanged
to work well.
But note that deeply nested paths is probably not a good idea, you should try to separate the UI and business logic, so you should try to avoid binding to anything other than viewModel classes designed for it.
Notably, if you have a progress bar you should bind to a Progress<T>
object that is handed to the method that needs to report progress. You should avoid using a progress-property on the object performing the work, after all, what would happen if the method was called concurrently from multiple threads?
CodePudding user response:
You need to ensure that you are calling the UpdateSpeed()
on a background thread since the UI thread cannot both update the progress bar and execute your code simultaneously.