I have a ListView whose ItemTemplate is a custom control (acts as expander) that has a toggle that is always visible and border content bellow that expands as needed.
<ControlTemplate TargetType="local:ExpanderControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ExpandStateGroup">
<VisualState x:Name="Collapsed">
<!--<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_ExpandableContent"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
To="0.0"
Duration="0:0:0.2"
AutoReverse="False"
EnableDependentAnimation="True"></DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="Expanded">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_ExpandableContent"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
To="1.0"
Duration="0:0:0.2"
AutoReverse="False"
EnableDependentAnimation="True"></DoubleAnimation>
</Storyboard>-->
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_ExpandableContent"
Storyboard.TargetProperty="Height"
To="0.0"
Duration="0:0:0.2"
AutoReverse="False"
EnableDependentAnimation="True"></DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="Expanded">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_ExpandableContent"
Storyboard.TargetProperty="Height"
To="100.0"
Duration="0:0:0.2"
AutoReverse="False"
EnableDependentAnimation="True"></DoubleAnimation>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ToggleButton x:Name="PART_expanderButton" Foreground="{TemplateBinding Foreground}"
Style="{StaticResource ExpanderButtonStyle}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded , Mode=TwoWay}">
<ContentPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
ContentTemplate="{TemplateBinding HeaderContentTemplate}" Content="{TemplateBinding Header}"
Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontStyle}"
Margin="{TemplateBinding Padding}"/>
</ToggleButton>
<Border Grid.Row="1" x:Name="PART_ExpandableContent" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
Height="0">
<!--<Border.RenderTransform>
<ScaleTransform ScaleY="0.0" />
</Border.RenderTransform>-->
<ContentPresenter x:Name="PART_ExpandableContentPresenter" Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}">
</ContentPresenter>
</Border>
</Grid>
</ControlTemplate>
I've been playing around with VisualState trying to achieve a simple animation: when clicked the item expands and when clicked again it collapses.
Using Visibility on the "expandable" control would be an option except I want a "growing" animation, having the Height increase until it's full height, instead of the snapping effect that the Visibility provides.
I also messed around with the ScaleY effect and it is almost what I want, except the parent reserves the Height for the expandable control even when this is with ScaleY = 0 leaving a big unwanted space between every element on the list and it makes sense.
Now the working solution as demonstrated above is having a set Height value on the control and varying between this one and 0. But I would like to achieve a more reusable solution without having to hardcode the height.
Any help would be appreciated.
Thanks!
Ive recently built a Custom Control that does something similar to the behaviour you are after. Here is a test version to show what I mean as you maybe able to adapt for your project.
Two key points are the control the items you wish to move are in a StackPanel and the Height property is manipulated rather than using Transform.ScaleY
MainPage.xaml
<Grid Background="Black">
<StackPanel Orientation="Vertical" Margin="20"
VerticalAlignment="Top">
<Button x:Name="ExpandButton" Width="200" Height="75" Content="expand"
Foreground="White" BorderBrush="White"
Click="ExpandButton_Click" />
<Rectangle x:Name="Red" Width="200" Height="200" Fill="Red" />
<Rectangle x:Name="Green" Width="200" Height="200" Fill="Green" />
<Rectangle x:Name="Blue" Width="200" Height="200" Fill="Blue" />
</StackPanel>
</Grid>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
private double panelHeight = 0.0;
private bool contentOpen = false;
public MainPage()
{
this.InitializeComponent();
//this is the Green rectangles height we want to expand on Button Click
panelHeight = this.Green.ActualHeight;
//expandable item should be 0 height is Flag is false
if (!contentOpen)
Green.Height = 0.0;
}
private void ExpandButton_Click(object sender, RoutedEventArgs e)
{
OpenCloseAnimation animate;
//Create animation based on Flag.
if (contentOpen)
{
//pass in zero height as we are closing
animate = new OpenCloseAnimation(Green, 0);
contentOpen = false;
this.ExpandButton.Content = "expand";
}
else
{
//pass in Green rectangles actual height as we are opening
animate = new OpenCloseAnimation(Green, panelHeight);
contentOpen = true;
this.ExpandButton.Content = "close";
}
//start the animation
animate.sb.Begin();
}
}
OpenCloseAnimation.cs
public class OpenCloseAnimation
{
public Storyboard sb { get; set; }
private double _height;
private FrameworkElement _element;
public OpenCloseAnimation(FrameworkElement element, double height)
{
_element = element;
_height = height;
CreateStoryboard();
}
public void CreateStoryboard()
{
sb = new Storyboard();
var animation = new DoubleAnimationUsingKeyFrames { EnableDependentAnimation = true };
var easing = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)), Value = _height };
animation.KeyFrames.Add(easing);
Storyboard.SetTargetProperty(animation, "(FrameworkElement.Height)");
Storyboard.SetTarget(animation, _element);
sb.Children.Add(animation);
}
}
Hope it might help
Related
In my UWP application, I have a ListView with incremental loading. Now I need to also include an image in the ListViewItem. I tried directly giving it the URI.
where ThumbnailImage is just a string in my view model. The problem with this is that because of incremental loading, when I scroll down a little bit while the previous images are still loading, the app just hangs. Whereas if I let all the images appear and then scroll down, it works fine.
I've also tried the ImageEx control from the toolkit and it has the same problem.
I did some searching the only thing I found was doing IsAsync = True in XAML. But it seems that IsAsync is not available in UWP.
My ViewModel:
public class MyViewModel
{
...
public string ThumbnailImage { get; set; }
...
}
My XAML ListView that gets filled incrementally
<ListView Grid.Row="0"
SelectionMode="Single"
IsItemClickEnabled="True"
ItemClick="SearchResultListView_ItemClick" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="0, 0, 0, 1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter
ContentTransitions="{TemplateBinding ContentTransitions}"
SelectionCheckMarkVisualEnabled="True"
CheckBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
CheckBoxBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
DragBackground="{ThemeResource ListViewItemDragBackgroundThemeBrush}"
DragForeground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
FocusBorderBrush="{ThemeResource SystemControlForegroundAltHighBrush}"
FocusSecondaryBorderBrush="{ThemeResource SystemControlForegroundBaseHighBrush}"
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
PointerOverBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
SelectedBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
SelectedForeground="{ThemeResource SystemControlDisabledTransparentBrush}"
SelectedPointerOverBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
PressedBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
SelectedPressedBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
ContentMargin="{TemplateBinding Padding}"
CheckMode="Inline"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewModel:MyViewModel">
<UserControl >
<Grid Style="{StaticResource SomeStyle}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="VisualStatePhone">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ThumbnailImage.Width" Value="50"/>
<Setter Target="ThumbnailImage.Height" Value="50"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateTablet">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ThumbnailImage.Width" Value="70"/>
<Setter Target="ThumbnailImage.Height" Value="70"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateDesktop">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1200" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ThumbnailImage.Width" Value="90"/>
<Setter Target="ThumbnailImage.Height" Value="90"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="30"></RowDefinition>
<RowDefinition MaxHeight="30"></RowDefinition>
<RowDefinition MaxHeight="30"></RowDefinition>
<RowDefinition MaxHeight="30"></RowDefinition>
<RowDefinition MaxHeight="30"></RowDefinition>
<RowDefinition MaxHeight="10"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{x:Bind ThumbnailImage}"
Visibility="{x:Bind ThumbnailImage, Converter={StaticResource BoolToVisibilityImage}}"
Name="ThumbnailImage"/>
<TextBlock Text="{x:Bind Name}" .../>
<Grid Grid.Row="1"
Grid.Column="1"
Visibility="{x:Bind Source, Converter={StaticResource ConverterNameHere}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind lblSource, Mode=OneWay}" .../>
<TextBlock Text="{x:Bind Source}" .../>
</Grid>
<Grid Grid.Row="2"
Grid.Column="1"
Visibility="{x:Bind Author, Converter={StaticResource ConverterNameHere}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind lblAuthor, Mode=OneWay}" .../>
<TextBlock Text="{x:Bind Author}" .../>
</Grid>
<TextBlock Text="{x:Bind EducationalLevel}" .../>
<StackPanel Orientation="Horizontal"
Grid.Row="4"
Grid.Column="1">
<ItemsControl ItemsSource="{x:Bind AccessRights}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{x:Bind Languages}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl ItemsSource="{x:Bind TypeImages}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</UserControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here's my incremental loading class:
public class ItemsToShow : ObservableCollection<MyViewModel>, ISupportIncrementalLoading
{
private SearchResponse ResponseObject { get; set; } = new SearchResponse();
MyViewModel viewModel = null;
public bool HasMoreItems
{
get
{
if ((string.IsNullOrEmpty(SearchResultDataStore.NextPageToken) && !SearchResultDataStore.IsFirstRequest) || SearchResultDataStore.StopIncrementalLoading)
return false;
if(SearchResultDataStore.IsFirstRequest)
{
using (var db = new DbContext())
{
var json = db.UpdateResponse.First(r => r.LanguageId == DataStore.Language).JsonResponse;
Metadata = Newtonsoft.Json.JsonConvert.DeserializeObject<UpdateApiResponse>(json).response.metadata.reply;
}
var returnObject = SearchResultDataStore.SearchResponse;
ResponseObject = returnObject.response;
//This will show a grid with some message for 3 seconds on a xaml page named SearchResultPage.
Toast.ShowToast(ResponseObject.message, SearchResultPage.Current.ToastGrid);
}
else
{
SearchApiResponse returnObject = null;
try
{
returnObject = new SearchApiCall().CallSearchApiAsync(param1, param2, param3).Result;
}
catch
{
return false;
}
ResponseObject = returnObject.response;
}
try
{
return ResponseObject.documents.Count > 0;
}
catch { return false; }
}
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
CoreDispatcher coreDispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(async () =>
{
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
foreach (var item in ResponseObject.documents)
{
this.Add(PrepareViewModel(item));
}
});
await Task.Delay(350);
return new LoadMoreItemsResult() { Count = count };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
public MyViewModel PrepareViewModel(Document document)
{
viewModel = new MyViewModel();
viewModel.property1 = document.value1;
viewModel.property2 = document.value2;
...
...
if(SearchResultDataStore.ShowImage)
{
//thumbnailUrl is a string.
viewModel.ThumbnailImage = document.thumbnailUrl;
}
else
{
viewModel.ThumbnailImage = "somegarbage.png";
}
...
...
return viewModel;
}
}
By default when you're using x:Bind for the source the binding is not updated automatically.
To verify that's actually the root cause of your problem you could set a breakpoint in your value converter, and see how many time is called.
I'd suggest using x:Bind ThumbnailImage, Mode=OneWay
You might have to implement INotifyPropertyChanged in your code behind.
Make few steps:
Add x:Phase attribute to Image control. Set biggest than other value (for ex. all are "0", set "1" etc.)
Add Mode=OneWay to Image because your should know when property will be changed.
Add control extension to Image control with dependency property ImagePath. When property will be changed -> start to load image from net asynchronically (for ex. by HttpClient). When request will be finished -> get content and put it to BitmapSource and put it to the Source property of Image.
Be sure that original image's size not much more than Image control's size, because if it's true -> UI will be hang.
I'm pretty new to UWP Xaml C++/CX development so I apologize if I use the wrong terms.
I'm trying to make an image/photo thumbnail selection tray. The user is to select which folder to browse and the contents are displayed in the scrollable thumbnail tray with the filename displayed below.
Pages are built in xaml and i'm using C++/CX. The thumbnail tray is built using the following xaml
<ScrollViewer Grid.Row="1" Margin="20,20,20,20" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<Grid x:Name="thumb_grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
</Grid>
</ScrollViewer>
I dynamically add new rows via code using
AddRow() {
RowDefinition^ rd = ref new RowDefinition();
rd->Height.Auto;
thumb_grid->RowDefinitions->Append(rd);
}
Then I populate each grid element with a new Button using
AddImageButton(int row, int col) {
Button^ b = ref new Button();
auto style = R->Lookup("ButtonStyle1");
b->Style = safe_cast<Windows::UI::Xaml::Style^>(style);
thumb_grid->Children->Append(b);
thumb_grid->SetRow(b, row);
thumb_grid->SetColumn(b, col);
}
Where "ButtonStyle1" is defined in my dictionary.xaml as
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundTransparentBrush}"/>
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}"/>
<Setter Property="Padding" Value="8,4,8,4"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="UseSystemFocusVisuals" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<!--REMOVED THIS SECTION TO REDUCE EXAMPLE CODE-->
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}"
Content="{TemplateBinding Content}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Image x:Name="thumb_image" Grid.Row="0" HorizontalAlignment="Stretch" Source="Assets/demo.jpg"/>
<TextBlock x:Name="thumb_filename" Grid.Row="1" Text="demo.jpg" FontSize="20" Foreground="White"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
TextAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
All this results in creating the desired output as shown in the following image. Obviously I'm just using a hardcoded image and text for the Button content at the moment.
The Problem
The question is how do I dynamically change the content of the custom image buttons?
Currently I can change the images within a button by accessing the content by name. However this requires the image content of a button have a unique name.
Xaml code
<Button x:Name="btnToggleShowHide" Grid.Row="1" Height="50" Width="50"
HorizontalAlignment="Left" VerticalAlignment="Bottom"
Background="Transparent"
Click="btnToggleShowHide_Click">
<Image x:Name="imgToggleShowHideBtn"
Source="Assets/Graphics/show.png"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Button>
and the C++/CX code for changing the image (excluding the actually logic, just showing the code used)
show = ref new BitmapImage(ref new Uri(imgToggleShowHideBtn->BaseUri->RawUri, "Assets/Graphics/show.png"));
hide = ref new BitmapImage(ref new Uri(imgToggleShowHideBtn->BaseUri->RawUri, "Assets/Graphics/hide.png"));
imgToggleShowHideBtn->Source = show;
imgToggleShowHideBtn->Source = hide;
But I cannot directly access the text within the custom button image "demo.jpg" by using its name
thumb_filename->Text = "test.jpg";
because the textblock thumb_filename only exists at runtime. In AddImageButton() I need to somehow set the content but can't figure out how to access the child objects in the button. In my mind I want to do something like
Button^ b = ref new Button();
auto style = R->Lookup("ButtonStyle1");
b->Style = safe_cast<Windows::UI::Xaml::Style^>(style);
//NOT ACTUAL CODE - THIS IS WHAT I'M TRYIN TO ACHIEVE
b->Image->Source = img; //obviously wont work because it has no knowledge whether ButtonStyle1 contains an image
b->TextBlock->Text = filename; //obviously wont work because it has no knowledge whether ButtonStyle1 contains a textblock
So I ended up using the approach suggested by Nico Zhu - MSFT and using GridView with data binding.
Firstly I created a button class (Note: I'm still in the process of refining so there may be some redundant snippets)
namespace Models {
[Windows::UI::Xaml::Data::Bindable]
public ref class FileBrowser sealed
{
private:
Platform::String^ _fname;
Platform::String^ _path;
ImageSource^ _thumbImage;
BitmapImage^ _imagedata;
public:
FileBrowser();
property Platform::String^ Filename
{
Platform::String^ get()
{
return _fname;
}
void set(Platform::String^ value)
{
_fname = value;
}
}
property Platform::String^ Filepath
{
Platform::String^ get()
{
return _path;
}
void set(Platform::String^ value)
{
_path = value;
}
}
property ImageSource^ ThumbImage
{
ImageSource^ get()
{
return _thumbImage;
}
void set(ImageSource^ value)
{
_thumbImage = value;
}
}
property BitmapImage^ ImageData
{
BitmapImage^ get()
{
return _imagedata;
}
void set(BitmapImage^ value)
{
_imagedata = value;
}
}
};
}
Add #include "FileBrowser.h" to the "MainPage.xaml.h". In my View.xaml page that contains the image browser/list add xmlns:local="using:Models" to the tag.
My GridView is created in the following way
<GridView x:Name="thumb_grid" Grid.Row="1" Margin="20,20,20,20" IsItemClickEnabled="True" SelectionMode="Single" ItemClick="thumb_grid_ItemClick">
<GridView.ItemTemplate>
<DataTemplate>
<Grid MaxHeight="200" MinHeight="200" MinWidth="200" MaxWidth="200">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding ImageData}"/>
<TextBlock Text="{Binding Filename}" Grid.Row="1" FontSize="20" Foreground="Gray" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.Items>
</GridView.Items>
</GridView>
At runtime a new button is added to the GridView using (where f and I supply the relevant file and image data)
auto btn = ref new Models::FileBrowser();
btn->Filename = f->GetAt(i)->DisplayName + f->GetAt(i)->FileType;
btn->Filepath = f->GetAt(i)->Path;
btn->ImageData = I;
thumb_grid->Items->Append(btn);
This now results in the desired image tray that displays the contents of a folder. Each image clickable/touchable and calls the same function, in this case each item in the GridView calls thumb_grid_ItemClick() when pressed.
Data from the button can be obtained using the following
void View::thumb_grid_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e)
{
auto btn = (Models::FileBrowser^)e->ClickedItem;
}
I'm using the following GridView and have an ItemTemplate with 3 Elements inside.
Now what i would like to do, is to animate the MyTextBlock opacity on PointerOver from the GridViewItem.
<GridView x:Name="MyList" ItemContainerStyle="{StaticResource GridViewItemContainerStyle}" ItemsSource="{Binding MyList}">
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="200" />
<RowDefinition Height="3" />
<RowDefinition Height="80" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Url}" />
<ProgressBar Grid.Row="1" IsIndeterminate="True" />
<TextBlock Name="MyTextBlock" Opacity="0" Text="Test" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The ItemContainerStyle is like this. Problem is, i can't access MyTextBlock with Storyboard.TargetName="MyTextBlock" from here since it's inside the ContentPresenter.
How can i do a visualstate animation of an element inside the ContentPresenter?
<Style TargetType="GridViewItem" x:Key="GridViewItemContainerStyle">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<Grid x:Name="ContentBorder" Control.IsTemplateFocusTarget="True" RenderTransformOrigin="0.5,0.5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="ContentPresenter" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BorderRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="1" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="BorderRectangle" Fill="{ThemeResource SystemControlHighlightListAccentLowBrush}" Opacity="0" />
<ContentPresenter x:Name="ContentPresenter" ContentTransitions="{TemplateBinding ContentTransitions}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit:
I tried using the following TargetProperty, but it's just setting the opacity of the whole ContentPresenter
and not just TextBlock inside of it.
<DoubleAnimation Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="(TextBlock.Opacity)" Duration="0" To="1" />
Problem is, i can't access MyTextBlock with Storyboard.TargetName="MyTextBlock" from here since it's inside the ContentPresenter. How can i do a visualstate animation of an element inside the ContentPresenter?
You can add two visualstates inside DataTemplate(PointerEntered and PointerExited):
<GridView x:Name="MyList" Grid.Row="1" ItemContainerStyle="{StaticResource GridViewItemContainerStyle}" >
<GridView.ItemTemplate>
<DataTemplate>
<Grid PointerExited="Grid_PointerExited" PointerEntered="Grid_PointerEntered">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="PointerEntered">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyTextBlock" Storyboard.TargetProperty="Opacity" Duration="0" To="1" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerExited">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyTextBlock" Storyboard.TargetProperty="Opacity" Duration="0" To="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="200" />
<RowDefinition Height="3" />
<RowDefinition Height="80" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Url}" />
<ProgressBar Grid.Row="1" IsIndeterminate="True" />
<TextBlock Grid.Row="2" Name="MyTextBlock" Opacity="0" Text="Test" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
And using the following codes in Grid.PointerEntered and Grid.PointerExited events to get the storyboard start:
private void Grid_PointerExited(object sender, PointerRoutedEventArgs e)
{
Grid grid = sender as Grid;
var visualStateGroups = VisualStateManager.GetVisualStateGroups(grid);
var visualStateGroup = visualStateGroups[0];
visualStateGroup.States[1].Storyboard.Begin();
}
private void Grid_PointerEntered(object sender, PointerRoutedEventArgs e)
{
Grid grid = sender as Grid;
var visualStateGroups = VisualStateManager.GetVisualStateGroups(grid);
var visualStateGroup = visualStateGroups[0];
visualStateGroup.States[0].Storyboard.Begin();
}
DataTemplate can come from anywhere (App Resource, Window Resource, Local Resource, Code), so it uses its own NameScope.
You are unable to use MyTextBlock because of Namescope issues. So, best would be to define VisualStates as part of DataTemplate itself.
I have LongListSelector with GroupHeaderTemplate and ItemTemplate.
I would like to add 'Selected' effect on selected item in group. For example my element called RightArrow can go grey (now it's blue).
I was trying to do that with Expression Blend but effect didn't apply on selected item, but on every item.
<phone:LongListSelector x:Name="longListSelectorState" RenderTransformOrigin="-0.893,0.033" ItemTemplate="{StaticResource StateItemTemplate}" JumpListStyle="{StaticResource StateJumpListStyle}" LayoutMode="List" IsGroupingEnabled="true" HideEmptyGroups ="true" GroupHeaderTemplate="{StaticResource StateGroupHeaderTemplate}" Style="{StaticResource LongListSelectorStyle}"/>
<Style x:Key="LongListSelectorStyle" TargetType="phone:LongListSelector">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="phone:LongListSelector">
<Grid Background="{TemplateBinding Background}" d:DesignWidth="480" d:DesignHeight="800">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ScrollStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="00:00:00.5"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Scrolling">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="VerticalScrollBar"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Duration="0" To="48" Storyboard.TargetProperty="(Control.FontSize)"
Storyboard.TargetName="textBlock" />
<ColorAnimation Duration="0" To="Red" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="textBlock" />
</Storyboard>
</VisualState>
<VisualState x:Name="NotScrolling"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Margin="{TemplateBinding Padding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<ViewportControl x:Name="ViewportControl" HorizontalContentAlignment="Stretch" VerticalAlignment="Top"/>
<ScrollBar x:Name="VerticalScrollBar" Grid.Column="1" Margin="4,0,4,0" Opacity="0" Orientation="Vertical"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="StateItemTemplate">
<StackPanel VerticalAlignment="Top">
<Grid x:Name="grid">
<StackPanel x:Name="stackPanel" Orientation="Vertical" HorizontalAlignment="Left">
<TextBlock x:Name="textBlock" Text="{Binding ItemName}" Foreground="#DE000000" FontFamily="Segoe WP SemiLight" FontSize="29.333" Padding="0,5,0,0" Margin="4,0,0,0" />
<TextBlock x:Name="textBlock1" Text="{Binding SubItemNames}" Visibility="{Binding HasSubItems, Converter={StaticResource BoolVisibilityConverter}}" Foreground="#DE000000" FontFamily="Segoe WP SemiLight" FontSize="21.333" Padding="0,4" LineHeight="2.667" />
</StackPanel>
<Ellipse x:Name="RightArrow" Visibility="{Binding HasSubItems, Converter={StaticResource BoolVisibilityConverter}}" Fill="#FF0202EA" Stroke="Black" HorizontalAlignment="Right" Width="44" Height="44"/>
</Grid>
</StackPanel>
</DataTemplate>
From what I see the problem is that you are applying the storyboard to the whole LongListSelector control instead of the single item. Your VisualStateManager should be placed into a ItemTemplate (DataTemplate).
Unluckily it seems that no hooks are provided from the box, so you'll have to manually manage your item state using VisualStateManager.GoToState method.
First of all you should remove the storyboard assigned to the Selected state of your LongListSelector as this part affects the whole list.
Then you should create simple control with two visual states: Normal and Selected.
<UserControl x:Class="PhoneApp2.CustomLongListSelectorItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Duration="0" To="Red"
Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="ContentTextBlock" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Margin="12,12">
<TextBlock x:Name="ContentTextBlock" Text="{Binding}" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</UserControl>
Then add this control to your DataTemplate as below:
<phone:LongListSelector x:Name="ListSelector" SelectionChanged="HandleSelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<phoneApp2:CustomLongListSelectorItem/>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
And finally you will have to add little code to the code-behind of your page that will manage the states of your custom control.
Basically you need to implement the HandleSelectionChanged method and GetItemsRecursive as a little helper to easily get control`s children.
private void HandleSelectionChanged(Object sender, SelectionChangedEventArgs e)
{
var userControlList = new List<CustomLongListSelectorItem>();
GetItemsRecursive(ListSelector, ref userControlList);
// Selected.
if (e.AddedItems.Count > 0 && e.AddedItems[0] != null)
{
foreach (var userControl in userControlList)
{
if (e.AddedItems[0].Equals(userControl.DataContext))
{
VisualStateManager.GoToState(userControl, "Selected", true);
}
}
}
// Unselected.
if (e.RemovedItems.Count > 0 && e.RemovedItems[0] != null)
{
foreach (var userControl in userControlList)
{
if (e.RemovedItems[0].Equals(userControl.DataContext))
{
VisualStateManager.GoToState(userControl, "Normal", true);
}
}
}
}
public static void GetItemsRecursive<T>(DependencyObject parents, ref List<T> objectList) where T : DependencyObject
{
var childrenCount = VisualTreeHelper.GetChildrenCount(parents);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parents, i);
if (child is T)
{
objectList.Add(child as T);
}
GetItemsRecursive(child, ref objectList);
}
}
The code provided above is mostly from this sample Highlight a selected item in the LongListSelector on WP8. It might be slightly different from the sample as I wanted to make sure this will work properly.
This question has been asked before but in most cases no more recent than 2 years ago and often specific to WPF. The answer might still be there same, but here it goes. I'm trying to build a triangular (arrow) button that changes color and grows in size when the mouse is over it. I've got that working for a single button. But now I need buttons with the arrow pointing different directions. I want to reuse as much of the code as possible. Without using a custom button control, I couldn't think of a way to use the same style completely, so I'm trying to just reuse the mouse over animation by making it a resource. When I reference the Storyboard as a StaticResource in the VisualStateManager of my button template, it makes my button disappear completely. Why doesn't this work?
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:System="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d"
x:Class="SilverlightTest.MainPage"
Width="640" Height="480">
<UserControl.Resources>
<Storyboard x:Key="ArrowMouseOver">
<DoubleAnimation Duration="0:0:0.165" To="1.25" Storyboard.TargetProperty="(UiElement.RenderTransform).(ScaleTransform.ScaleX)" Storyboard.TargetName="polygon"/>
<DoubleAnimation Duration="0:0:0.165" To="1.25" Storyboard.TargetProperty="(UiElement.RenderTransform).(ScaleTransform.ScaleY)" Storyboard.TargetName="polygon"/>
<ColorAnimation Duration="0:0:0.165" To="#FF9BD6FF" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
<ColorAnimation Duration="0:0:0.165" To="#FF70ACDF" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
<ColorAnimation Duration="0:0:0.165" To="#FF7DAEFF" Storyboard.TargetProperty="(Shape.Stroke).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
<ColorAnimation Duration="0:0:0.165" To="#FF2B5CB4" Storyboard.TargetProperty="(Shape.Stroke).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
</Storyboard>
<Style x:Key="LeftArrow" TargetType="Button">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="grdRoot" RenderTransformOrigin="0.5,0.5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition From="MouseOver" GeneratedDuration="0:0:0.165"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver" Storyboard="{StaticResource ArrowMouseOver}">
</VisualState>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Polygon x:Name="polygon" Grid.Row="0" Margin="1" StrokeThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<Polygon.Points>
<Point X="10"/>
<Point X="0" Y="5" />
<Point Y="10" X="10" />
</Polygon.Points>
<Polygon.RenderTransform>
<ScaleTransform />
</Polygon.RenderTransform>
<Polygon.Fill>
<LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
<GradientStop Color="#FFA9A9A9"/>
<GradientStop Color="#FFD3D3D3" Offset="1"/>
</LinearGradientBrush>
</Polygon.Fill>
<Polygon.Stroke>
<LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
<GradientStop Color="#FF696969"/>
<GradientStop Color="#FF939393" Offset="1"/>
</LinearGradientBrush>
</Polygon.Stroke>
</Polygon>
<ContentPresenter Grid.Row="1" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button Style="{StaticResource LeftArrow}" HorizontalAlignment="Left" VerticalAlignment="Top">
</Button>
</Grid>
This sounds like you should introduce your own Button class.
I would do it like so:
<my:GlowingArrowButton ArrowDirection="Left"/>
And your generic.xaml:
<Style TargetType="my:GlowingArrowButton">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:GlowingArrowButton">
<Grid x:Name="grdRoot" RenderTransformOrigin="0.5,0.5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition From="MouseOver" GeneratedDuration="0:0:0.165"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"
<Storyboard>
<DoubleAnimation Duration="0:0:0.165" To="1.25" Storyboard.TargetProperty="(UiElement.RenderTransform).(ScaleTransform.ScaleX)" Storyboard.TargetName="polygon"/>
<DoubleAnimation Duration="0:0:0.165" To="1.25" Storyboard.TargetProperty="(UiElement.RenderTransform).(ScaleTransform.ScaleY)" Storyboard.TargetName="polygon"/>
<ColorAnimation Duration="0:0:0.165" To="#FF9BD6FF" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
<ColorAnimation Duration="0:0:0.165" To="#FF70ACDF" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
<ColorAnimation Duration="0:0:0.165" To="#FF7DAEFF" Storyboard.TargetProperty="(Shape.Stroke).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
<ColorAnimation Duration="0:0:0.165" To="#FF2B5CB4" Storyboard.TargetProperty="(Shape.Stroke).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="polygon" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<LayoutTransformer
LayoutTransform="{Binding Path=ArrowDirection,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource RotationTranslator_ToBeImplemented}}"
Grid.Row="0"
HorizontalAlignment="Center">
<Polygon
x:Name="polygon"
Margin="1"
StrokeThickness="{TemplateBinding BorderThickness}"
RenderTransformOrigin="0.5,0.5">
<Polygon.Points>
<Point X="10"/>
<Point X="0" Y="5" />
<Point Y="10" X="10" />
</Polygon.Points>
<Polygon.RenderTransform>
<ScaleTransform />
</Polygon.RenderTransform>
<Polygon.Fill>
<LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
<GradientStop Color="#FFA9A9A9"/>
<GradientStop Color="#FFD3D3D3" Offset="1"/>
</LinearGradientBrush>
</Polygon.Fill>
<Polygon.Stroke>
<LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
<GradientStop Color="#FF696969"/>
<GradientStop Color="#FF939393" Offset="1"/>
</LinearGradientBrush>
</Polygon.Stroke>
</Polygon>
</LayoutTransformer>
<ContentPresenter
Grid.Row="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And code:
public class GlowingArrowButton : ButtonBase
{
public GlowingArrowButton()
{
DefaultStyleKey = typeof (GlowingArrowButton);
}
public ArrowDirection ArrowDirection
{
get { return (ArrowDirection) GetValue( ArrowDirectionProperty ); }
set { SetValue( ArrowDirectionProperty, value ); }
}
public static readonly DependencyProperty ArrowDirectionProperty =
DependencyProperty.Register( "ArrowDirection", typeof( ArrowDirection ), typeof( GlowingArrowButton ), new PropertyMetadata( default( ArrowDirection ) ) );
}
public enum ArrowDirection
{
Left,
Up,
Right,
Down
}
[Edit]
Written but untested:
public class RotationTranslator : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var arrowDirection = (ArrowDirection) value;
switch (arrowDirection)
{
case ArrowDirection.Left: return new RotateTransform{ Angle = 0 };
case ArrowDirection.Up: return new RotateTransform { Angle = 90 };
case ArrowDirection.Right: return new RotateTransform { Angle = 180 };
case ArrowDirection.Down: return new RotateTransform { Angle = -90 };
}
throw new InvalidOperationException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){throw new NotSupportedException();}
}
Storyboard requires the DynamicResource markup extension and "Silverlight does not support dynamic resources." Strewth.