Xamarin: Visual State Manager and custom properties - xamarin

I am trying to set a custom property on a custom control using the Visual State Manager but I'm not having any luck so far. My custom control is just a label with an additional bindable property on it.
public class SelectableLabel : Label
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableLabel), false);
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
SetValue(IsSelectedProperty, value);
}
}
I use this control inside a CollectionView to toggle the IsSelected property when the control enters the Selected visual state.
<CollectionView
x:Name="cv"
ItemsSource="{Binding Names}"
SelectionMode="Multiple"
SelectedItems="{Binding SelectedNames, Mode=TwoWay}"
VerticalOptions="Fill">
<CollectionView.ItemTemplate>
<DataTemplate>
<local:SelectableLabel
x:Name="lblName"
Text="{Binding First}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Selected">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableLabel>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
When I run this on an iOS simulator, I'm not seeing the setter being fired when the visual state changes to Selected. If I change the property in the setter to BackgroundColor or Text, I'm seeing the expected behavior. The problem seems specific to the custom property. I looked at the documentation for the Setter.Property and it states that the Setter can be applied to a BindableProperty which IsSelected is. Am I doing something wrong or does the VSM not support this functionality?
Edit: The CollectionView part of this example is irrelevant. The same issue happens with this code.
public class SelectableEntry : Entry
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false);
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
var color = value ? Color.LightBlue : Color.LightPink;
SetValue(IsSelectedProperty, value);
SetValue(BackgroundColorProperty, color);
}
}
}
Here is the corresponding XAML. The background color changes when the first custom Entry control receives focus while the second does not. I also don't see my WriteLine statement in the console.
<local:SelectableEntry Text="First">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="LightBlue" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Focused">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="LightPink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
<local:SelectableEntry Text="Second">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Focused">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>

I have tried with Bindable Property to check whether property be invoked in VisualStateManager . That's regrettable, it can not be invoked even in propertyChanged method . I think maybe bindable property can not work in VisualStateManager.
Custom Entry Code as follow :
public class SelectableEntry : Entry
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false ,propertyChanged:changedMethod);
private static void changedMethod(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine($"MDO: {oldValue}.IsSelected_set={newValue}");
}
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
var color = value ? Color.LightBlue : Color.LightPink;
SetValue(IsSelectedProperty, value);
SetValue(BackgroundColorProperty, color);
}
}
}
Solution:
However , there is a Attached Properties that can be used in Style.Setters. Then you can also have a try with it in VisualState.Setters .It also will work .
Attached property class Code as follow :
public class SelectableEntryStyle
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.CreateAttached("IsSelected", typeof(bool), typeof(SelectableEntryStyle), false,propertyChanged:onChangedMethod);
private static void onChangedMethod(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine($"MDO:IsSelected_set={newValue}");
var color = (bool)newValue ? Color.LightBlue : Color.LightPink;
var entry = bindable as Entry;
entry.SetValue(Entry.BackgroundColorProperty, color);
}
public static bool GetIsSelected(BindableObject view)
{
return (bool)view.GetValue(IsSelectedProperty);
}
public static void SetIsSelected(BindableObject view, bool value)
{
view.SetValue(IsSelectedProperty, value);
}
}
The code of Xaml as follow :
<local:SelectableEntry FontSize="18" Placeholder="Bindable Property">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
<Entry Placeholder="Attached Property">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="local:SelectableEntryStyle.IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="local:SelectableEntryStyle.IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
Then console can print log when Entry is Normal or Focused .
02-18 14:26:27.360 I/mono-stdout(26014): MDO:IsSelected_set=True
02-18 14:26:28.675 I/mono-stdout(26014): MDO:IsSelected_set=False
The effects :

Related

VisualStateManager.GoToState useTransitions Ignored in custom control UWP

I've created a custom control which inherits from contentControl . The control uses PlaneProjection.RotationX animation while changing from open to close and vice versa.
I want the control initial state to be in closed state. When the app launched the transition from open to close is shown even though I set changeVisualState(false).
What am I doing wrong?
My code:
public sealed class FlipPanel : ContentControl
{
private VisualState collapsedState;
private FrameworkElement contentElement;
public FlipPanel()
{
this.DefaultStyleKey = typeof(FlipPanel);
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(FlipPanel), new PropertyMetadata(false, onIsOpenChanged));
private static void onIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FlipPanel flipPanel = d as FlipPanel;
flipPanel.changeVisualState(true);
}
private void changeVisualState(bool useTransitions)
{
Debug.WriteLine(IsOpen.ToString());
if (IsOpen)
{
if (contentElement != null)
{
contentElement.Visibility = Visibility.Visible;
}
VisualStateManager.GoToState(this, "Opening", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Closing", useTransitions);
}
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
contentElement = (FrameworkElement)GetTemplateChild("Content");
if (contentElement != null)
{
collapsedState = (VisualState)GetTemplateChild("Closing");
if ((collapsedState != null) && (collapsedState.Storyboard != null))
{
collapsedState.Storyboard.Completed += (object sender, object e) =>
{
contentElement.Visibility = Visibility.Collapsed;
};
}
changeVisualState(false);
}
}
}
and my Style
<Style TargetType="local:FlipPanel" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:FlipPanel">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ViewStates">
<VisualState x:Name="Opening">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="Content">
<EasingDoubleKeyFrame KeyTime="0" Value="-90"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Closing">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="Content">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="90"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter Content="{TemplateBinding Content}" x:Name="Content">
<ContentPresenter.Projection>
<PlaneProjection CenterOfRotationX="0.5"/>
</ContentPresenter.Projection>
</ContentPresenter>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I think this is because you make the Content Collapsed in the Completed event of the storyboard and then you set changeVisualState(false);, it will do nothing since the "closing" storyboard is completed.
I modified your code of OnApplyTemplate like this and it works:
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
contentElement = (FrameworkElement)GetTemplateChild("Content");
if (contentElement != null)
{
if (!IsOpen)
contentElement.Visibility = Visibility.Collapsed;
else
changeVisualState(true);
}
}

Set image for disabled button in windows phone 8

I'm using the below ControlTemplate for image button in my application.
Control Template in App.xaml is below.
<!--Below is the style for all ImageButtons in this project -->
<Style x:Key="ButtonStyleIB" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
<!--<Setter Property="Padding" Value="10,3,10,5"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="grid" Background="Transparent">
<Grid.Projection>
<PlaneProjection/>
</Grid.Projection>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneBackgroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0" To="-25" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)" Storyboard.TargetName="grid" />
<!--d:IsOptimized="True"-->
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.CenterOfRotationX)" Storyboard.TargetName="grid" />
<!--d:IsOptimized="True"-->
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now in my page i'm creating the button as below.
<Button x:Name="saveButton" Style ="{StaticResource ButtonStyleIB}" VerticalAlignment="Center" Click="save_button_clicked">
<Image Source="/STCDirectory;component/Images/save.png" Stretch="Fill" />
</Button>
Upto this it is fine. So when i run the application the button with image is displaying.
And it is working fine.
Now based on some internal condition i want to disable the image button.
So for disable the button i'm using the below code.
saveButton.IsEnabled = false;
So then image button is not clickable according to the above step.
But i want to change the image also whenever the button is disabled.
How could i achive this.
Dont give direct uri of the image in the buttons
you can bind it with a string property and on button click you can change the that string to another uri then it will change the image of the button to new image.
i hope this might help ...
you can do it using imagesource binding ..first define your button like this.
<Button Grid.Row="1" x:Name="saveButton" Style ="{StaticResource ButtonStyleIB}" VerticalAlignment="Center" Click="saveButton_Click_1" Width="300" Height="50">
<Image Source="{Binding ImageSource}" Stretch="Fill" />
</Button>
and in your page.xaml.cs create property ImageSource like this.
public partial class MainPage : PhoneApplicationPage , INotifyPropertyChanged
{
// Constructor
public MainPage()
{
InitializeComponent();
ImageSource = "/Assets/1.jpg";
this.DataContext = this;
}
private string _ImageSource;
public string ImageSource
{
get
{
return _ImageSource;
}
set
{
_ImageSource = value;
FirePropertyChanged("ImageSource");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void saveButton_Click_1(object sender, RoutedEventArgs e)
{
ImageSource = "/Assets/2.jpg";
saveButton.IsEnabled = false;
}
}
here 1.jpg and 2.jpg are images i have toggled.

Can one set visual state for PhoneApplicationPage?

I have a question about visual state management of PhoneApplicationPage. Basically, can one use VisualStateManager approach to set states of the page itself? In the end, it inherits the Control class, so this stuff should be applicable.
I'm asking because I've tried and failed. Here's my code:
<phone:PhoneApplicationPage
x:Class="Encountered.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="State1">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="button" Storyboard.TargetProperty="(UIElement.Opacity)" To="1" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="State2">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="button" Storyboard.TargetProperty="(UIElement.Opacity)" To="0" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Orientation="Vertical">
<HyperlinkButton x:Name="button" NavigateUri="/Views/EditPage.xaml" Content="Go"/>
<Button Click="Button_Click">state</Button>
</StackPanel>
</phone:PhoneApplicationPage>
In the code-behind:
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
VisualStateManager.GoToState(this, "State1", true);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(this, "State2", true);
}
}
Any ideas what could be wrong?
The VisualStateManager enables you to specify when a control enters a specific state.
So, simply wrap all in a grid :
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="State1">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="button" Storyboard.TargetProperty="(UIElement.Opacity)" To="1" />
</Storyboard>
</VisualState>
<VisualState x:Name="State2">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="button" Storyboard.TargetProperty="(UIElement.Opacity)" To="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Orientation="Vertical">
<HyperlinkButton x:Name="button" NavigateUri="/Views/EditPage.xaml" Content="Go" />
<Button Click="Button_Click" >state</Button>
</StackPanel>
</Grid>

What kind of controls should I use in Windows Phone 7 to make a "star" control

I show some items from the database in the list and I want the user to be able to mark some of them as favorites. The best way would be to show some star icon for user to click on which then would turn into slightly different star to indicate that the item is now favorite. What controls should I use for those stars? Could I bind them to some boolean property of the item?
You can also use vector graphics to achieve this without using png icons.
I created this style a while a go, basically it is for CheckBox but I think it also works for ToggleButton by simply changing the TargetType from CheckBox to ToggleButton.
By setting the IsChecked of either CheckBox or ToggleButton to True, the star will be filled with the accent color of the phone.
The Style
<Style x:Key="StarCheckBoxStyle" TargetType="CheckBox">
<Setter Property="Background" Value="{StaticResource PhoneAccentBrush}" />
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused" />
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="00:00:00.2000000" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="check" Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Indeterminate" />
<VisualState x:Name="Unchecked" />
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid" />
<VisualState x:Name="InvalidUnfocused" />
<VisualState x:Name="InvalidFocused" />
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="00:00:00.2000000" />
</VisualStateGroup.Transitions>
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed" />
<VisualState x:Name="Disabled" />
<VisualState x:Name="Normal" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="check" Stretch="Fill" Height="48" Width="48" UseLayoutRounding="False" Data="M16.000002,0 L19.77688,12.223213 L32,12.222913 L22.111122,19.776972 L25.888546,32 L16.000002,24.445454 L6.1114569,32 L9.8888807,19.776972 L8.574415E-09,12.222913 L12.223121,12.223213 z" Opacity="0" Fill="{TemplateBinding Background}" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Path x:Name="stroke" Stretch="Fill" Stroke="{TemplateBinding Background}" Height="48" Width="48" UseLayoutRounding="False" Data="M16.000002,0 L19.77688,12.223213 L32,12.222913 L22.111122,19.776972 L25.888546,32 L16.000002,24.445454 L6.1114569,32 L9.8888807,19.776972 L8.574415E-09,12.222913 L12.223121,12.223213 z" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
<ContentPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="0,0,8,0" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Apply the style
<CheckBox Content="unchecked state" Style="{StaticResource StarCheckBoxStyle}" />
<CheckBox IsChecked="True" Content="checked state" Style="{StaticResource StarCheckBoxStyle}" />
How they look
You can use a ToggleButton to do this. Keep 2 star images in your project, one for clicked and the other for not clicked. You can use data binding along with the IsChecked property to switch between the 2 images.
XAML:
<ToggleButton IsChecked="{Binding IsFavorited, Mode=TwoWay}">
<ToggleButton.Content>
<Image Source="{Binding FavoriteImage}" />
</ToggleButton.Content>
</ToggleButton>
C#:
Image ClickedImage = new Image() {
Source = new BitmapImage(new Uri("/clicked.png", UriKind.Relative));
};
Image NotClickedImage = new Image() {
Source = new BitmapImage(new Uri("/not_clicked.png", UriKind.Relative));
};
bool _isFavorited = false;
Image _favoriteImage = NotClickedImage;
public bool IsFavorited
{
get { return _isFavorited; }
set
{
if( _isFavorited != value ) {
_isFavorited = value;
NotifyPropertyChanged( "IsFavorite" );
FavoriteImage = _isFavorited ? ClickedImage : NotClickedImage;
}
}
}
public Image FavoriteImage
{
get { return _favoriteImage; }
private set
{
if( _favoriteImage != value ) {
_favoriteImage = value;
NotifyPropertyChanged( "FavoriteImage" );
}
}
}
The class containing the code behind needs to implement the INotifyPropertyChanged interface.
Refer here for a list of star controls available for the silverlight toolkit for the windows phone.

wp7: how to create image button and bind template to ImageSource dependency property

I am trying to create a silverlight imagebutton control. the xaml and code-behind will follow, but my problem is that I get an 'unspecified error' when i try a TemplateBinding to the image's source property. I'm hoping that someone can help me preserve my last shreds of sanity and last few scraps of hair on my head.
XAML:
<Grid x:Name="LayoutRoot">
<Button x:Name="btn" Click="Cancel_Click" Margin="0,0,10,10"
Width="{Binding ImageWidth}" Height="{Binding ImageHeight}" >
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="ButtonBackground"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="33"
Margin="{StaticResource PhoneTouchTargetOverhang}">
<Image x:Name="image" Source="{TemplateBinding IconSource}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}" />
</Border>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</UserControl>
Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace sltest.controls
{
public partial class ImageButtonControl : UserControl
{
public ImageButtonControl()
{
InitializeComponent();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
}
public double ImageWidth { get; set; }
public double ImageHeight { get; set; }
public static readonly DependencyProperty IconSourceProperty =
DependencyProperty.Register("IconSource", typeof(ImageSource), typeof(ImageButtonControl), null);
public ImageSource IconSource
{
get { return base.GetValue(IconSourceProperty) as ImageSource; }
set { base.SetValue(IconSourceProperty, value); }
}
}
}
I'm using the following implementation which contains additional functionality to display text, but you can exclude it:
ImageTextButtonControl class
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace SecureBox.Controls
{
public class ImageTextButtonControl: Button
{
///
///
///
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register("ImageHeight", typeof(double), typeof(ImageTextButtonControl), null);
public double ImageHeight
{
get { return (double)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
///
///
///
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(double), typeof(ImageTextButtonControl), null);
public double ImageWidth
{
get { return (double)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
///
///
///
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapImage), typeof(ImageTextButtonControl), null);
public BitmapImage ImageSource
{
get { return (BitmapImage)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
///
///
///
public static readonly DependencyProperty TextMarginProperty =
DependencyProperty.Register("TextMargin", typeof(Thickness), typeof(ImageTextButtonControl), null);
public Thickness TextMargin
{
get { return (Thickness)GetValue(TextMarginProperty); }
set { SetValue(TextMarginProperty, value); }
}
///
///
///
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ImageTextButtonControl), null);
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public ImageTextButtonControl()
{
DefaultStyleKey = typeof(ImageTextButtonControl);
}
}
}
Themes\generic.xaml contains:
<Style TargetType="local:ImageTextButtonControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageTextButtonControl">
<Grid Margin="{StaticResource PhoneTouchTargetOverhang}" toolkit:TiltEffect.IsTiltEnabled="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="White" BorderThickness="0" Opacity="0.9" >
<Image Grid.Column="0" Width="{TemplateBinding ImageWidth}" Height="{TemplateBinding ImageHeight}" Source="{TemplateBinding ImageSource}" VerticalAlignment="Top" />
</Border>
<TextBlock Grid.Column="1" Margin="{TemplateBinding TextMargin}" Text="{TemplateBinding Text}" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="{TemplateBinding FontSize}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Example of usage:
<local:ImageTextButtonControl ImageSource="/ImagePath/YourImage.png" Text="YourText" FontSize="50" ImageHeight="128" ImageWidth="128" TextMargin="20,0,0,0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<Command:EventToCommand
Command="{Binding GoTo}" CommandParameterValue="YourCommandParameter" />
</i:EventTrigger>
</i:Interaction.Triggers>
Note:
I've inherited the control from Button control. This allows me to
subscribe to Click event using MVVMLight framework
Xaml code is located in special place: Themes\generic.xaml file
I guess you can try to check whether these notes can fix your the issue in your code or use my one instead.

Resources