I am using MVVM solution provided in my previous question
XAML
<ProgressBar x:Name="StatusProgressBar" IsIndeterminate="{Binding IsIndeterminate, Mode=OneWay}" Height="18" Width="120" VerticalAlignment="Center" Background="White" BorderBrush="#FF05438D" />
ViewModel
Notice here in DoExecuteGetIpCommand() method if i do same thing in code behind on content rendered event works correctly but in mvvm all codes fires at same time so progress bar update after all time consuming process.
So i want to set ProgressBar IsIndeterminate Property true while time consuming method is working after done finally set IsIndeterminate to false. any idea to this and why it is happening but working fine in code behind Content rendered event.
public class MainWindowViewModel : INotifyPropertyChanged
{
public bool _isIndeterminate;
private string _ipAdrress;
private bool _errorOccured;
public event PropertyChangedEventHandler PropertyChanged;
GetPublicIP getPublicIP = new GetPublicIP();
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/*
public bool IpIndeterminate
{
get { return _isIndeterminate; }
set
{
_isIndeterminate = value;
OnPropertyChanged(nameof(IpIndeterminate));
}
}
*/
//OR
//IsIndeterminate here is problem
public bool IsIndeterminate => _isIndeterminate;
public string IpAddress => _ipAdrress;
public Brush IpForeground => _errorOccured ? new SolidColorBrush(Colors.IndianRed) : new SolidColorBrush(Colors.Black);
public FontWeight IpFontWeight => _errorOccured ? FontWeights.SemiBold : FontWeights.Normal;
public ICommand GetIpCommand
{
get { return new RelayCommand(param => DoExecuteGetIpCommand()); }
}
private async void DoExecuteGetIpCommand()
{
_isIndeterminate = true;
try
{
_errorOccured = false;
//_ipAdrress = await MyService.GetIpAddress();
_ipAdrress = await getPublicIP.GetIPAddressAsync();//time consuming method.
}
finally
{
//Commented this because progress bar immediately Is indeterminate go false.
//_isIndeterminate = false;
}
if (await getPublicIP.ExceptionOccursAsync() == true)
{
_errorOccured = true;
}
OnPropertyChanged(nameof(IsIndeterminate));
OnPropertyChanged(nameof(IpAddress));
OnPropertyChanged(nameof(IpForeground));
OnPropertyChanged(nameof(IpFontWeight));
}
}
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute"); //NOTTOTRANS
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
You have to change your code like this: (as dymanoid said)
private async void DoExecuteGetIpCommand()
{
_isIndeterminate = true;
//Tell the UI that something changed...
OnPropertyChanged(nameof(IsIndeterminate));
try
{
_errorOccured = false;
_ipAdrress = await getPublicIP.GetIPAddressAsync();//time consuming method.
}
finally
{
_isIndeterminate = false;
}
if (await getPublicIP.ExceptionOccursAsync() == true)
{
_errorOccured = true;
}
OnPropertyChanged(nameof(IsIndeterminate));
OnPropertyChanged(nameof(IpAddress));
OnPropertyChanged(nameof(IpForeground));
OnPropertyChanged(nameof(IpFontWeight));
}
Related
!!! SOLVED, THANK YOU VERY MUCH !!!
I'm writing my first MVVM application (in WPF C#). Because of that, I want to use commands instead "Click" event defined in a view. The command, which I want to induce is really simple, it should to create and open a view.
I have written RelayCommand class which inherits ICommand interface.
internal class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
I write a method changing value of the field, which represent a view.
private bool openSoundsWindow;
private bool openChordsWindow;
public bool OpenSoundsWindow
{
get { return openSoundsWindow; }
set { openSoundsWindow = value; }
}
public bool OpenChordsWindow
{
get { return openChordsWindow; }
set { openChordsWindow = value; }
}
public void OpenSounds()
{
openSoundsWindow = true;
}
public void OpenChords()
{
OpenChordsWindow = true;
}
I wrote in view model class commands by RelayCommand and OnPropertyChanged event. View model class inherits INotifyPropertyChanged.
private MainModel model = new MainModel();
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public bool OpenSoundsWindow
{
get { return model.OpenSoundsWindow; }
set
{
model.OpenSoundsWindow = value;
OnPropertyChanged(nameof(OpenSoundsWindow));
}
}
private ICommand openSounds = null;
public ICommand OpenSounds
{
get
{
if (openSounds == null)
{
openChords = new RelayCommand(
(object o) =>
{
model.OpenSounds();
OnPropertyChanged(nameof(OpenSoundsWindow));
var newSoundsWindow = new Sounds();
newSoundsWindow.Show();
},
(object o) =>
{
return model.OpenSoundsWindow != null;
});
}
return openSounds;
}
}
I created instance of view model in view's xaml code.
xmlns:vm="clr-namespace:HearingTeacher.ViewModels"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
<Window.Resources>
<vm:MainViewModel x:Key="mainViewModel" />
</Window.Resources>
I binded property command for buttons with created commands in view model.
<Button Grid.Row="0" Content="Sounds" Command="{Binding Path=OpenSounds,
UpdateSourceTrigger=PropertyChanged}" />
Compiler doesn't throw any exception, and .NET starts an application correctly, but commands doesn't work.
sorry for so much code.
But unfortunately there is so much connected.
That's why I had to insert so much code.
I tried to keep it to a minimum.
I hope it is enough.
To my problem:
As you can see here there is a button to select a path on the computer.
There is also a textbox which shows the path again.
XAML UserControl Code:
<DockPanel Grid.Row="1" Grid.Column="1">
<Button
Content="Repo Pfad"
Command="{Binding SelectRepoPathCommand}"/>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="2">
<TextBox Text="{Binding repoPath, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DockPanel>
Here the code is stored in the config for the user.
So that he does not have to enter the path again and again at the next start.
Small additional Info:
Since the path to the config is saved successfully, the test box will be updated after the second time you select the path.
This is because the second time the previously saved path is displayed.
ViewModel:
class BuildToolViewModel : ObservableObject
{
private string _repoPath;
public string repoPath
{
get
{
if (Properties.Settings.Default.repoPath != null)
{
return Properties.Settings.Default.repoPath;
}
else
{
return _repoPath;
}
}
set
{
_repoPath = value;
OnPropertyChanged("repoPath");
Properties.Settings.Default.repoPath = _repoPath;
Properties.Settings.Default.Save();
}
}
public RelayCommand SelectRepoPathCommand{ get; private set; }
#endregion #Properties
#region ctor
public BuildToolViewModel()
{
SelectRepoPathCommand = new RelayCommand(SelectRepoPath);
}
#endregion //ctor
#region Methods
public void SelectRepoPath(object sender)
{
repoPath = explorerDialog.OpenFileDialogPath();
}
}
Here is my ObservableObject that inherits from INotifyPropertyChanged.
ObservableObject (INotifyPropertyChanged):
class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Here is my RelayCommand that inherits from ICommand.
RelayCommand (ICommand):
class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region ctor
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion //ctor
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion // ICommand Members
}
Here still like it in the MainWindowViewModel with the ICommand implement.
MainWindowViewModel (ObservableObject):
class MainWindowViewModel : ObservableObject
{
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
{
_changePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return _changePageCommand;
}
}
Fix you property repoPatch code:
private string _repoPath;
public string repoPath
{
get
{
if (string.IsNullOrEmpty(_repoPath))
{
_repoPath= Properties.Settings.Default.repoPath;
}
return _repopath
}
....................
............
I found the error, the problem is that the "repoPath" can be null or empty, and you are only validating that it is not null. but if the value of "repoPath" is empty, it will always return an empty value, since the value is different from null. You need to change your validation to
if (!string.IsNullOrEmpty(Properties.Settings.Default.repoPath))
also, I can see that you are saving the value of the variable "_repoPath" to the user settings "avlPath" instead of "repoPath", is it correct?
I develop a WPF application, obviously, I use MVVM pattern. Without an external library (MvvmCross, MvvmLight, ...)
And I've tried to implement ICommand:
Option 1
public class Command : ICommand
{
private readonly Func<bool> _canExecute;
private readonly Action _action;
public Command1(Action action, Func<bool> canExecute)
{
_action = action;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _action();
}
Option 2
public class Command : ICommand
{
private readonly Func<bool> _canExecute;
private readonly Action<object> _action;
public Command1(Action<object> action, Func<bool> canExecute)
{
_action = action;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _action(parameter);
}
Option 3
...with some delegates
public class Command : ICommand
{
private readonly Func<object, bool> _canExecute;
private readonly Action<object> _execute;
public Command(Action<object> execute) => _execute = execute ?? throw new ArgumentNullException(nameof(execute));
public Command(Action execute)
: this((Action<object>)delegate { execute(); })
{
if (execute == null)
{
throw new ArgumentNullException(nameof(execute));
}
}
public Command(Action<object> execute, Func<object, bool> canExecute)
: this(execute) => _canExecute = canExecute ?? throw new ArgumentNullException(nameof(canExecute));
public Command(Action execute, Func<bool> canExecute)
: this(delegate
{
execute();
}, (object o) => canExecute())
{
if (execute == null)
{
throw new ArgumentNullException(nameof(execute));
}
if (canExecute == null)
{
throw new ArgumentNullException(nameof(canExecute));
}
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter) => _canExecute != null ? _canExecute(parameter) : true;
public void Execute(object parameter) => _execute(parameter);
}
In all cases:
public class MainViewModel : BaseViewModel
{
public ICommand MyCommand = new Command(() => MyVoid());
private void MyVoid()
{
// do something
}
}
public class MainViewModel : BaseViewModel
{
public ICommand MyCommand = new Command(MyVoid);
private void MyVoid()
{
// do something
}
}
I've a CS0201 error (Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement).
I don't understand why.
In other projects, which use MVVM pattern (Xamarin.Forms, Xamarin, ...), I use Xamarin.Forms.Command or MvxCommand (MvvmCross) and it works...
I don't suggest to name the realying class Command because you may get naming conflict with some built-in classes.
The closest to your try code:
public ICommand MyCommand => new Command(parameter => { MyVoid(); });
or the same in "block" syntax
public ICommand MyCommand
{
get
{
return new Command(parameter => { MyVoid(); });
}
}
But it's wrong approach because it will create new Command instance each time the command was called. Give to Garbage Collector as less work as possible. Examples of the correct ways doing it you may find below.
You're developing another bicycle. :) Look here.
Here is copy-paste from my project. It's exactly the same as in above link.
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
}
And the usage
<Button Contecnt="Click me!" Command="{Binding MyCommand}"/>
private ICommand _myCommand;
public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
{
// do here the execution
}));
And with parameter (Binding for CommandParameter is available too)
<Button Contecnt="Click me!" Command="{Binding MyCommand}" CommandParameter="test value"/>
public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
{
if (parameter is string p && p == "test value")
{
// do here the execution
}
}));
And finally, usage of optional CanExecute
public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
{
// do here the execution
// don't put the same condition as in CanExecute here,
// it was already checked before execution has entered this block
},
parameter => (x > y) && (a + b > c) // any condition or just return a bool
));
if CanExecute returns false, the command will not be executed and Button or MenuItem becomes automatically disabled. Just test it.
I am trying to pass CommandParameter to the method in my ViewModel.
How to do this?
private void Open(object sender)
{
if (sender==this.objMainWindow.btnHistory)
{
objMainWindow.Container.Child = objHistory;
}
if (sender == this.objMainWindow.btnNew_Item)
{
objMainWindow.Container.Child = objNewItem;
}
if (sender == this.objMainWindow.btnSide_Effects)
{
objMainWindow.Container.Child = objSideEffect;
}
}
This is my meyhod in ViewModel that I want to pass CommandParameter. I use CommandParameter for button.
"ViewModel" implies MVVM. If you're doing MVVM you shouldn't be passing views into your view models. Typically you do something like this in your XAML:
<Button Content="Edit"
Command="{Binding EditCommand}"
CommandParameter="{Binding ViewModelItem}" >
And then this in your view model:
private ViewModelItemType _ViewModelItem;
public ViewModelItemType ViewModelItem
{
get
{
return this._ViewModelItem;
}
set
{
this._ViewModelItem = value;
RaisePropertyChanged(() => this.ViewModelItem);
}
}
public ICommand EditCommand { get { return new RelayCommand<ViewModelItemType>(OnEdit); } }
private void OnEdit(ViewModelItemType itemToEdit)
{
... do something here...
}
Obviously this is just to illustrate the point, if you only had one property to edit called ViewModelItem then you wouldn't need to pass it in as a command parameter.
Just using Data Binding syntax. For example,
<Button x:Name="btn"
Content="Click"
Command="{Binding ClickCmd}"
CommandParameter="{Binding ElementName=btn,Path=Content}" />
Not only can we use Data Binding to get some data from View Models, but also pass data back to View Models. In CommandParameter, must use ElementName to declare binding source explicitly.
If you are that particular to pass elements to viewmodel You can use
CommandParameter="{Binding ElementName=ManualParcelScanScreen}"
Try this:
public class MyVmBase : INotifyPropertyChanged
{
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler( MyAction));
}
}
public void MyAction(object message)
{
if(message == null)
{
Notify($"Method {message} not defined");
return;
}
switch (message.ToString())
{
case "btnAdd":
{
btnAdd_Click();
break;
}
case "BtnEdit_Click":
{
BtnEdit_Click();
break;
}
default:
throw new Exception($"Method {message} not defined");
break;
}
}
}
public class CommandHandler : ICommand
{
private Action<object> _action;
private Func<object, bool> _canExecute;
/// <summary>
/// Creates instance of the command handler
/// </summary>
/// <param name="action">Action to be executed by the command</param>
/// <param name="canExecute">A bolean property to containing current permissions to execute the command</param>
public CommandHandler(Action<object> action, Func<object, bool> canExecute)
{
if (action == null) throw new ArgumentNullException(nameof(action));
_action = action;
_canExecute = canExecute ?? (x => true);
}
public CommandHandler(Action<object> action) : this(action, null)
{
}
/// <summary>
/// Wires CanExecuteChanged event
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Forcess checking if execute is allowed
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_action(parameter);
}
public void Refresh()
{
CommandManager.InvalidateRequerySuggested();
}
}
And in xaml:
<Button
Command="{Binding ClickCommand}"
CommandParameter="BtnEdit_Click"/>
I try to use commands with the MVVM - Pattern and I don't know how to "bind" a command to a special event, e.g. MouseUp or MouseEnter. How to do this?
First you should define ICommnad property in your ViewModel.
public ICommand MouseUpCommand
{
get
{
if (this.mouseUpCommand == null)
{
this.mouseUpCommand = new RelayCommand(this.OnMouseUp);
}
return this.mouseUpCommand;
}
}
private void OnMouseUp()
{
// Handle MouseUp event.
}
You can find lot of ICommand implementations. One of them:
public class RelayCommand : ICommand
{
public RelayCommand(Action<object> execute)
{
this._execute = execute;
...
}
...
public void Execute(object parameter)
{
_execute(parameter);
}
}
Then add event trigger within which invoke your Command:
<i:EventTrigger EventName="MouseUp">
<i:InvokeCommandAction Command="{Binding MouseUpCommand}"/>
</i:EventTrigger>
Read the EventToCommand at the following page, please
Look at WPF Binding UI events to commands in ViewModel.
For this you need System.Windows.Interactivity.dll which you can get from Nuget
Completing #AnatoliiG post here's an implementation and a sample usage of the RelayCommand class.
Code:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
Usage:
// To use this class within your viewmodel class:
RelayCommand _myCommand;
public ICommand MyCommand
{
get
{
if (_myCommand == null)
{
_myCommand = new RelayCommand(p => this.DoMyCommand(p),
p => this.CanDoMyCommand(p) );
}
return _myCommand;
}
}