I made a custom control called ImageButton that allows me to set a different image for Up, Down, and Inactive states. It can also operate in "normal" mode or in "latched" mode.
It works fine except for one small piece ... the values I set in the XAML aren't applied immediately. It only uses the default values.
Here is the ImageButton.cs
public class ImageButton : Image
{
public enum State
{
Inactive,
Up,
Down
};
public static readonly BindableProperty CommandProperty =
BindableProperty.Create("Command", typeof(ICommand), typeof(ImageButton), null);
public static readonly BindableProperty SourceUpProperty =
BindableProperty.Create("SourceUp", typeof(string), typeof(ImageButton), null);
public static readonly BindableProperty SourceDownProperty =
BindableProperty.Create("SourceDown", typeof(string), typeof(ImageButton), null);
public static readonly BindableProperty SourceInactiveProperty =
BindableProperty.Create("SourceInactive", typeof(string), typeof(ImageButton), null);
public static readonly BindableProperty ToggleProperty =
BindableProperty.Create("Toggle", typeof(bool), typeof(ImageButton), false);
public static readonly BindableProperty ToggleStateProperty =
BindableProperty.Create("ToggleState", typeof(State), typeof(ImageButton), State.Up, BindingMode.TwoWay);
public ImageButton()
{
Initialize();
}
public void Initialize()
{
switch (ToggleState) // <- this is returning "State.Up" (the default) no matter what is set in the xaml.
{
case State.Up:
Source = SourceUp;
break;
case State.Down:
Source = SourceDown;
break;
case State.Inactive:
Source = SourceInactive;
break;
default:
Source = SourceUp;
break;
}
GestureRecognizers.Add(new TapGestureRecognizer
{
Command = TransitionCommand
});
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
private ICommand TransitionCommand
{
get
{
return new Command(async () =>
{
if (ToggleState != State.Inactive)
{
AnchorX = 0.48;
AnchorY = 0.48;
await this.ScaleTo(0.8, 50, Easing.Linear);
if (Toggle)
{
if (ToggleState == State.Down)
ToggleState = State.Up;
else
ToggleState = State.Down;
}
await this.ScaleTo(1, 50, Easing.Linear);
if (Command != null)
{
Command.Execute(null);
}
}
});
}
}
public string SourceUp
{
get { return (string)GetValue(SourceUpProperty); }
set { SetValue(SourceUpProperty, value); }
}
public string SourceDown
{
get { return (string)GetValue(SourceDownProperty); }
set { SetValue(SourceDownProperty, value); }
}
public string SourceInactive
{
get { return (string)GetValue(SourceInactiveProperty); }
set { SetValue(SourceInactiveProperty, value); }
}
public bool Toggle
{
get { return (bool)GetValue(ToggleProperty); }
set { SetValue(ToggleProperty, value); }
}
public State ToggleState
{
get { return (State)GetValue(ToggleStateProperty); }
set
{
SetValue(ToggleStateProperty, value);
switch (value)
{
case State.Up:
Source = SourceUp;
break;
case State.Down:
Source = SourceDown;
break;
case State.Inactive:
Source = SourceInactive;
break;
default:
Source = SourceUp;
break;
}
}
}
}
The button works if I set it up with a "Source" like so:
<custom:ImageButton
Source="i_left.png"
SourceUp="i_left.png"
SourceDown="i_right.png"
SourceInactive="i_close.png"
Toggle="True"
ToggleState="Up"
WidthRequest="{StaticResource IconMedium}"
HeightRequest="{StaticResource IconMedium}"
Command="{Binding ImageButton1Command}"/>
I shouldn't need to specify "Source" because in the constructor I set it according to the initial state.
But it appears that "ToggleState" isn't set to my xaml value yet.
I am trying to set it up like this
<custom:ImageButton
SourceUp="i_left.png"
SourceDown="i_right.png"
SourceInactive="i_close.png"
Toggle="True"
ToggleState="Down"
WidthRequest="{StaticResource IconMedium}"
HeightRequest="{StaticResource IconMedium}"
Command="{Binding ImageButton1Command}"/>
And upon load it should be on the "i_right.png" image, but it isn't.
Edit per answer: The following class works as expected!
public class ImageButton : Image
{
public enum State
{
Inactive,
Up,
Down
};
public static readonly BindableProperty CommandProperty =
BindableProperty.Create("Command", typeof(ICommand), typeof(ImageButton), null, propertyChanged: OnStateChanged);
public static readonly BindableProperty SourceUpProperty =
BindableProperty.Create("SourceUp", typeof(ImageSource), typeof(ImageButton), null, propertyChanged: OnStateChanged);
public static readonly BindableProperty SourceDownProperty =
BindableProperty.Create("SourceDown", typeof(ImageSource), typeof(ImageButton), null, propertyChanged: OnStateChanged);
public static readonly BindableProperty SourceInactiveProperty =
BindableProperty.Create("SourceInactive", typeof(ImageSource), typeof(ImageButton), null, propertyChanged: OnStateChanged);
public static readonly BindableProperty ToggleProperty =
BindableProperty.Create("Toggle", typeof(bool), typeof(ImageButton), false);
public static readonly BindableProperty ToggleStateProperty =
BindableProperty.Create("ToggleState", typeof(State), typeof(ImageButton), State.Up, BindingMode.TwoWay, propertyChanged: OnStateChanged);
public ImageButton()
{
Initialize();
}
public void Initialize()
{
GestureRecognizers.Add(new TapGestureRecognizer
{
Command = TransitionCommand
});
}
static void OnStateChanged(BindableObject bindable, object oldValue, object newValue)
{
var imageButton = bindable as ImageButton;
imageButton.SetState();
}
public void SetState()
{
switch (ToggleState)
{
case State.Up:
Source = SourceUp;
break;
case State.Down:
Source = SourceDown;
break;
case State.Inactive:
Source = SourceInactive;
break;
default:
Source = SourceUp;
break;
}
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
private ICommand TransitionCommand
{
get
{
return new Command(async () =>
{
if (ToggleState != State.Inactive)
{
AnchorX = 0.48;
AnchorY = 0.48;
await this.ScaleTo(0.8, 50, Easing.Linear);
if (Toggle)
{
if (ToggleState == State.Down)
ToggleState = State.Up;
else
ToggleState = State.Down;
}
await this.ScaleTo(1, 50, Easing.Linear);
if (Command != null)
{
Command.Execute(null);
}
}
});
}
}
public ImageSource SourceUp
{
get { return (ImageSource)GetValue(SourceUpProperty); }
set { SetValue(SourceUpProperty, value); }
}
public ImageSource SourceDown
{
get { return (ImageSource)GetValue(SourceDownProperty); }
set { SetValue(SourceDownProperty, value); }
}
public ImageSource SourceInactive
{
get { return (ImageSource)GetValue(SourceInactiveProperty); }
set { SetValue(SourceInactiveProperty, value); }
}
public bool Toggle
{
get { return (bool)GetValue(ToggleProperty); }
set { SetValue(ToggleProperty, value); }
}
public State ToggleState
{
get { return (State)GetValue(ToggleStateProperty); }
set { SetValue(ToggleStateProperty, value); }
}
}