WPF ProgressBar not updating - wpf

This is casual and prototype code, hence me trying what I think should work, googling around if it doesn't, then asking here after perusing similar questions.
I have the following markup in my Shell view:
<StatusBarItem Grid.Column="0">
<TextBlock Text="{Binding StatusMessage}" />
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2">
<ProgressBar Value="{Binding StatusProgress}" Minimum="0" Maximum="100" Height="16" Width="198" />
</StatusBarItem>
Then in ShellViewModel I have the following two properties and an event handler:
private string _statusMessage;
public string StatusMessage
{
get => _statusMessage;
set => SetProperty(ref _statusMessage, value);
}
private double _statusProgress;
public double StatusProgress
{
get => _statusProgress;
set => SetProperty(ref _statusProgress, value);
}
private void OnFileTransferStatusChanged(object sender, FileTransferStatusEventArgs fileTransferStatusEventArgs)
{
StatusMessage = fileTransferStatusEventArgs.RelativePath;
StatusProgress = fileTransferStatusEventArgs.Progress;
}
The event is raised periodically, i.e. every n iterations, from a file download helper class.
Now the strange thing is this, when the event handler updates the vm properties, on the Shell view, the TextBlock bound to StatusMessage updates and displays correctly, but the ProgressBar bound to StatusProgress does not, and remains blank. If I put a break-point in the event handler, I can see the StatusProgress property being properly updated in various values from 0 to 100, yet this does not reflect on the ProgressBar.
The idea of the event handler executing on another thread, which often causes UI update problems, occurred to me, but why is one UI element updating properly and the other not?
NOTE: I have been monumentally stupid and not tested the ProgressBar statically, i.e. just set the viewmodel's StatusProgress to a value and get the shell window to display, without going through the download loop. If I do this, the progress bar displays a length that more or less corresponds to its Value property. None of the layout change suggestions made in comments or answers changes this. Statically it is always visible and always displays a value.
EXAMPLE: I created a small example that believe duplicates the problem. In the example the progress bar doesn't update until the waited on task has completed, and I believe this is the case with my main question, but it was a long download, and I didn't wait for it to complete before noticing the progress bar wasn't updating.
Here is the StatusBar in `MainWindow.xaml:
<StatusBar DockPanel.Dock="Bottom" Height="20">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="2">
<ProgressBar Value="{Binding StatusProgress}" Maximum="100" Minimum="0" Height="16" Width="198" />
</StatusBarItem>
</StatusBar>
With the code behind in MainWindow.xaml.cs:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
public MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ViewModel.Download();
}
And the code in the MainWindowViewModel:
private string _statusMessage = "Downloading something";
public string StatusMessage
{
get => _statusMessage;
set
{
if (value == _statusMessage) return;
_statusMessage = value;
OnPropertyChanged();
}
}
private int _statusProgress;
public int StatusProgress
{
get => _statusProgress;
set
{
if (value == _statusProgress) return;
_statusProgress = value;
OnPropertyChanged();
}
}
public void Download()
{
var dl = new FileDownloader();
dl.ProgressChanged += (sender, args) =>
{
StatusProgress = args.Progress;
};
dl.Download();
}
And finally the code for FileDownloader:
public class ProgressChangedEventArgs
{
public int Progress { get; set; }
}
public class FileDownloader
{
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
public void Download()
{
for (var i = 0; i < 100; i++)
{
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs{Progress = i});
Thread.Sleep(200);
}
}
}
In the example, the progress bar remains blank, until FileDownloader finishes its loop, and then suddenly the progress bar shows full progress, i.e. complete.

What's happening
Anything that is not about UI should be done in tasks, because, if not, you're blocking the UI thread and the UI.
In your case, the download was happening on you UI thread, the latter was waiting for the download to finish before updating your UI.
Solution
You need to do two things to solve your problem:
remove the work from the UI thread.
make sure the work can communicate with you UI thread.
So, first, start the download work as a Task like this:
private ICommand _startDownloadCommand;
public ICommand StartDownloadCommand
{
get
{
return _startDownloadCommand ?? (_startDownloadCommand = new DelegateCommand(
s => { Task.Run(() => Download()); },
s => true));
}
}
and connect the button to the command like this:
<Button Command="{Binding StartDownloadCommand}" Content="Start download" Height="20"/>
Then have you download method as such:
public void Download()
{
Application.Current.Dispatcher.Invoke(() => { StatusMessage = "download started"; });
var dl = new FileDownloader();
dl.ProgressChanged += (sender, args) =>
{
Application.Current.Dispatcher.Invoke(() => { StatusProgress = args.Progress; });
};
dl.Download();
Application.Current.Dispatcher.Invoke(() => { StatusMessage = "download DONE"; });
}
The dispatch will have your property (on UI thread) updated from a non UI thread.
And yet, the DelegateCommand helper class:
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute)
: this(execute, null) {}
public DelegateCommand(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
Remarks
In order to implement the MVVM pattern I had this code behind:
public partial class MainWindow : IView
{
public IViewModel ViewModel
{
get { return (IViewModel)DataContext; }
set { DataContext = value; }
}
public MainWindow()
{
DataContext = new MainWindowViewModel();
}
}
public interface IViewModel {}
public interface IView {}
and this View:
<Window x:Class="WpfApp1.MainWindow"
d:DataContext="{d:DesignInstance local:MainWindowViewModel,
IsDesignTimeCreatable=True}"
xmlns:local="clr-namespace:WpfApp1"
and this ViewModel:
public class MainWindowViewModel: INotifyPropertyChanged, IViewModel

This happens, because StatusBarItem default style sets its HorizontalContentAlignment to Left, which leads ProgressBar to get only a small amount of space horizontally.
You can make the ProgressBar to fill the StatusBarItem completely by setting StatusBarItem's HorizontalContentAlignment to Stretch or you can set the Width of the ProgressBar.

ProgressBar is a DispatcherObject, and DispatcherObject can be only accessed by the Dispatcher it is associated with.
If I understand your question well your OnFileTransferStatusChanged is being triggered on a background thread, so since you're not accessing controls using a Dispatcher (or from the UI thread) you're not guaranteed that the code will work.
The problem is that binding from a non-UI thread usually works until it doesn't - e.g. on a non-dev machine.

like first answer
be sure to be on the main UI thread, because OnFileTransferStatusChanged is on another thread. use this in your event
Application.Current.Dispatcher.Invoke(prio, (ThreadStart)(() =>
{
StatusMessage = fileTransferStatusEventArgs.RelativePath;
StatusProgress = fileTransferStatusEventArgs.Progress;
}));

I made few changes to your sample as your file downloaded is working on UI thread and application just freezes you can see it by changing focus to other application and trying to get back - window will not appear nor update.
changes:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() => ViewModel.Download());
}
forces download to execute in new thread.
public MainWindow()
{
InitializeComponent();
DataContext = ViewModel = new MainWindowViewModel();
}
public MainWindowViewModel ViewModel { get; }
removed cast and access to UI thread only property DataContext.
Now I can see progress bar filling up.

You can't see any changes because your Main thread AKA UI Thread is busy Sleeping
and it does not have time to update your UI
Let Task handle your lengthy job and Main thread for Updating UI
Wrap your code inside Task and you can see your progress bar progressing.
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
_viewModel.Download();
});
//_viewModel.Download(); //this will run on UI Thread
}

Related

WPF: RadioButton do not raise event when its load

MainWindow.xaml
<Grid>
<StackPanel>
<RadioButton Content="A" GroupName="ASD"
Command="{Binding ButtonCommand}"
IsChecked="True"/>
<RadioButton Content="B" GroupName="ASD"
Command="{Binding ButtonCommand}"/>
<RadioButton Content="C" GroupName="ASD"
Command="{Binding ButtonCommand}"/>
</StackPanel>
</Grid>
MainViewModel.cs
public class MainViewModel : BaseViewModel
{
private ICommand _buttonCommand;
public ICommand ButtonCommand { get { return (_buttonCommand ?? new BaseCommand(MyAction)); } }
public void MyAction()
{
Debug.WriteLine("clicked");
}
}
BaseCommand.cs
public class BaseCommand : ICommand
{
private Action _action;
public BaseCommand(Action action)
{
_action = action;
}
public event EventHandler? CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
_action();
}
}
When I click the button, call MyAction() and print "clicked", It works right.
When the window is loaded, I want its raise event click, call to MyAction() and print "clicked", so I set the first radiobutton IsChecked property = true, but it do not raise event. Why? And how to solve it, thanks.
Events and commands are not the same. RadioButoon has many events to reflect input events or state changes, but can invoke only a single command.
To know the available events visit the API class reference of the class e.g. by moving the cursor on the class name and then pressing "F1": RadioButton.
In your case you must handle the RadioButton.Checked event. Note that this event will be raised during the initialization routine of the RadioButton in order to fetch the local IsChecked value. If you really need wait until the button is loaded in framework terms (which means layout calculations and rendering have completed), you can defer the Checked event using the Dispatcher with a DispatcherPriority.Loaded or directly handle the RadioButton.Loaded event (instead of the Checked event alongside using the Dispatcher):
<RadioButton Content="A"
GroupName="ASD"
Checked="RadioButton_Checked"
Loaded="RadioButton_Loaded"
IsChecked="True" />
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
RadioButton radioButton = sender as RadioButton;
// Handle IsChecked changed
radionButton.Command.Execute(default);
// Alternatively, wait until the control has loaded (in case you need to reference layout related properties)
this.Dispatcher.InvokeAsync(() =>
{
radionButton.Command.Execute(default);
}, DispatcherPriority.Loaded);
// If this is a one-time operation, unregister the event handler
radioButton.Checked -= RadioButton_Checked;
}
As mentioned before, if you need the RadioButton to be completely loaded, consider to handle the RadioButton.Loaded event instead of using the Dispatcher.

Async progress bar in WPF using binding

I have a problem with visualizing the MVVM WPF progress bar. My methods for the main script runs but the progress bar isn't updating at all. I could use tips on getting in the right direction. In my previous codes, I was given tips to use Progress<T> (by #Jonathan Willcock) but I couldn't implement it successfully.
(Please note that this post is not a repeated question because last time, I used button click but I want it to purely be run on data binding)
Question
How do I asynchronously bind the progress bar to the method which is called in my view models? I am using Delegate command to call my methods and I do not want to use the button click event.
What I have in the view - XAML
I have two main functions here, the progress bar and the button. The button starts the method calling successfully but the progress bar doesn't load.
<ProgressBar Name="pbStatus"
Minimum="0"
Value="{Binding PrgBarVal, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
Maximum="100"
Height="20"/>
<Button x:Name = "ApplyButton" Margin="0 1 0 1" Content ="Run software" Command="{Binding RunCalcBtn, Mode=TwoWay}"/>
What I have in the XAML.cs
What I know from the background worker method is that it tries to run it async. I have an issue here, I added a breakpoint here to check if the code runs through the 'Window_ContentRendered' method but it doesn't.
public partial class UserInterface: UserControl
{
public UserInterface()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork; //Do I need a worker.ProgressChanged here?
worker.RunWorkerAsync();
}
thenamespace.ViewModel.SelectedViewModel get = new thenamespace.ViewModel.SelectedViewModel ();
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = Convert.ToInt32(get.PrgBarVal); i < 100; i++)
{
(sender as BackgroundWorker).ReportProgress(i);
}
}
}
What I have in the View Model
In the view model, I am using delegate command to call my data binding methods.
The public SelectedViewModel() is where the Run() method is called. In the Run() method, the functions in there runs correctly and updates the value of PrgBarVal correctly. However, this doesn't update the progress bar at all.
public class SelectedViewModel : ModelView, INotifyPropertyChanged
{
public ModelView modelView { get; set; }
private DelegateCommand _runCalcBtn;
public DelegateCommand RunCalcBtn
{
get { return _runCalcBtn; }
set
{
_runCalcBtn = value;
SetPropertyChanged("RunCalcBtn"); //Same as RaisedPropertyChanged
}
}
public SelectedViewModel()
{
modelView = new ModelView();
RunCalcBtn = new DelegateCommand(Run);
}
private int _prgBarVal;
public int PrgBarVal
{
get { return _prgBarVal; }
set
{
_prgBarVal = value;
OnPropertyChanged("PrgBarVal"); //Same as RaisedPropertyChanged
}
}
//Main Function
private async void Run()
{
MethodToWork(); //Function that calls other methods to run
}
Thank you very much for your help!
Here is the ViewModel (used NuGet ReactiveUI.WPF)
public class MainViewModel : ReactiveObject
{
private int _workProgress;
public MainViewModel()
{
IProgress<int> progress = new Progress<int>( e => WorkProgress = e );
StartWork = ReactiveCommand.CreateFromTask( () => ExecuteStartWorkAsync( progress ) );
}
private async Task ExecuteStartWorkAsync( IProgress<int> progress )
{
progress.Report( 0 );
for ( int i = 0; i < 1000; i++ )
{
await Task.Delay( 10 ).ConfigureAwait( false );
progress.Report( (int)Math.Floor( i / 1000.0 * 100.0 ) );
}
}
public int WorkProgress { get => _workProgress; private set => this.RaiseAndSetIfChanged( ref _workProgress, value ); }
public ICommand StartWork { get; }
}
and the View
<Window x:Class="WpfApp4.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"
xmlns:local="clr-namespace:WpfApp4"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Start work" Command="{Binding StartWork}" Width="80"/>
<ProgressBar Value="{Binding WorkProgress,Mode=OneWay}" Width="120"/>
</StackPanel>
</Grid>
</Window>

WPF MVVM cancel Window.Closing event

In WPF application together with MVVMLight Toolkit, I would like to see your opinion, what is the best way to implement if I need to Cancel the Window Close event.
In Window.Closing event I can set the e.Cancel = true, which prevents closing the form. To identify if the Close is allowed, or should be prevented is in the ViewModel context.
One solution could be if I define an Application variable, and I can query this in the normal event handler in view code behind?
thanks
With MVVM Light you got EventToCommand:
So you could in xaml wire up the closing event to the VM.
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<command:EventToCommand Command="{Binding ClosingCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in the VM:
public RelayCommand<CancelEventArgs> ClosingCommand { get; private set; }
ctor() {
ClosingCommand = new RelayCommand<CancelEventArgs>(args => args.Cancel = true);
}
If you do not want to pass CancelEventArgs to the VM:
You could always take the similar approach with a Behavior and just use a simple bool from the VM(bind this bool to the Behavior) to indicate the closing event should be cancelled.
Update:
Download Link for following example
To do this with a Behavior you could just have a Behavior such as:
internal class CancelCloseWindowBehavior : Behavior<Window> {
public static readonly DependencyProperty CancelCloseProperty =
DependencyProperty.Register("CancelClose", typeof(bool),
typeof(CancelCloseWindowBehavior), new FrameworkPropertyMetadata(false));
public bool CancelClose {
get { return (bool) GetValue(CancelCloseProperty); }
set { SetValue(CancelCloseProperty, value); }
}
protected override void OnAttached() {
AssociatedObject.Closing += (sender, args) => args.Cancel = CancelClose;
}
}
Now in xaml:
<i:Interaction.Behaviors>
<local:CancelCloseWindowBehavior CancelClose="{Binding CancelClose}" />
</i:Interaction.Behaviors>
Where CancelClose is a bool property from the VM which indicates if the Closing event should be cancelled or not. In the attached example I have a Button to toggle this bool from the VM that should let you test the Behavior
You could to control this using Messages, for instance:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<CloseApplicationMessage>(this, m => Close());
Loaded += MainWindowLoaded;
Closing += MainWindowClosing;
}
private void MainWindowClosing(object sender, CancelEventArgs e)
{
//Ask for saving
var closingMessage = new ClosingApplicationMessage();
Messenger.Default.Send(closingMessage);
if (closingMessage.Cancel)
e.Cancel = true;
}
...
The mvvm message:
public class ClosingApplicationMessage
{
public bool Cancel { get; set; }
}
In this way, in any place you are listening to the ClosingApplicationMessage, you can control when the application is going to close, and may to cancel it.
Hope this helps...

When does the ui detach from commands?

I'm really scratching my head with this one. I have a mainwindow which opens a dialog. After the dialog closes, the CanExecute method on commands bound in the dialog are still executing. This is causing some serious problems in my application.
Example:
MainWindow has a button with a click handler. This is the click event handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
DialogWindow window = new DialogWindow();
window.ShowDialog();
}
In the dialog I bind an items control to a static resource in the dialog window, and each item in the list has a command:
<Window.Resources>
<Collections:ArrayList x:Key="itemsSource">
<local:ItemViewModel Description="A"></local:ItemViewModel>
<local:ItemViewModel Description="B"></local:ItemViewModel>
<local:ItemViewModel Description="C"></local:ItemViewModel>
</Collections:ArrayList>
<DataTemplate DataType="{x:Type local:ItemViewModel}">
<Button Grid.Column="1" Command="{Binding Path=CommandClickMe}" Content="{Binding Path=Description}" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
</Button>
</DataTemplate>
</Window.Resources>
<Grid>
<ToolBar ItemsSource="{StaticResource itemsSource}"></ToolBar>
</Grid>
This is the viewmodel:
public class ItemViewModel
{
private RelayWpfCommand<object> _commandClickMe;
public RelayWpfCommand<object> CommandClickMe
{
get
{
if (_commandClickMe == null)
_commandClickMe = new RelayWpfCommand<object>(obj => System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());
return _commandClickMe;
}
}
private bool CanClickMe()
{
return true;
}
public string Description { get; set; }
And this is the DelegateCommand implementation:
public class RelayWpfCommand<T> : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public RelayWpfCommand(Action<T> execute, Predicate<T> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
/// <summary>
/// Forces a notification that the CanExecute state has changed
/// </summary>
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
public bool CanExecute(T parameter)
{
return _canExecute(parameter);
}
public void Execute(T parameter)
{
_execute(parameter);
}
bool ICommand.CanExecute(object parameter)
{
if (!IsParameterValidType(parameter))
return false;
return CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
if (!IsParameterValidType(parameter))
throw new ArgumentException(string.Format("Parameter must be of type {0}", typeof(T)));
Execute((T)parameter);
}
private static bool IsParameterValidType(object parameter)
{
if (parameter != null && !typeof(T).IsAssignableFrom(parameter.GetType()))
return false;
return true;
}
}
Now, If I close the dialog window and set a breakpoint in the CanExecute (I'm using Prism DelegateCommand with weak event subscription) method on the viewmodel, I notice that it triggers although the dialog has been closed. Why on earth is the binding between the button in the dialog and the command on the ViewModel still alive?
And I am checking if its being executed by closing the window and at a later time setting a breakpoint in the "CanClickMe" method in the viewmodel. It will get executed for a while, then suddenly stop (probably due to GC). This non-determenistic behaviour is causing problems because in the real application the viewmodel might already bee disposed.
You may use the WeakEvent Pattern to mitigate this problem. Please refer to the following Stackoverflow question: Is Josh Smith's implementation of the RelayCommand flawed?
I've seen this catch many times in different projects, I'm not sure whether this creepy bug lurks in your app too, but it's worth checking.
There is a known memory leak issue in WPF 3.5 (including SP1), basically you can encounter it if you are binding to something that isn’t a DependencyProperty or doesn’t implement INotifyPropertyChanged. And this is exactly what your code is about.
Just implement INotifyPropertyChanged on ItemViewModel and see how it goes. Hope this helps.
You could clear the CommandBindings Collection of your window, when it closes.
rather than having your command as a property, could you try the following:
public ICommand CommandClickMe
{
get
{
return new RelayWpfCommand<object>((obj)=>System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());
}
}

WPF CommandParameter Binding Problem

I'm having some trouble understanding how command parameter binding works.
When I create an instance of the widget class before the call to InitializeComponent it seems to work fine. Modifications to the parameter(Widget) in the ExecuteCommand function will be "applied" to _widget. This is the behavior I expected.
If the instance of _widget is created after InitializeComponent, I get null reference exceptions for e.Parameter in the ExecuteCommand function.
Why is this? How do I make this work with MVP pattern, where the bound object may get created after the view is created?
public partial class WidgetView : Window
{
RoutedCommand _doSomethingCommand = new RoutedCommand();
Widget _widget;
public WidgetView()
{
_widget = new Widget();
InitializeComponent();
this.CommandBindings.Add(new CommandBinding(DoSomethingCommand, ExecuteCommand, CanExecuteCommand));
}
public Widget TestWidget
{
get { return _widget; }
set { _widget = value; }
}
public RoutedCommand DoSomethingCommand
{
get { return _doSomethingCommand; }
}
private static void CanExecuteCommand(object sender, CanExecuteRoutedEventArgs e)
{
if (e.Parameter == null)
e.CanExecute = true;
else
{
e.CanExecute = ((Widget)e.Parameter).Count < 2;
}
}
private static void ExecuteCommand(object sender, ExecutedRoutedEventArgs e)
{
((Widget)e.Parameter).DoSomething();
}
}
<Window x:Class="CommandParameterTest.WidgetView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WidgetView" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<Button Name="_Button" Command="{Binding DoSomethingCommand}"
CommandParameter="{Binding TestWidget}">Do Something</Button>
</StackPanel>
</Window>
public class Widget
{
public int Count = 0;
public void DoSomething()
{
Count++;
}
}
InitializeCompenent processes the xaml associated with the file. It is at this point in time that the CommandParameter binding is first processed. If you initialize your field before InitializeCompenent then your property will not be null. If you create it after then it is null.
If you want to create the widget after InitializeCompenent then you will need to use a dependency property. The dependency proeprty will raise a notification that will cause the CommandParameter to be updated and thus it will not be null.
Here is a sample of how to make TestWidget a dependency property.
public static readonly DependencyProperty TestWidgetProperty =
DependencyProperty.Register("TestWidget", typeof(Widget), typeof(Window1), new UIPropertyMetadata(null));
public Widget TestWidget
{
get { return (Widget) GetValue(TestWidgetProperty); }
set { SetValue(TestWidgetProperty, value); }
}
Even with the dependency property, you still need to call CommandManager.InvalidateRequerySuggested to force the CanExecute of the Command being evaluated.

Resources