I have a class called Commander and it have a property called CurrentRound and it gets updated every few seconds internally. I'm using CommunityToolkit.MVVM and In my viewmodel I declared an observable property called CurrentRound and in the constructor of my viewmodel I did this currentRound = _commander.CurrentRound;.
The strange thing is that when I put a breakpoint in my Commander class and pause the program, I can see the updated value in my xaml file(view) but not on the actual UI.
Commander Class:
public partial class Commander
{
private readonly Communication communication;
public Commander(Communication communication)
{
this.communication = communication;
ThreadStart work = this.NameOfMethodToCall;
Thread thread = new Thread(work);
thread.Start();
}
private async void NameOfMethodToCall()
{
while(true)
{
await Task.Delay(10000);
Console.WriteLine("hey I'm the second thread!");
CurrentRound = GetCurrentRound();
}
}
public int CurrentRound { get; private set; } = 1;
}
ViewModel:
public partial class ParametersViewModel: ObservableObject
{
public ParametersViewModel(Commander commander, ...)
{
_commander = commander;
currentRound = _commander.CurrentRound;
...
}
private readonly Commander _commander;
...
[ObservableProperty]
int currentRound;
...
}
View:
<Label
Text="{Binding CurrentRound, StringFormat='Current Round = {0}'}"
HorizontalOptions="Center"
FontAttributes="Bold"
FontSize="Body"/>
...
UPDATE:
Still having the same issue!
ViewModel:
public partial class ParametersViewModel: ObservableObject
{
...
[ObservableProperty]
Commander myCommander;
...
}
View:
<Label
Text="{Binding MyCommander.CurrentRound, ...
SECOND UPDATE:
This update solved my issue but I should make my Commander class inherits from ObservableObject and make its CurrentRound property an ObservableProperty, but when using MVVM design pattern I should not worry about UI in the models and business logic, but if I do this, I should care about UI bindings in the models right? isn't there a better ways of doing this?
Commander Class:
public partial class Commander: ObservableObject
{
...
[ObservableProperty]
int currentRound = 1;
...
}
Just want to point out something.
Considering:
and in the constructor of my viewmodel I did this currentRound =
_commander.CurrentRound
And:
[ObservableProperty]
int currentRound;
currentRound is a field.
CurrentRound is a property.
Setting fields does not call code. Setting properties does.
All the problems you had, comes from no following naming conventions.
Property A has field _a, not a.
Related
I have a DatePicker control and also there is a command to load data for specific date. How can I get a value of DatePicker and pass it to command?
My ViewModel class now:
public class MainWindowViewModel
{
private readonly JiraHandler jiraHandler;
private DateTime date;
public ICommand DateChosen { get; }
public ObservableCollection<Issue> Issues { get; private set; } = new ObservableCollection<Issue>();
public MainWindowViewModel(JiraHandler jiraHandler)
{
this.jiraHandler = jiraHandler;
}
}
The xaml part should look like something like this at the end:
<DatePicker x:Name="MyDatePicker">
</DatePicker>
<Button Content="Click me!"
Command="{Binding Path=DateChosen}"
CommandParameter="{Binding Path=DisplayDate, ElementName=MyDatePicker}"/>
And in the ViewModel ICommand:
private ICommand _dateChosen ;
public ICommand DateChosen
{
get
{
if (_dateChosen == null)
{
_dateChosen = new RelayCommand(param => OnDateChosen(param));
}
return _dateChosen ;
}
}
For the implementation of the RelayCommand i can recommend this link: RelayCommand
On the OnDateChosen(object param) method you can cast the 'param' parameter to DateTime.
Good luck!
I recommend the community toolkit mvvm. It provides a relaycommand implementation of icommand, messenger, base classes and does code generation.
https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
Here's a quick sample to give you the idea of how this could work:
<StackPanel>
<DatePicker Name="DateSelection"
SelectedDate="{Binding DateSelected}"/>
<Button Content="DoSomething"
Command="{Binding DateChosenCommand}"
CommandParameter="{Binding SelectedDate, ElementName=DateSelection}"/>
</StackPanel>
And the viewmodel:
public partial class MainWindowViewModel : ObservableObject
{
private readonly JiraHandler jiraHandler;
[ObservableProperty]
private DateTime? dateSelected;
partial void OnDateSelectedChanged(DateTime? newDate)
{
// Fires when bound Date value changes
}
[RelayCommand]
private void DateChosen(DateTime? selectedDate)
{
if (selectedDate != null)
{
dateSelected = (DateTime)selectedDate;
}
}
[ObservableProperty]
private ObservableCollection<Issue> issues = new ObservableCollection<Issue>();
public MainWindowViewModel(JiraHandler jiraHandler)
{
this.jiraHandler = jiraHandler;
}
You should use DateSelected from the datepicker. That is a nullable DateTime?
You could potentially bind that selected date and the command references that value.
Notes:
Aways implement inotifypropertychanged on any viewmodel ( to avoid memory leaks. ) The ObservableObject does so. There is also a ObservableValidator which gives validation support.
The public properties are generated in another partial class file.
You automatically get methods you can provide for property changed or propertychanging. This allows you to conveniently act when say the date is selected in your datepicker.
I am implementing a WPF application and I am switching view models on button click. I had to implement an navigation store by youtube tutorial. When I click a button, navigateCommand will execute, creating a new viewModel and notifying view to change. However I dont understand what is method OnCurrentViewModelChanged() doing and why is it needed, action CurrentViewModelChanged is returning void, and is empty? Or am I missing something? What is CurrentViewModelChanged doing? Can someone please explain?
public class NavigationStore
{
public event Action CurrentViewModelChanged;
private NotifyPropertyChanged currentViewModel;
public NotifyPropertyChanged CurrentViewModel
{
get => currentViewModel;
set
{
currentViewModel = value;
OnCurrentViewModelChanged();
}
}
private void OnCurrentViewModelChanged()
{
CurrentViewModelChanged?.Invoke();
}
}
public class NavigateCommand<TViewModel> : CommandBase where TViewModel : NotifyPropertyChanged
{
private readonly NavigationStore _navigationStore;
private readonly Func<TViewModel> _createViewModel;
public NavigateCommand(NavigationStore navigationStore, Func<TViewModel> createViewModel)
{
_navigationStore = navigationStore;
_createViewModel = createViewModel;
}
public override void Execute()
{
_navigationStore.CurrentViewModel = _createViewModel();
}
}
public class MainViewModel : NotifyPropertyChanged
{
private readonly NavigationStore _navigationStore;
public NotifyPropertyChanged CurrentViewModel => _navigationStore.CurrentViewModel;
public MainViewModel(NavigationStore navigationStore)
{
_navigationStore = navigationStore;
_navigationStore.CurrentViewModelChanged += OnCurrentViewModelChanged;
}
private void OnCurrentViewModelChanged()
{
OnPropertyChanged(nameof(CurrentViewModel));
}
}
So first of all, I also followed his tutorials (it's most likely SingletonSean's) and I don't share #BenicCode's opinion on that (tho I'm not a professional at WPF like he may be), I really like his explanations and solutions to problems. Besides, he keeps changing the project throughout the guide, implementing better solutions and explaining why it's better to use this than that.
The OnCurrentViewModelChanged() method raises an event so that any method that is subscribed to it will be invoked. However, you actually don't need it, you can implement NavigationStore like this:
NavigationStore.cs
public class NavigationStore : INavigationStore
{
private ViewModelBase? _currentViewModel;
public ViewModelBase? CurrentViewModel
{
get => _currentViewModel;
set
{
_currentViewModel?.Dispose();
_currentViewModel = value;
NavigationStateChanged?.Invoke();
}
}
public event Action? NavigationStateChanged;
}
And now, in your MainViewModel, you can simply subscribe the NavigationStateChanged action to OnCurrentViewModelChanged() instead of having one more method in your navigation store.
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private readonly INavigationStore _navigationStore;
public ViewModelBase? CurrentViewModel => _navigationStore.CurrentViewModel;
public MainViewModel(INavigationStore navigationStore)
{
_navigationStore = navigationStore;
_navigationStore.NavigationStateChanged += OnNavigator_NavigationStateChanged;
}
private void OnNavigator_NavigationStateChanged()
{
OnPropertyChanged(nameof(CurrentViewModel));
}
}
It's basically the same, but a bit simpler (correct me if I'm wrong). By subscribing NavigationStateChanged to OnNavigator_NavigationStateChanged, whenever NavigationStateChanged is raised, OnNavigator_NavigationStateChanged will fire too, which will notify your UI to change (since you bind the ContentControl's Content property to the CurrentViewModel property).
MainWindow.xaml
<Grid>
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
At this point of the tutorial he just wanted to demonstrate really basic navigation. As you progress further, things get cleaner and more complicated. I really suggest finishing his tutorials, there might be better guides, but as a starting point, I couldn't find any better channel.
I have a situation and I'm not sure if I'm doing it correct.
I have a ApplicationViewModel that is my "shell" for my whole application.
And within that viewmodel I have other child-ViewModels.
public ApplicationModelView()
{
ModelViewPages.Add(new GameViewModel());
ModelViewPages.Add(new EditGameViewModel());
//Set Current HomePage
CurrentPage = ModelViewPages[0];
}
#endregion
#region Properties
public BaseModelView CurrentPage
{
get { return _currentPage; }
set { _currentPage = value; OnPropertyChanged(); }
}
public List<BaseModelView> ModelViewPages
{
get
{
if (_modelViewPages == null){_modelViewPages = new List<BaseModelView>();}
return _modelViewPages;
}
}
#endregion
In my GameViewModel I display a list of objects from my model GamesModel that contains title,description etc.
When I click on an item in this list it becomes selected and then I want to change my View to EditGameViewModel with a button but I'm not sure if how to do it.
How can I get my child-ViewModel to change content in my parent-ViewModel?
Or should the child even do that?
EDIT
How I want it to function
I want when I select an item and click on button that I change from the view GameViewModel to EditGameViewModel with the data that I have selected from the list.
public void EditGame(object param)
{
//MessageBox.Show("From EditGame Function"); HERE I WANT TO CHANGE THE VIEWMODEL ON MY APPLICATIONVIEWMODEL
}
public bool CanEditGame(object param)
{
return SelectedGame != null;
}
I can offer something that works, but could be questionable. It all really depends on how you plan for your application to function.
First, similar to your MainViewModel, you want something like this:
public class MainViewModel : ObservableObject //ObservableObject being a property change notification parent
{
//Current view will always be here
public BaseViewModel ViewModel { get; set; }
public MainViewModel()
{
//By default we will say this is out startup view
Navigate<RedViewModel>(new RedViewModel(this));
}
public void Navigate<T>(BaseViewModel viewModel) where T : BaseViewModel
{
ViewModel = viewModel as T;
Console.WriteLine(ViewModel.GetType());
OnPropertyChanged("ViewModel");
}
}
Now, since we are navigating this way, every child view needs to derive from BaseViewModel.
public class BaseViewModel : ObservableObject
{
private MainViewModel _mainVM;
public BaseViewModel(MainViewModel mainVM)
{
_mainVM = mainVM;
}
protected void Navigate<T>() where T : BaseViewModel
{
//Create new instance of generic type(i.e. Type of view model passed)
T newVM = (T)Activator.CreateInstance(typeof(T), _mainVM);
//Change MainViewModels ViewModel to the new instance
_mainVM.Navigate<T>(newVM);
}
}
Now we just really need to see how we are going to have child views delegate this call of change. So we will have a BlueView.
public class BlueViewModel : BaseViewModel
{
//Relay command to call 'ToRed' function
public ICommand ChangeToRed
{
get { return new RelayCommand(ToRed); }
}
//Requires MainViewModel for BaseViewModel
public BlueViewModel(MainViewModel main) : base(main)
{
}
//Calling BaseViewModel function. Passed BaseViewModel Type
public void ToRed(object param)
{
Navigate<RedViewModel>();
}
}
And a RedView:
public class RedViewModel : BaseViewModel
{
//Relay command to call 'ToBlue' function
public ICommand ChangeToBlue
{
get { return new RelayCommand(ToBlue); }
}
//Requires MainViewModel for BaseViewModel
public RedViewModel(MainViewModel main) : base(main)
{
}
//Calling BaseViewModel function. Passed BaseViewModel Type
public void ToBlue(object param)
{
Navigate<BlueViewModel>();
}
}
Our MainWindow.xaml could look like this:
<Window.DataContext>
<viewmodels:MainViewModel/>
</Window.DataContext>
<Grid>
<ContentControl Content="{Binding ViewModel}" />
</Grid>
Doing this all children will be able to call to their parent that they would like a change. The BaseViewModel holds this parent for all children who derive, so they can pass it back and forth during navigation like a baton.
Again, Navigation all really depends on how you are using, building and planning for your application. It's not always a one size fits all way.
I've created an application that need a lot of access to UI controls, now what I did firstly is create an interface scalable, in particular I created different controls as UserControl and one class ViewModel that manage all method of this control for update the UI. Actually all working good in the Main thread. In particular the followin scenario working perfect:
MainWindow XAML
xmlns:MyControls="clr-namespace:HeavyAPP"
...
<!-- I use the control in the following way: -->
<Grid>
<MyControls:Scheduler x:Name="Sc"/>
</Grid>
so for example the Scheduler control contains this Data Binding:
<StackPanel Orientation="Horizontal">
<Label x:Name="NextSync" Content="{Binding NextSynchronization, IsAsync=True}" ></Label>
</StackPanel>
ViewModel structure
public class ViewModelClass : INotifyPropertyChanged
{
private CScheduler scheduler;
public ViewModelClass()
{
scheduler = new Scheduler();
}
public string NextSynchronization
{
get
{
return scheduler.GetNextSync();
}
}
}
How you can see in the ViewModel I've an instance of the Scheduler control and a property called NextSyncrhonization as the binding, so this property return a result from the method of the control instance.
For use this in the MainWindow I did the following:
public MainWindow()
{
ViewModelClass viewModel = new ViewModelClass();
DataContext = viewModel;
}
this automatically fill the control property. Now the problem's that I use a BackgroundWorker for perform some task, what I need is use the DataContext of MainWindow from different classes, (not Window, but classes).
For solve this situation I though to do something like this:
MainWindow.AppWindow.Sc.SyncLog.Dispatcher.Invoke(
new Action(() =>
{
ViewModelClass viewModel = new ViewModelClass();
var dataContext = System.Windows.Application.Current.MainWindow.DataContext;
dataContext = viewModel;
viewModel.SynchronizationLog = "this is a test from other thread"}));
now SynchronizationLog is another property that append the text to the Control, just for precision, is this:
private string _text;
public string SynchronizationLog
{
get
{
return _text += _text;
}
set
{
_text = value;
OnPropertyChanged();
}
}
this is the implementation of INotifyPropertyChanged:
`public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}`
this working only in the MainWindow, but in the external classes I can't update the UI, what am I doing wrong?
I reiceve no error, anyway.
Try the following:
extend your ViewModel as follow:
public class ViewModelClass : INotifyPropertyChanged
{
private CScheduler scheduler;
//Add this:
public static ViewModelClass Instance {get; set;} //NEW
public ViewModelClass()
{
scheduler = new Scheduler();
}
public string NextSynchronization
{
get
{
return scheduler.GetNextSync();
}
}
}
This changes your code in the xaml.cs to:
public MainWindow()
{
ViewModelClass.Instance = new ViewModelClass();
DataContext = viewModel.Instance;
}
In your external code you then DONT create a new Instance of the ViewModelClass - instead you use the existing one:
[...].Dispatcher.Invoke(() =>
{
if(ViewModelClass.Instance != null)
{
//Why you need the "var datacontext" in your example here ?
ViewModelClass.Instance.SynchronizationLog = "this is a test from other thread"
}
}));
Basically what you do here is setting a property in your ViewModel from outside of your viewModel. This can be done from everywhere.
What is different to your approach:
We dont create a new Instance of the ViewModel (different bindings in the UI aren't resetted anymore)
We created an Instance so there can always be ONLY ONE viewModel at a time
I'm a newbie in this WPF world, I have the following situation:
I am developing a multimedia-related application using VLC and the Caliburn.Micro, I have encountered a problem where I need the variable TotalTime from the MainViewModel to be shared with the TextBox on the SettingsViewModel.
This variable happens to change every second, so it has to be notified every second.
MainViewModel -> string TotalTime
SettingsViewModel -> TextBox Time
I have tried to do it with events, but I haven't succeeded.
If you nest the SettingsViewModel in the MainViewModel as a property (say Settings), you can bind an UI element to it like this:
Text = "{Binding Path=Settings.TotalTime}"
if the View's DataContext is set to an instance of the MainViewModel
In general, the solution to this problem is a singleton messenger that both view-models have injected into them on creation (usually from an IoC container). In your scenario, SettingsViewModel would subscribe to messages of a particular type, say TotalTimeChangedMessage, and MainViewModel would send messages of that type whenever TotalTime changed. The message simply contains the current value of TotalTime, e.g.:
public sealed class TotalTimeChangedMessage
{
public string totalTime;
public TotalTimeChangedMessage(string totalTime)
{
this.totalTime = totalTime;
}
public string TotalTime
{
get { return this.totalTime; }
}
}
Caliburn.Micro contains an event aggregator that you can use to pass messages in this fashion between view-models. Your MainViewModel would send the message like this:
public class MainViewModel
{
private readonly IEventAggregator _eventAggregator;
public MainViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public string TotalTime
{
set
{
this.totalTime = value;
_eventAggregator.Publish(new TotalTimeChangedMessage(this.totalTime));
}
}
}
..and your SettingsViewModel would handle it like this:
public class SettingsViewModel: IHandle<TotalTimeChangedMessage>
{
private readonly IEventAggregator eventAggregator;
public SettingsViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
this.eventAggregator.Subscribe(this);
}
public void Handle(TotalTimeChangedMessage message)
{
// Use message.TotalTime as necessary
}
}