The main idea:
I want to pass an Action
from a ViewModel
to a CustomControl
, such that whenever these Action
s are invoked from the ViewModel
, it automatically invokes a method within the CustomControl
.
The detailed idea:
I want to be able to show some messages on command. So I have these Action
s on my MainViewModel
that are invoked whenever I need them to show.
public class MainViewModel
{
// assuming these get and set methods are basically normal gets and sets
public Action ShowErrorMsg
{
get => GetValue<Action>();
set => SetValue(value);
}
public Action ShowSuccessMsg
{
get => GetValue<Action>();
set => SetValue(value);
}
public void SendMessage()
{
// some send message logic here
// ...
// invoke the actions based on result
if (sendSuccess)
{
ShowSuccessMsg?.Invoke();
}
else ShowErrorMsg?.Invoke();
}
}
I have a CustomLabel
control class that extends from Label
, and it sits in the MainView.xaml
like this, and I want it to pass in said Action
s:
<controls:CustomLabel Text="Error!" TextColor="Red"
ShowMessageAction="{Binding ShowErrorMsg}" />
<controls:CustomLabel Text="Success!" TextColor="Green"
ShowMessageAction="{Binding ShowSuccessMsg}" />
In the CustomLabel
class, a BindableProperty
will be assigned with any arbitrary Action
and I want it to be invoked whenever the Action
in MainViewModel
is invoked. From there, an animation is fired, and the text fades out in 8 seconds.
public class CustomLabel : Label
{
public static readonly BindableProperty ShowMessageActionProperty =
BindableProperty.Create(nameof(ShowMessageAction),
typeof(Action),
typeof(CustomLabel),
propertyChanged: OnShowMessageChanged);
public Action ShowMessageAction
{
get => (Action)GetValue(ShowMessageActionProperty);
set => SetValue(ShowMessageActionProperty, value);
}
// this should be fired when there is a new Action binded
public static void OnShowMessageChanged(BindableObject sender, object oldValue, object newValue)
{
var thisLabel = sender as CustomLabel;
var action = newValue as Action;
// subscribe the animation method to the Action that this class is bound to
action += thisLabel.ShowMessageAnimation;
}
// animation is fired when action is called, fades out in 8 seconds
private void ShowMessageAnimation()
{
this.IsVisible = true;
Timer timer = new Timer(8000);
timer.Elapsed += (_, _) =>
{
Application.Current.Dispatcher.Dispatch(async () =>
{
await this.FadeTo(0, 1000);
this.IsVisible = false;
this.Opacity = 1;
});
};
timer.Start();
}
}
However, unless I explicitly specify the Action
from within CustomLabel
, the Action
from MainViewModel
and ShowMessageAction
don’t sync as it should (sometimes the propertyChanged
isn’t even fired at all). Is there a fundamental gap in my knowledge, or is there a workaround/ a better way to approach this that I am completely missing?
What I have tried:
- I have tried assigning
ShowMessageAction
withMainViewModel.ShowMessage
in the constructor and that works fine, but does not allow me to be flexible on whichAction
I want to pass in. - I have tried subscribing within the
Set
method inShowMessageAction
property, manually subscribing fromMainView.xaml.cs
, to no avail.
8