Asynchronous UI Pattern on UWA - wpf

I am following this great article from Stephen about asynchronous UI, but unfortunately this does not apply to Universal Windows Apps, since CommandManager class is not available.
How can I workaround this limitation?
This is the base type for Async Commands:
public abstract class AsyncCommandBase : IAsyncCommand
{
public abstract bool CanExecute(object parameter);
public abstract Task ExecuteAsync(object parameter);
public async void Execute(object parameter)
{
await ExecuteAsync(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
protected void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
}

As pointed in the comments, this "pattern" shouldn't be used because of it's impact on performance when you have many commands registered.
Any call to CommandManager.InvalidateRequerySuggested() will force WPF to validate every single registered command and it's CanExecute(...) methods.
Instead, just declare the event and only let WPF register to it
public abstract class AsyncCommandBase : IAsyncCommand
{
...
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
// C# 6.0, otherwise assign the handler to variable and do null check
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
...
}
Whenever you want to invalidate the command you still just use command.RaiseCanExecuteChanged();. If you want to trigger revalidation of multiple commands, you need to manage it yourself (i.e. creating a CompoundCommand or something similar where you register your commands to groups).

Related

Drag Drop for Files AND also Folders

So I looked at this link before:
http://blogs.msdn.com/b/delay/archive/2009/10/26/creating-something-from-nothing-developer-friendly-virtual-file-implementation-for-net.aspx
The class works flawlessly for Files, but it doesn't support directory's etc, does anyone have any idea how I can change the class to support it, I"m no pinvoke whiz. I've tried a million different things, overriding some code to do File Copy and Directory creation of my drop source into the TEMP directory and attempting to trigger a FileDrop, but this locks up the app entirely.
This leads me to believe that there must be a better way to enable directory structure creation as well.
The main part of the Drag and Drop operation is the DragDrop.DoDragDrop method. From the DragDrop.DoDragDrop Method page on MSDN:
public static DragDropEffects DoDragDrop(
DependencyObject dragSource,
Object data,
DragDropEffects allowedEffects
)
Of particular interest is the data parameter:
A data object that contains the data being dragged.
Notice how this parameter is of type Object, so it's completely up to you as to what object you use in the operation. Now I'm not sure what code you found from the page that you linked to, but if I were trying to drag and drop files and folders, I wouldn't need special classes to do it for me.
The simplest way to do that is to just pass the file and/or folder paths instead of the actual data. The control that the data is dropped on can access the data using the file paths just as easily as the drag source. You should be able to locate the DragDrop.DoDragDrop method from your code and easily adapt that code.
If you want to do Drag and Drop operations in the correct way, then I'd recommend that you take a look at the Drag and Drop Overview page on MSDN. It fully explains what to do and provides several code examples.
To implement drag and drop in MVVM without much experience in WPF, you can refer to 5 steps; I will outline these...
Step 1: Attached Behaviours
Add a new class to your project called Behaviours. It should look like this...
public static class Behaviours
{
#region DandBehaviour
public static readonly DependencyProperty DandBehaviourProperty =
DependencyProperty.RegisterAttached("DandBehaviour", typeof(ICommand), typeof(Behaviours),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.None,
OnDandBehaviourChanged));
public static ICommand GetDandBehaviour(DependencyObject d)
{
return (ICommand)d.GetValue(DandBehaviourProperty);
}
public static void SetDandBehaviour(DependencyObject d, ICommand value)
{
d.SetValue(DandBehaviourProperty, value);
}
private static void OnDandBehaviourChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Grid g = d as Grid;
if (g != null)
{
g.Drop += (s, a) =>
{
ICommand iCommand = GetDandBehaviour(d);
if (iCommand != null)
{
if (iCommand.CanExecute(a.Data))
{
iCommand.Execute(a.Data);
}
}
};
}
else
{
throw new ApplicationException("Non grid");
}
}
#endregion
}
This class implements an attached dependency property which can be reached from both your Xaml and your View Model. It hooks the "Drop" event and invokes a command on it.
Step 2: Instrument the Xaml
In this step you need to add the name space to the Xaml so that it can find the behaviours class in Step 1. It looks something like this...
xmlns:b="clr-namespace:DdMvvm"
This statement assigns the alias 'b' to the behaviours. Then you tell the WPF root window to accept drops...
AllowDrop="true"
Then you can add the behaviour to your logical tree thusly...
<Grid AllowDrop="True" b:Behaviours.DandBehaviour="{Binding DandCommand}">
<DockPanel Background="Bisque" AllowDrop="True"/>
</Grid>
Step 3: Add command support
Download Josh Smith's 'Relay Command' from http://msdn.microsoft.com/en-us/magazine/dd419663.aspx For completeness purposes, it is given here as...
public class RelayCommand : ICommand
{ //http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
}
Step 4: Write the View Model
A View Model looks like this...
public class ViewModel : INotifyPropertyChanged
{
public ICommand DandCommand { get; set; }
public ViewModel()
{
DandCommand = new RelayCommand(ExecuteDandCommand, CanExecuteDandCommand);
}
private void ExecuteDandCommand(object obj)
{
if (obj != null)
{
IDataObject ido = obj as IDataObject;
if (ido != null)
{
var fileDrop = ido.GetData(DataFormats.FileDrop, true);
var filesOrDirectories = fileDrop as String[];
if (filesOrDirectories != null && filesOrDirectories.Length > 0)
{
foreach (string fullPath in filesOrDirectories)
{
if (Directory.Exists(fullPath))
{
Console.WriteLine(#"{0} is a directory", fullPath);
}
else if (File.Exists(fullPath))
{
Console.WriteLine(#"{0} is a file", fullPath);
}
else
{
Console.WriteLine(#"{0} is not a file and not a directory", fullPath);
}
}
}
}
}
}
private bool CanExecuteDandCommand(object obj)
{
return true;
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
This VM implements a command (called DandCommand) which will be fired by the attached behaviour (remember Step 1?). The 'juicy part' of the VM is the bit which dereferences the drag-and-drop payload. In this particular VM, the code dereferences the data and finds out if it is a file or a directory. It then prints a diagnostic to the console. You can change this part to load images, or internet links, or what-ever can be dropped.
Step 5: Wiring the data context
This is done by different developers in different ways (for industrial apps, lots of people like to use Prism and Unity, but that's ott for a simple how-to like this post). The most straight-forward approach is to change your View to look like this...
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
This code sets the window's data context to your VM so that the binding can take place.
Those steps give you a good starting point for MVVM and drag-and-drop and getting the whole thing to work. The big payback in using MVVM for these things is the clean separation and compartmentalisation that you get. I.e., the VM can be unit-tested without having to instantiate the WPF binding engine.

MVVM RelayCommand CanExecute

I'm implementing an RelayCommand with an execute and an canExecute part. The RelayCommand works when it is without the canExecute part, however when I add the canExecute part, the command locks the button. The RelayCommand only checks whether or not the button can be executed as long as the CanExecute part is true. Once the canExecute part becomes false, the button can no longer be clicked, even if it is supposed to. How do I make sure that every time I click on the button it controls whether or not it can be executed, and doesn't lock it forever, once it cannot be executed?
RedoCommand = new RelayCommand(undoRedoController.Redo,undoRedoController.CanRedo);
public bool CanRedo()
{
redoStack.Count();
redoStack.Any();
return redoStack.Any();
}
public void Redo()
{
if (redoStack.Count() <= 0) throw new InvalidOperationException();
IUndoRedoCommand command = redoStack.Pop();
undoStack.Push(command);
command.Execute();
}
public class UndoRedoController
{
private static UndoRedoController controller = new UndoRedoController();
private readonly Stack<IUndoRedoCommand> undoStack = new Stack<IUndoRedoCommand>();
private readonly Stack<IUndoRedoCommand> redoStack = new Stack<IUndoRedoCommand>();
private UndoRedoController() { }
public static UndoRedoController GetInstance() { return controller; }
There has been a hiatus with MVVMLight due to the fact that after the .NET 4.5 update the CommandManager no longer fires the can execute check. This has since been solved. Instead of including the GalaSoft.MvvmLight.Command namespace you should use the GalaSoft.MvvmLight.CommandWpf namespace. The RelayCommand defined in that namespace is still checking the CanExecute function that you pass to the command.
Took me about a day to find out what the hell was going wrong in my application. I hope this will help some of you.
Here is a blog post by the developer explanining why this is necessary.
For some reason you have to do the following:
public RelayCommand RedoCommand{
get;
set;
}
you can also put private before set optional, depending on your access level. Then you do
RedoCommand = new RelayCommand(() => undoRedoController.Redo(), () => undoRedoController.CanRedo());
Now your able to call RedoCommand.RaiseCanExecuteChanged();
And everything works.
If you are using an unpatched .net 4.5. Microsoft broke the .CanExecute event.
http://connect.microsoft.com/VisualStudio/feedback/details/753666/net-4-0-application-commands-canexecute-not-updating-in-4-5
If you are using the RelayCommand from http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030 and are not raising the CanExecuteChanged event when redoStack changes.
(Answering from a Silverlight perspective so assuming this will help you.)
Are you doing a RedoCommand.RaiseCanExecuteChanged() anywhere? Once whatever condition you are monitoring changes, you'll need to raise this command manually.
EDIT
Since you are using MVVM Light.. Heres sample code:
RedoCommand = new RelayCommand(undoRedoController.Redo,undoRedoController.CanRedo);
public bool CanRedo()
{
redoStack.Count();
redoStack.Any();
return redoStack.Any();
}
public void Redo()
{
if (redoStack.Count() <= 0) throw new InvalidOperationException();
IUndoRedoCommand command = redoStack.Pop();
undoStack.Push(command);
command.Execute();
// At this point, your stacks have changed; that is, the stacks
// may or may not contain items. Thus, raise the commands CanExecute part
// which will in turn enable/disable the commands based on the functions
// return value
RedoCommand.RaiseCanExecuteChanged();
// assuming you could possibly have an UndoCommand somewhere
UndoCommand.RaiseCanExecuteChanged();
}

Custom UpdateSourceTrigger with delay?

I'm looking to create a custom version of UpdateSourceTrigger that I can use with my binding. I don't know if this is possible, or if instead, I'd need to just create my own binding class. What I'm looking for is, instead of LostFocus or PropertyChanged, have something where it will update the source after some specified time limit.
I found this, but I don't know if there's a better way (one of the comments mentioned some memory leaks with the implementation).
Any ideas?
I just noticed that WPF 4.5 has a Delay Property, see for more information this link
http://www.shujaat.net/2011/12/wpf-45-developers-preview-delay-binding.html
I wouldn't bother doing this at the binding level, but would instead manifest it in my view model. When the property changes, restart a DispatcherTimer. When the timer expires, kick off your logic. It's that simple.
This can be easily implemented using Reactive Extensions's Throttle() method in conjunction with an observable property.
public class ObservablePropertyBacking<T> : IObservable<T>
{
private readonly Subject<T> _innerObservable = new Subject<T>();
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
_innerObservable.OnNext(value);
}
}
#region IObservable<T> Members
public IDisposable Subscribe(IObserver<T> observer)
{
return _innerObservable
.DistinctUntilChanged()
.AsObservable()
.Subscribe(observer);
}
#endregion
}
Used like this:
// wire query observable
var queryActual = new ObservablePropertyBacking<string>();
queryActual.Throttle(TimeSpan.FromMilliseconds(300)).Subscribe(DoSearch);
Implement property:
string query;
public string Query
{
get { return query; }
set
{
queryActual.Value = value;
}
}

Routed Events in WPF - using an Action delegate

I'm developing a user control, and wish to use a routed event. I notice that there are two delegates provided - RoutedEventHandler, and RoutedPropertyChangedEventHandler. The first that doesn't pass along any information, and the second that takes the old and new values of a property change. However, I need to pass just a single piece of information, so I want the equivalent of an Action delegate. Is there anything provided? Can I use an Action delegate?
Create a subclass of RoutedEventArgs to hold your additional data, and use EventHandler<T> with your args class. This will be convertible to RoutedEventHandler and the additional data will be available in your handlers.
You could create a generic RoutedEventArgs class that holds a single parameter of any type, but creating a new class usually makes the code easier to read and easier to modify to include more parameters in the future.
public class FooEventArgs
: RoutedEventArgs
{
// Declare additional data to pass here
public string Data { get; set; }
}
public class FooControl
: UserControl
{
public static readonly RoutedEvent FooEvent =
EventManager.RegisterRoutedEvent("Foo", RoutingStrategy.Bubble,
typeof(EventHandler<FooEventArgs>), typeof(FooControl));
public event EventHandler<FooEventArgs> Foo
{
add { AddHandler(FooEvent, value); }
remove { RemoveHandler(FooEvent, value); }
}
protected void OnFoo()
{
base.RaiseEvent(new FooEventArgs()
{
RoutedEvent = FooEvent,
// Supply the data here
Data = "data",
});
}
}

Changetracking and concurrency - can this fail?

I've been messing around with Expressions - and I may have gone beyond my capabilities - but here goes... I've implemented 'type-safe' INotifyPropertyChanged implementation (an example is here), but gone a bit farther and included changetracking:
public abstract BaseViewModel<TEntity>:INotifyPropertyChanged
{
private readonly IBaseChangeTracker<TEntity> _changeTracker;
protected void OnPropertyChanged<T>(Expression<Func<T>> property, T value)
{
_changeTracker.AddChange(property, value);
OnPropertyChanged(property);
}
protected virtual void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(property.GetMemberInfo().Name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public abstract class BaseChangeTracker<TEntity>:IBaseChangeTracker<TEntity>
{
private readonly IDictionary<Expression, object> _changes = new Dictionary<Expression, object>();
public void AddChange<T>(Expression<Func<T>> expression, T newValue)
{
_changes.Add(expression, newValue);
}
public void ApplyChanges(TEntity entity)
{
foreach (var change in _changes)
{
var property = typeof(TEntity).GetProperty(change.Key.GetMemberInfo().Name);
property.SetValue(entity, change.Value, null);
}
}
public virtual void CopyCurrentState(TEntity entity)
{
_changes.Clear();
}
public virtual void ResetEntity(TEntity entity)
{
_changes.Clear();
}
public bool HasUnsavedChanges
{
get { return _changes.Any(); }
}
}
This may seem a bit excessive - each entity will have it it's own ChangeTracker keeping the original state of the entity when loaded and can reset it back to these, but the idea is that if there is a concurrency conflict when it tries to save the updated entity, I reload the entity from the database and run it through .ApplyChanges and try to save it again. This will remove about 95% of my concurrency problems... if it works. My tests show that for limited entities it works, but that is with simple property-changes.
Known issue:
I have yet to find an elegant way of handling collections.
What else am I missing - or are there obvious flaws in my design?
I realize that this is not a direct answer to your question, but you might consider using the Command Pattern to implement an undo/redo stack.
Encapsulating changes in commands is a very tidy way to cycle/re-cycle through changes, with the added benefits of (1) a nice feature that adds value to the application, (2) you can wrap many actions in any given command, like raising event change notifications for databinding support in both the do and undo directions.
Additionally, managing collection changes is no more or less challenging than simple property updates.
Specific to the code you posted, the OnPropertyChanged implementation will never raise the PropertyChanged event because you call return after the if() statement and then again in bare brackets (these do not correspond to the if condition).
if (PropertyChanged == null) return; // this returns based on if
{
return; // this returns no matter what
}
Additionally, it seems that the user won't ever see any changes in the UI. The values aren't updated until ApplyChanges is called, and when it is there is no PropertyChanged event. (I might not be following your code correctly, but just looking over it this seems to be the case).

Resources