I'm using MVVM pattern to build UWP app. I have implemented ICommand interface as mentioned in book: "Microsoft Visual C# 2013 - Step-by-step".
ICommand implementation:
public class Command : ICommand
{
private Action _methodToExecute;
private Func<bool> _methodCanExecute;
public Command(Action methodToExecute) : this(methodToExecute, null)
{
}
public Command(Action methodToExecute, Func<bool> methodCanExecute)
{
_methodToExecute = methodToExecute;
_methodCanExecute = methodCanExecute;
var dt=new DispatcherTimer();
dt.Tick += (s, e) => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
dt.Interval = new TimeSpan(0, 0, 1);
dt.Start();
}
public bool CanExecute(object parameter) => _methodCanExecute == null ? true : _methodCanExecute();
public void Execute(object parameter) => _methodToExecute();
public event EventHandler CanExecuteChanged;
}
App crashes with COM exception after every 3-4 mins of running.
System.Runtime.InteropServices.COMException was unhandled by user code
ErrorCode=-2147467259
HResult=-2147467259
Message=Error HRESULT E_FAIL has been returned from a call to a COM component.
Source=mscorlib
StackTrace:
at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
at System.Runtime.InteropServices.WindowsRuntime.ICommandAdapterHelpers.<>c__DisplayClass2.<CreateWrapperHandler>b__3(Object sender, EventArgs e)
at FavQuotesMain.ViewModels.Command.<.ctor>b__3_0(Object s, Object e)
InnerException:
This exception didn't occur while building Win 8.1 apps.
Please give suggestions to remove this exception.
dt.Tick += (s, e) => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
That's most likely what's causing your issue. I'm not sure why you're spamming your CanExecuteChanged, but maybe you want to re-think your execution model.
Also we don't know what methods your delegate is calling. So there could be a plethora of reasons it's failing.
I guess your problem is the timer.
I always implement the ICommand like:
public class Command : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public Command(Action<object> execute, Predicate<object> canExecute = null)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public void Execute(object parameter)
{
execute(parameter);
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
#Water Solution is to manually raise CanExecuteChanged whenever we want View to know about Control's changed status.
Steps:
Wrap CanExecuteChanged into a method like OnCanExecuteChanged ()
Call this method form relevant ViewModel Property Setters.
Related
I have two methods that do almost the two things:
public static void ShowThing()
{
// code..
}
and
public static bool TryShowThing()
{
if(condition)
{
// same code above..
return true;
}
return false;
}
At the moment I'm binding a button's Command to the void method and it does what it should.
Problem is that now I'm cleaning up the code and to avoid coupling I wanted to bind the button to the bool method and that won't work.
Is Command={Binding BooleandReturningMedhod} even allowed in xaml?
Apparently nobody on the internet has ever had this problem before so I think I'm missing something here...
You cannot bind directly to a method.
What i think what you really wanna achieve is something like this
Code:
ShowThingCommand { get; } = new RelayCommand((o) => ShowThing(),(o) => condition)
RelayCommand:
public class RelayCommand : ICommand
{
private Action<object> execute;
private 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)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
XAML:
<Button Command={Binding ShowThingCommand } />
Important part is the CanExecute method, when it returns false your Button gets disabled
The current step of learning MVVM is RelayCommand for me.
So i came up with this RelayCommand class:
Relay Command class
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute ?? (x => true);
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public void Refresh()
{
CommandManager.InvalidateRequerySuggested();
}
}
View Code-Behind
To test if CanExecute is true or false, I created a Click Event which is calling the Command if CanExecute == true or Show an Error Message when CanExecute == false.
if (sender is Button button)
{
if (_viewModel.MyCommand.CanExecute(button.Tag)) // Also testet to set this parameter `null`
_viewModel.MyCommand.Execute(button.Tag);
else
ErrorMessage.Error("CanExecute = false");
}
ViewModel
In my ViewModel I created the Command and added a Thread.Sleep() to have time that canExecute can show me the ErrorMessage from the Code-Behind.
public ICommand MyCommand { get; set; }
public ViewModel()
{
MyCommand = new RelayCommand(MyCommandMethod);
}
public async void MyCommandMethod(object obj)
{
await Task.Run(() =>
{
Thread.Sleep(5000);
ErrorMessage.Error(obj as string);
});
}
The Problem now is, that if I click the Button 5 times for example, that MyCommandMetod() is used 5 times. So CanExecute will never change.
But why isn't it changing?
I understand RelayCommand as this:
1st - Button is clicked
2nd - canExecute = false (wait till process is finished)
3rd - canExecute = true
4th - Button can be executed again.
So that u can't spam Button clicks and crash the application if for example someone use SpeedClicker and clicks 1.000.000 times a seconds or so.
You have to pass some can-execute-logic to the command when creating it:
public ViewModel()
{
MyCommand = new RelayCommand(MyCommandMethod, MyCanExecutePredicate);
}
private bool MyCanExecutePredicate( object commandParameter )
{
// TODO: decide whether or not MyCommandMethod is allowed to execute right now
}
Example: if you want to allow only one command execution at a time, you could come up with something along these lines:
public async void MyCommandMethod(object obj)
{
_myCanExecute = false;
MyCommand.Refresh();
await Task.Run(() =>
{
Thread.Sleep(5000);
ErrorMessage.Error(obj as string);
});
_myCanExecute = true;
MyCommand.Refresh();
}
private bool MyCanExecutePredicate( object commandParameter )
{
return _myCanExecute;
}
I've got a little help from another Thread here, which I not fully understood.
It's a UserControl which differentiates between several types of texts and makes them look like as one single textbox. Some of the types (i.e. Hyperlinks) are clickable.
To click on them I've got this code.
public class RelayCommand<T> : ICommand
{
readonly Action<T> _execute;
readonly Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public void RefreshCommand()
{
var cec = CanExecuteChanged;
if (cec != null)
cec(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
if (_canExecute == null) return true;
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
}
The problem is now, that I want to stop the Tap/Click event after clicking on the links. Usually to prevent the event from bubbling up I would do this e.Handled = True. But in this case I've got no TappEvent on the links.
The code- behind of the user control look like this:
private void Init()
{
//ComplexTextPresenterElement.Input = "This is where the Content string has to be....";
ComplexTextPresenterElement.OnHyperlinkCommand = new RelayCommand<object>(Execute);
}
private async void Execute(object o)
{
List<object> passingParameters = new List<object>();
//put here the code that can open browser
if (o is HyperLinkPart)
{
var obj = (HyperLinkPart)o;
await Windows.System.Launcher.LaunchUriAsync(new Uri(obj.RealUrl, UriKind.Absolute));
} [...]
Before the webbrowser opens I have to stop the TapEvent which gets called from the UI Element surrounding this UserControl.
I hope this is enough information. Let me know otherwise.
Cheers,
Ulpin
Have a bool canTapBrowser = false;
Change this
ComplexTextPresenterElement.OnHyperlinkCommand = new RelayCommand<object>(Execute);
To
ComplexTextPresenterElement.OnHyperlinkCommand = new RelayCommand<object>(Execute,()=> {return canTapBrowser});
In the webbrower navigation completed event do the following:
canTapBrowser = true;
OnHyperlinkCommand.RefreshCommand();
I have the following implementation of an RelayCommand in my viewModel:
RelayCommand _resetCounter;
private void ResetCounterExecute()
{
_data.ResetCounter();
}
private bool CanResetCounterExecute()
{
if (_data.Counter > 0)
{
return true;
}
else
{
return false;
}
}
public ICommand ResetCounter
{
get
{
if (_resetCounter == null)
{
_resetCounter = new RelayCommand(this.ResetCounterExecute,this.CanResetCounterExecute);
}
return _resetCounter;
}
}
By calling _data.ResetCounter(); in the ResetCounterExecute method i reset the counter value to 0 in my model.
And this is the implementation of my RealyCommand Class that i use based on samples.
internal class RelayCommand : ICommand
{
readonly Action _execute;
readonly Func<bool> _canExecute;
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute();
}
}
In XAML i bind the comman to a button:
<Button Name="btnResetCount" Content="Reset" Command="{Binding Path=CounterViewModel.ResetCounter}" Click="btnResetCount_Click">
My Problem is that the button just gets enabled once i click on any control in the UI. But what i need is that the button gets enabled once my _data.Counter > 0 applies. So from my research it seems that i need to implement CommandManager.InvalidateRequerySuggested(); or use the RelayCommand.RaiseCanExecuteChanged().
I would like to know if this two ways are the only ways to notify the UI to refresh the bindings.
Also i would like to ask how i would have to implement the RelayCommand.RaiseCanExecuteChanged() in my current case. Where and how should i raise it to ensure that the UI changes the button state if the condition is given.
Thanks in advance.
when using CommandManager.RequerySuggested you can force CommandManager to invoke RequerySuggested event by calling CommandManager.InvalidateRequerySuggested()
or you may perhaps implement RaiseCanExecuteChanged. this may be more reliable method to trigger the same.
example
internal class RelayCommand : ICommand
{
...
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
EventHandler handler = CanExecuteChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
...
}
and when you want to invalidate or _data.Counter changes, call
ResetCounter.RaiseCanExecuteChanged();
additionally you may also want to read How does CommandManager.RequerySuggested work?
I'm creating a WPF application with a borderless window. Applying the MVVVM pattern (with help of Caliburn.Micro) I do not have a code behind file but only a XAML file.
In several posts I found following solution:
XAML:
<Window
...
WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
Code behind:
private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
Now I'm searching a solution to define this completely in XAML.
Any idea?
The solution I will present is not really advised, but you can put your code behind right in your XAML file like this:
<Window
...
WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
<x:Code>
<![CDATA[
private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
]]>
</x:Code>
Check this Codeproject article for more information on this!
I think your best option is a behavior.
http://wpftutorial.net/Behaviors.html
You can download the Microsoft.Windows.Shell dll (Link. You can find another download options with google), which gives you a property of CaptionHeight that enables tou to drag the window from its top part (like a normal window).
You can use an EventCommandTrigger. Check these references:
http://www.danharman.net/2011/08/05/binding-wpf-events-to-mvvm-viewmodel-commands/
http://zamjad.wordpress.com/2011/06/07/convert-event-into-command-in-mvvm-model/
I know that I am a little late to the question, but this is what I have been using for sometime now and it works like a charm.
DashboardViewModel viewModel;
public DashboardView()
{
InitializeComponent();
viewModel = new DashboardViewModel();
viewModel.RequestClose += (s, e) => Application.Current.Dispatcher.Invoke(this.Close);
viewModel.RequestMinimize += (s, e) => Application.Current.Dispatcher.Invoke(() => { this.WindowState = WindowState.Minimized; });
DataContext = viewModel;
}
and something like this in your viewModel
#region Public Event Handlers
public event EventHandler<EventArgs> RequestClose;
public event EventHandler<EventArgs> RequestMinimize;
#endregion
Using the ICommand interface...
#region ICommand Members
public ICommand CloseCommand { get; private set; }
public ICommand MinimizeCommand { get; private set; }
#endregion
Configure the commands...
private void SetupCommands()
{
CloseCommand = new RelayCommand(CloseApplication);
MinimizeCommand = new RelayCommand(MinimizeApplication);
}
Here is the RelayCommand class.
public class RelayCommand : ICommand
{
#region Private Readonly Properties
private readonly Action<object> executeCommand;
private readonly Predicate<object> canExecute;
#endregion
#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");
this.executeCommand = execute;
this.canExecute = canExecute;
}
#endregion
#region Public 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)
{
executeCommand(parameter);
}
#endregion
}
And some example methods...
private void MinimizeApplication(object obj)
{
RequestMinimize(this, new EventArgs());
}
private void CloseApplication(object obj)
{
RequestClose(this, new EventArgs());
}
Hope this helps!