I was trying to undestand how RoutedUICommand Works. I have worked with MVVM in Wpf, and was trying to under stand ICommand implementation actually works in wpf. However, I am unable to understand something.
public class RelayCommand : ICommand
{
private Action<Task> callBack;
private bool runOnUi;
private Action<object> whatToExecute;
private Func<object, bool> whenToExecute;
public RelayCommand(Func<object, bool> whenToExecute, Action<object> whatToExecute,
bool runOnUi = true, Action<Task> callBack = null)
{
this.whenToExecute = whenToExecute;
this.whatToExecute = whatToExecute;
this.runOnUi = runOnUi;
this.callBack = callBack;
}
/// <summary>
/// This is an event from Interface
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// Decides whether the command can execute or not.
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
if (this.whenToExecute != null)
{
try
{
return this.whenToExecute(parameter);
}
catch
{
return false;
}
}
return true;
}
/// <summary>
/// Called when CanExecute is true and command is fired.
/// </summary>
/// <param name="parameter"></param>
public void Execute(object parameter)
{
if (this.whatToExecute != null)
{
if (this.runOnUi)
{
this.whatToExecute(parameter);
}
else
{
var parallelTask = Task.Run(() =>
{
this.whatToExecute(parameter);
});
if (this.callBack != null)
parallelTask.ContinueWith(this.callBack);
}
}
}
}
This is the custom implementation of ICommand interface.
I have added two menuitems to context menu of label. First with ApplicationCommands.Close and second on with my customer RelayCommand type.
this.Label1.ContextMenu = new ContextMenu();
var menuItem1 = new MenuItem();//Source
menuItem1.Command = ApplicationCommands.Close; //Command
var commandBindingObject = new CommandBinding(menuItem1.Command);
commandBindingObject.CanExecute += this.MenuItem1Close_CanExecute;
commandBindingObject.Executed += this.MenuItem1Close_Executed;
menuItem1.CommandBindings.Add(commandBindingObject);
var menuItem2 = new MenuItem();//Source
menuItem2.Header = "Custom Command";
menuItem2.Command = new RelayCommand(o =>
{
return true;
},
o =>
{
}); //Command
var commandBindingObject2 = new CommandBinding(menuItem2.Command);
commandBindingObject2.CanExecute += this.MenuItem2Close_CanExecute;
commandBindingObject2.Executed += this.MenuItem2Close_Executed;
menuItem2.CommandBindings.Add(commandBindingObject2);
this.Label1.ContextMenu.Items.Add(menuItem1);
this.Label1.ContextMenu.Items.Add(menuItem2);
Whenever I click the first menu item handler is menuitem1 handler for executed is called, but not for the menuitem2. I am just trying to understand how command pattern is implemented in wpf, so in addition any links to such would also be of great help.
Routed Commands
The ApplicationCommands.Close command is a RoutedUICommand. Routed commands raise the PreviewExecuted and Executed routed events which traverse the element tree until they find a command binding. If one is found the corresponding ExecutedRoutedEventHandler is called. In case of menuItem1 the executed handler is MenuItem1Close_Executed. The same applies to the PreviewCanExecute and CanExecute events and the MenuItem1Close_CanExecute handler as CanExecuteRoutedEventHandler.
Relay Commands
Your menuItem2 uses a RelayCommand. This type of command uses delegates for CanExecute and Execute, instead of traversing the element tree in search for command bindings.
In your case the following lines are unnecessary.
var commandBindingObject2 = new CommandBinding(menuItem2.Command);
commandBindingObject2.CanExecute += this.MenuItem2Close_CanExecute;
commandBindingObject2.Executed += this.MenuItem2Close_Executed;
menuItem2.CommandBindings.Add(commandBindingObject2);
Instead pass methods or anonymous methods for whenToExecute and whatToExecute.
menuItem2.Command = new RelayCommand(CanExecute, Execute);
You cannot pass MenuItem1Close_CanExecute or MenuItem1Close_Executed here, because they have incompatible signatures:
void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e)
void ExecutedRoutedEventHandler(object sender, ExecutedRoutedEventArgs e)
CanExecute has to be a Func<object, bool> and Execute has to be an Action<object> delegate.
bool CanExecute(object obj)
void Execute(object obj)
Related
Context
Xaml:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:WpfApp2"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
Title="MainWindow" Height="350" Width="400">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="The ListView:"/>
<ListView
Grid.Row="1"
ItemsSource="{Binding ItemCollection}"
SelectionMode="Single">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction
Command="{Binding Path=ProcessChangeCommand}"
CommandParameter="{Binding Path=SelectedItem,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
</Grid>
</Window>
Code behind:
public partial class MainWindow : Window {
public MainWindow () {
InitializeComponent ();
}
}
public class ViewModel : INotifyPropertyChanged {
bool colorList;
string[] colors = { "blue", "yellow", "green", "orange", "black" };
string[] towns = { "Dakar", "Berlin", "Toronto" };
private ObservableCollection<string> itemCollection;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> ItemCollection {
get => itemCollection;
set {
itemCollection = value;
RaisePropertyChanged (nameof (ItemCollection));
}
}
public ICommand ProcessChangeCommand { get; private set; }
public ViewModel () {
ProcessChangeCommand = new RelayCommand<string> (ProcessChange);
itemCollection = new ObservableCollection<string> (colors);
}
public void ProcessChange (string parameter) {
if (parameter == null) return;
Debug.WriteLine ($"Selected: {parameter}");
ItemCollection = new ObservableCollection<string> (colorList ? colors : towns);
RaisePropertyChanged (nameof (ItemCollection));
colorList = !colorList;
}
void RaisePropertyChanged ([CallerMemberName] string propertyName = null) {
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName));
}
}
public class RelayCommand<T> : ICommand {
readonly Action<T> _execute = null;
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand (Action<T> excute) { _execute = excute; }
public bool CanExecute (object parameter) => true;
public void Execute (object parameter) => _execute ((T) parameter);
}
Using .Net Framework 4.8.
Add package Microsoft.Xaml.Behaviors.Wpf to the project.
The ListView displays a list. When a selection is done, its value is shown on the console and the list is swapped (there are two lists shown alternatively).
Problem
The "color" list is longer than the "town" list, and when orange or black are selected, after the selection is shown on the console and the list is swapped (normal), the first item of the town list, Dakar, is triggered (unexpected). When debugging, after clicking orange, ProcessChange is invoked 4 times:
with parameter orange (expected),
with parameter null (unexpected but understandable and discarded in the code. The call is a reentrant call happenning while ProcessChange is processing orange)
with parameter Dakar (unexpected and wrong),
with null(same as second bullet, also reentrant, occurring while processing unexpected Dakar call)
The resulting console output:
Observation: This double event anomaly disappears if the grid rows are set this way:
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/> <!-- height set to * -->
</Grid.RowDefinitions>
(or if a 100ms delay is introduced, or a breakpoint is set, in the event handler).
And the question is:
What is the reason Dakar appears on the console after orange?
You do not take into account that after changing the list, the selection is reset and the command is triggered a second time without completing the current execution.
It is necessary to exclude its repeated execution during the execution of the command.
To recheck a command, you need to call its event from the code.
This requires a slightly different implementation of the command.
Base class:
using System;
using System.Windows;
using System.Windows.Input;
namespace Common
{
#region Delegates for WPF Command Methods
/// <summary>Delegate of the executive team method.</summary>
/// <param name="parameter">Command parameter.</param>
public delegate void ExecuteHandler(object parameter);
/// <summary>Command сan execute method delegate.</summary>
/// <param name="parameter">Command parameter.</param>
/// <returns><see langword="true"/> if command execution is allowed.</returns>
public delegate bool CanExecuteHandler(object parameter);
#endregion
#region Class commands - RelayCommand
/// <summary>A class that implements the ICommand interface for creating WPF commands.</summary>
public class RelayCommand : ICommand
{
private readonly CanExecuteHandler _canExecute;
private readonly ExecuteHandler _onExecute;
private readonly EventHandler _requerySuggested;
public event EventHandler CanExecuteChanged;
/// <summary>Command constructor.</summary>
/// <param name="execute">Executable command method.</param>
/// <param name="canExecute">Method allowing command execution.</param>
public RelayCommand(ExecuteHandler execute, CanExecuteHandler canExecute = null)
{
_onExecute = execute;
_canExecute = canExecute;
_requerySuggested = (o, e) => Invalidate();
CommandManager.RequerySuggested += _requerySuggested;
}
public void Invalidate()
=> Application.Current.Dispatcher.BeginInvoke
(
new Action(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty)),
null
);
public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute.Invoke(parameter);
public void Execute(object parameter) => _onExecute?.Invoke(parameter);
}
#endregion
}
Command without parameter:
namespace Common
{
#region Delegates for WPF Parameterless Command Methods
/// <summary>Delegate to the execution method of a command without a parameter.</summary>
public delegate void ExecuteActionHandler();
/// <summary>Command state method delegate without parameter.</summary>
/// <returns><see langword="true"/> - if command execution is allowed.</returns>
public delegate bool CanExecuteActionHandler();
#endregion
/// <summary>Class for commands without parameters.</summary>
public class RelayActionCommand : RelayCommand
{
/// <summary>Command constructor.</summary>
/// <param name="execute">Command execution method.</param>
/// <param name="canExecute">Method allowing command execution.</param>
public RelayActionCommand(ExecuteActionHandler execute, CanExecuteActionHandler canExecute = null)
: base(_ => execute(), _ => canExecute()) { }
}
}
Typed Parameter Command
using System.ComponentModel;
namespace Common
{
#region Delegates for WPF Command Methods
/// <summary>Delegate of the executive team method.</summary>
/// <param name="parameter">Command parameter.</param>
public delegate void ExecuteHandler<T>(T parameter);
/// <summary>Command сan execute method delegate.</summary>
/// <param name="parameter">Command parameter.</param>
/// <returns><see langword="true"/> if command execution is allowed.</returns>
public delegate bool CanExecuteHandler<T>(T parameter);
#endregion
/// <summary>Class for typed parameter commands.</summary>
public class RelayCommand<T> : RelayCommand
{
/// <summary>Command constructor.</summary>
/// <param name="execute">Executable command method.</param>
/// <param name="canExecute">Method allowing command execution.</param>
public RelayCommand(ExecuteHandler<T> execute, CanExecuteHandler<T> canExecute = null)
: base
(
p => execute
(
p is T t
? t
: TypeDescriptor.GetConverter(typeof(T)).IsValid(p)
? (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(p)
: default
),
p =>
canExecute == null
|| (p is T t
? canExecute(t)
: TypeDescriptor.GetConverter(typeof(T)).IsValid(p) && canExecute((T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(p))
)
)
{}
}
}
ViewModel
using Common;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
namespace WpfApp2
{
public class ViewModel
{
bool isColorList;
string[] colors = { "blue", "yellow", "green", "orange", "black" };
string[] towns = { "Dakar", "Berlin", "Toronto" };
private bool isExecutedProcessChange = false;
public ObservableCollection<string> ItemCollection { get; }
= new ObservableCollection<string>();
public RelayCommand ProcessChangeCommand { get; }
public ViewModel()
{
ProcessChangeCommand = new RelayCommand<string>(ProcessChange, CanProcessChange);
Array.ForEach(colors, color => ItemCollection.Add(color));
}
private bool CanProcessChange(string parameter)
=>! isExecutedProcessChange;
public void ProcessChange(string parameter)
{
isExecutedProcessChange = true;
ProcessChangeCommand.Invalidate();
if (parameter != null)
{
Debug.WriteLine($"Selected: {parameter}");
ItemCollection.Clear();
Array.ForEach(isColorList ? colors : towns, color => ItemCollection.Add(color));
isColorList = !isColorList;
}
isExecutedProcessChange = false;
ProcessChangeCommand.Invalidate();
}
}
}
XAML does not require changes.
To find the reasons for double selection, I added a delay to the method:
public async void ProcessChange(string parameter)
{
isExecutedProcessChange = true;
ProcessChangeCommand.Invalidate();
if (parameter != null)
{
Debug.WriteLine($"Selected: {parameter}");
ItemCollection.Clear();
Array.ForEach(isColorList ? colors : towns, color => ItemCollection.Add(color));
isColorList = !isColorList;
await Task.Delay(500);
}
isExecutedProcessChange = false;
ProcessChangeCommand.Invalidate();
}
The double choice is almost over.
But sometimes it happens anyway.
Apparently, this is due to the bounce of the mouse button.
That is, instead of one signal of pressing a key from the mouse, sometimes two are received.
Setting a small delay (500ms) was able to filter out some of these double events.
The filter also helps not to run the command twice, but the item in the new list is still selected.
And you cannot select it again.
For the command to work, you have to select another element.
The solution may be to increase the delay and postpone it before changing the list.
Check how convenient it will be to use in this case:
public async void ProcessChange(string parameter)
{
isExecutedProcessChange = true;
ProcessChangeCommand.Invalidate();
if (parameter != null)
{
Debug.WriteLine($"Selected: {parameter}");
await Task.Delay(1000); // It is necessary to select the optimal delay time
ItemCollection.Clear();
Array.ForEach(isColorList ? colors : towns, color => ItemCollection.Add(color));
isColorList = !isColorList;
}
isExecutedProcessChange = false;
ProcessChangeCommand.Invalidate();
}
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 have a problem that the CanExecuteChanged is not done when I change a property. I pretty much figured out why. However I am not really sure how to fix it. The issue apparently comes from changing the property on a different thread.
My code..
Command:
class StartTestCommand : ICommand
{
private MainViewModel viewModel;
public StartTestCommand(MainViewModel viewModel)
{
this.viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
bool canExecute = true;
if (viewModel.SerialPortNo==null)
{
canExecute = false;
}
if (viewModel.IsTestRunning)
{
canExecute = false;
}
return canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
Task.Factory.StartNew(() => { viewModel.StartTest(); });
}
}
And the thing that is called on a separate thread:
/// <summary>
/// Start the testing
/// </summary>
public void StartTest()
{
StartSerial();
ExecuteSelectedTests();
StopSerial();
}
/// <summary>
/// start serial communication
/// </summary>
private void StartSerial()
{
serialManager.Start(serialPortNo);
}
/// <summary>
/// stop serial communication
/// </summary>
private void StopSerial()
{
serialManager.Stop();
}
/// <summary>
/// runs the selected tests
/// </summary>
private void ExecuteSelectedTests()
{
this.IsTestRunning = true;
foreach (var item in testItemsToRunCollection)
{
item.Execute();
}
this.IsTestRunning = false;
}
So the IsTestRunning is set from another thread. I would like to know what would be the correct way of fixing this problem :)
You can dispatch the code to be run on the UI thread like this:
Dispatcher.Invoke(((Action)(() => ]
{
this.IsTestRunning = false;
CommandManager.InvalidateRequerySuggested();
})));
I am trying to set a dependency property which is updated by a WCF callback thread.
There is a ProgressBar on MainWindow.xaml that is bound to this property:
MainWindow.xaml
<ProgressBar Name="ProgressBar" Value="{Binding Progress, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
MainWindow has an instance of DemoModule, which is defined as:
DemoModule.xaml.cs
/// <summary>
/// Interaction logic for DemoModule.xaml
/// </summary>
public partial class DemoModule : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register("Progress", typeof(int), typeof(DemoModule));
public event ProgressEventHandler ProgressChanged;
public event PropertyChangedEventHandler PropertyChanged;
public int Progress
{
get { return (int)GetValue(ProgressProperty); }
set { SetValue(ProgressProperty, value); } // setter throws InvalidOperationException "The calling thread cannot access this object because a different thread owns it"
}
/// <summary>
/// Initializes a new instance of the <see cref="DemoModule" /> class.
/// </summary>
public DemoModule()
{
InitializeComponent();
ProgressChanged += OnProgressChanged;
}
/// <summary>
/// Called when [progress changed].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="args">The <see cref="ProgressChangedEventArgs" /> instance containing the event data.</param>
public void OnProgressChanged(object sender, ProgressChangedEventArgs args)
{
Debug.WriteLine("Current Thread: {0}", Thread.CurrentThread.ManagedThreadId);
Debug.WriteLine("Current Dispatcher Thread: {0}", Application.Current.Dispatcher.Thread.ManagedThreadId);
if (ProgressChanged == null) return;
Debug.WriteLine("ProgressChangedEventArgs.Current: " + args.Current);
Progress = Convert.ToInt32(args.Current * 100);
OnPropertyChanged("Progress");
}
/// <summary>
/// Called when [property changed].
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
Trace.WriteLine("Property " + propertyName + " changed. Value = " + Progress);
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The Progress.set() is throwing an exception because of the thread affinity.
How can I fix this?
Update 1
This is allegedly thread safe, but has no effect:
public int Progress
{
get
{
return Dispatcher.Invoke((() => (int)GetValue(ProgressProperty)));
}
set
{
Dispatcher.BeginInvoke((Action)(() => SetValue(ProgressProperty, value)));
}
}
Update 2
My DemoModule.xaml.cs has a reference to a client library which implements the WCF callback method OnUpdateProgress:
InstallerAgentServiceClient.cs
public void OnUpdateProgress(double progress)
{
//Debug.WriteLine("Progress: " + progress*100 + "%");
var args = new ProgressChangedEventArgs(progress, 1, "Installing");
_installModule.OnProgressChanged(this, args);
}
The _installModule object above is the instance of DemoModule.
Update 3
After removing the [CallBackBehavior] attribute from the WCF client library, there no longer seems to be thread synchronization issues. I can update the progress bar in the MainWindow as follows:
DemoModule.xaml.cs
public void OnProgressChanged(object sender, ProgressChangedEventArgs args)
{
Progress = Convert.ToInt32(args.Current * 100);
var progressBar = Application.Current.MainWindow.FindName("ProgressBar") as ProgressBar;
if (progressBar != null)
progressBar.Value = Progress;
}
You need to update your DepedencyProperty via the UI Thread. Use:
Application.Current.Dispatcher.BeginInvoke(Action)
Or:
Application.Current.Dispatcher.Invoke(Action)
I recommand using the IProgress interface.
Works like a charm for me and is pretty easy to use.
In your progressbarVM add
public double Actualprogress
{
get { return (double)GetValue(ActualprogressProperty); }
set { SetValue(ActualprogressProperty, value); }
}
public static readonly DependencyProperty ActualprogressProperty =
DependencyProperty.Register("Actualprogress", typeof(double), typeof(ProgressBar),
new PropertyMetadata(0.0));
then call your method as an asyn task using await like so :
var progress = new Progress<double>(progressPercent =>
progressBarVM.progressBar.Actualprogress = progressPercent);
Parser parser = new Parser();
ModelVMResult result = await Task.Run(() => parser.Parse(filename,progress));
then in your method "parse" just do:
float threshold = 0.0f;
for (int i = 0; i < count; i++)
{
if (i >= threshold)
{ progress.Report(prog += 1); threshold += count / 100.0f; }
this.Readline(reader, i);
}
Of course you need to bind your xaml progressbar.Value to ProgressbarVM.Actualprogress.
Then your progressbar will update and your app will still be responsive during the process.
I want to pass assignment_id which is a command parameter of myButton to .cs file to update values in database. I wrote this code but when i clicked at myButton it showed a Message Box that Method and Operation is failed. How can i solve this Problem?
private void Command(Int32 parameter)
{
p = parameter;
}
private void btnUpdate_Click(Object sender, RoutedEventArgs e)
{
try
{
SqlConnection con = new SqlConnection("server=(local); Integrated Security=true; Database=nrcs");
con.Open();
SqlCommand comm = new SqlCommand("UPDATE Assignments SET assignment_title='myassignment' WHERE assignment_id=" + p + ";", con);
comm.ExecuteNonQuery();
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
Here Xaml Code:
<Button x:Name="myButton" Content="update" Command="{Binding Command}" CommandParameter="{Binding assignment_id}" Click="btnUpdate_Click" ></Button>
It seems like you might be trying to mix commanding and event handlers. If you want to use commanding, you should first create a viewmodel with the command defined there. Then set your datacontext in xaml to that viewmodel. Then you should be able to bind the commands the way you want to. A common approach would be to first define a RelayCommand class.
A simple relay class might look something like this:
public class RelayCommand<T> : ICommand
{
#region Fields
readonly Action<T> _execute = null;
readonly Predicate<T> _canExecute = null;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
/// </summary>
/// <param name="execute">Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.</param>
/// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
public RelayCommand(Action<T> 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<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
///<summary>
///Defines the method that determines whether the command can execute in its current state.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
///<returns>
///true if this command can be executed; otherwise, false.
///</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
///<summary>
///Occurs when changes occur that affect whether or not the command should execute.
///</summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
///<summary>
///Defines the method to be called when the command is invoked.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
public void Execute(object parameter)
{
_execute((T)parameter);
}
#endregion
}
That example came from this question Why RelayCommand
Once you have a RelayCommand class, in your ViewModel you can define commands such as
public class MainWindowViewModel
{
private ICommand _Command;
public ICommand Command
{
get
{
if (_Command == null)
_Command = new RelayCommand<object>((x) => ExecuteCommand(x));
return _Command;
}
set
{
_Command = value;
}
}
private void ExecuteCommand(object parameter)
{
try
{
if (!string.IsNullOrEmpty(parameter.ToString()){
//Do sql call here. parameter.ToString() is a string representation of the parameter that was bound on the xaml
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
}
Now that you have that set up, just set your view's data context to the view model. You can do this in the code behind, or in the xaml, whatever you want. Something like
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
Now your binding in xaml to the command should look like:
<Button x:Name="myButton" Content="update" Command="{Binding Command}" CommandParameter="{Binding assignment_id}"></Button>
However while we're here, please change your sql command to use parameters instead of concatenating it as a string like that. Doing it the way you are now will leave you open to sql injection.
Edit: it should be noted that the object you are binding to the CommandParameter should exist in your view model as well. Ideally you would have a model containing the property for assignment_id then you would instantiate a type of that model in the view model, then bind that instance of the model to your View.
I think it's the order in which the click event (btnUpdate_Click) and the command are called. If the event is called first, the variable 'p' doesn't have a value, and the query would fail.
I think you should use either the event or the command to perform the query, not both of them. if you intend to keep using the MVVM pattern (which I recommend), use the command in another class that would be your viewmodel.