My requirement: when tapped on the border in ListViewItem(DataTemplate) an overlay should appear on top of it with animation.
In my listview data template I defined a visual state at the root. I want to goto the visual state when user taps on the border. I have tried following xaml but it is not working
<DataTemplate x:Key="MyTemplate">
<Grid Background="{Binding ItemBackground}"
Margin="0,0,0,5" Height="auto">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup >
<VisualState x:Name="OverlayState">
<Storyboard >
<DoubleAnimation Storyboard.TargetName="checkedBorder"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="1" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Height="auto" Margin="14,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="55"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<!--Image Section" -->
<Grid Height="108" Margin="0,8,0,0" VerticalAlignment="Top">
<Grid Margin="0">
<Border HorizontalAlignment="Left" Margin="0" VerticalAlignment="Bottom" Width="98" Height="98" CacheMode="BitmapCache" CornerRadius="30">
<Border.Background>
<ImageBrush ImageSource="{Binding ImageSource}" Stretch="Fill"></ImageBrush>
</Border.Background>
<i:Interaction.Behaviors>
<icore:EventTriggerBehavior EventName="Tapped">
<icore:GoToStateAction StateName="OverlayState" TargetObject="{Binding ElementName=checkedBorder}"></icore:GoToStateAction>
</icore:EventTriggerBehavior>
</i:Interaction.Behaviors>
</Border>
<!-- Overlay Border -->
<Border HorizontalAlignment="Left" Opacity="0" x:Name="checkedBorder" Margin="0" Background="#99000000" VerticalAlignment="Bottom"
Width="98" Height="98" CacheMode="BitmapCache" CornerRadius="30">
</Border>
</Grid>
</Grid>
</Grid>
</Grid>
</DataTemplate>
This question is more complex than I initially thought.
First, let's correct a few issues in your xaml code.
The following line needs to be removed. It's like saying 'go find a state named OverlayState on the checkedBorder element'. Clearly that state is not on that element. Also, changing the ElementName binding to point to the top level Grid (where the visual state group is) won't work either, I will explain why later.
TargetObject="{Binding ElementName=checkedBorder}"
You should always give your VisualStateGroup a name, like this -
<VisualStateGroup x:Name="MyStates">
You need to set IsHitTestVisible to False on your overlay Border, otherwise the Border with image background will not be able to receive the Tapped event because it's sitting behind the overlay one.
I thought it should be good to go. However, if you run the app now, you will get an unhandled exception saying
Target does not define any VisualStateGroups.
If you add back the TargetObject ElementName binding and change it to point to the top level Grid, the exception will go away but the visual state is not called.
TargetObject="{Binding ElementName=myFirstLevelGridWhereTheVSGroupIs}"
This is because a normal VisualStateManager only works for Controls not FrameworkElements since your Grid is not a type of Control.
In a WP Silverlight app, it's fairly simple to fix. All you need to do is to add a built-in CustomVisualStateManager which allows you to pass in a FrameworkElement on top of the VisualStateGroups definition. But such class doesn't exist in WinRT.
I asked a question about this a while ago and eventually came up with an answer.
Include this class into your project, and add the reference in your xaml code, just before your VisualStateGroups definition.
<VisualStateManager.CustomVisualStateManager>
<local:ExtendedVisualStateManager />
</VisualStateManager.CustomVisualStateManager>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="MyStates">
<VisualState x:Name="OverlayState">
Lastly, because unlike the GoToStateAction in Silverlight app, the WinRT's GoToStateAction doesn't handle CustomVisualStateManager, you will have to create a custom GoToStateAction too.
public class ExtendedGoToStateAction : DependencyObject, IAction
{
public string StateName
{
get { return (string)GetValue(StateNameProperty); }
set { SetValue(StateNameProperty, value); }
}
public static readonly DependencyProperty StateNameProperty =
DependencyProperty.Register("StateName", typeof(string), typeof(ExtendedGoToStateAction), new PropertyMetadata(string.Empty));
public bool UseTransitions
{
get { return (bool)GetValue(UseTransitionsProperty); }
set { SetValue(UseTransitionsProperty, value); }
}
public static readonly DependencyProperty UseTransitionsProperty =
DependencyProperty.Register("UseTransitions", typeof(bool), typeof(ExtendedGoToStateAction), new PropertyMetadata(false));
public object Execute(object sender, object parameter)
{
return ExtendedVisualStateManager.GoToElementState((FrameworkElement)sender, this.StateName, this.UseTransitions);
}
}
And replace the built-in one with this.
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="Tapped">
<local:ExtendedGoToStateAction StateName="OverlayState"/>
</Core:EventTriggerBehavior>
You should be able to see the animation working now. :)
Related
I have a basic C# UWP app running on the desktop. The app's layout is similar to Word Mobile, i.e. it has a main menu with a command bar below to apply various commands.
The default CommandBar has primary commands (displayed as icons) and secondary commands shown in the overflow menu.
The Word Mobile app uses a special command bar with "groups" of command buttons. Each group has their own overflow menu should the window size be too small to show all commands (see screenshots below).
Is there a way to get these "grouped" commands with their own overflow menu using standard XAML controls? If not, what would be a strategy to implement a custom control like this?
Example:
(1) Wide window: CommandBar shows all command buttons:
(2) Small window: two separate overflow menu buttons:
Sorry about the delay but I thought I would post a proof of concept answer.
For this example I have created 7 action AppBarButton in a CommandBar including the following
2 Buttons that are independent - AllAppsButton, CalculatorButton
2 Buttons that are part of the CameraGroup - CameraButton, AttachCameraButton
3 Buttons that are part of the FontGroup - BoldButton, FontButton, ItalicButton
The idea is that if the screen size is less than 501 pixels I use VisualStateManager and AdaptiveTriggers to show the Group Chevron Buttons instead of the action Buttons. The action Buttons are then shown by clicking the down chevron which opens up a relevant Popup control.
The Popups are hidden again using AdaptiveTriggers if the screen is increased to 501 pixels or above.
MainPage.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CameraGroupStates">
<VisualState x:Name="Default">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="501" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="CameraButton.Visibility" Value="Visible"/>
<Setter Target="AttachCameraButton.Visibility" Value="Visible" />
<Setter Target="CameraGroupButton.Visibility" Value="Collapsed" />
<Setter Target="CameraGroupPopup.IsOpen" Value="false" />
<Setter Target="FontButton.Visibility" Value="Visible"/>
<Setter Target="BoldButton.Visibility" Value="Visible" />
<Setter Target="ItalicButton.Visibility" Value="Visible" />
<Setter Target="FontGroupButton.Visibility" Value="Collapsed" />
<Setter Target="FontGroupPopup.IsOpen" Value="false" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Minimal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="CameraButton.Visibility" Value="Collapsed"/>
<Setter Target="AttachCameraButton.Visibility" Value="Collapsed" />
<Setter Target="CameraGroupButton.Visibility" Value="Visible" />
<Setter Target="FontButton.Visibility" Value="Collapsed"/>
<Setter Target="BoldButton.Visibility" Value="Collapsed" />
<Setter Target="ItalicButton.Visibility" Value="Collapsed" />
<Setter Target="FontGroupButton.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<CommandBar x:Name="MainCommandBar" Height="50">
<AppBarButton x:Name="AllAppsButton" Icon="AllApps"></AppBarButton>
<AppBarButton x:Name="CameraButton" Icon="Camera"></AppBarButton>
<AppBarButton x:Name="AttachCameraButton" Icon="AttachCamera"></AppBarButton>
<!--This is the Group Chevron button for Camera-->
<AppBarButton x:Name="CameraGroupButton" Visibility="Collapsed" Content=""
Click="CameraGroupButton_Click">
<AppBarButton.ContentTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Content}"
FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Center"></TextBlock>
</Grid>
</DataTemplate>
</AppBarButton.ContentTemplate>
</AppBarButton>
<AppBarButton x:Name="CaluclatorButton" Icon="Calculator"></AppBarButton>
<AppBarButton x:Name="BoldButton" Icon="Bold"></AppBarButton>
<AppBarButton x:Name="FontButton" Icon="Font"></AppBarButton>
<AppBarButton x:Name="ItalicButton" Icon="Italic"></AppBarButton>
<!--This is the Group Chevron button for Fonts-->
<AppBarButton x:Name="FontGroupButton" Visibility="Collapsed" Content=""
Click="FontGroupButton_Click">
<AppBarButton.ContentTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Content}"
FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Center"></TextBlock>
</Grid>
</DataTemplate>
</AppBarButton.ContentTemplate>
</AppBarButton>
</CommandBar>
<Popup x:Name="CameraGroupPopup" HorizontalAlignment="Right" HorizontalOffset="-120"
VerticalOffset="50" IsOpen="False">
<StackPanel Orientation="Horizontal" Width="120" Height="60" Background="LightGray"
HorizontalAlignment="Center">
<AppBarButton x:Name="CameraGroupCameraButton" Icon="Camera" Width="50" Height="50"
Foreground="Black"/>
<AppBarButton x:Name="CameraGroupAttachCameraButton" Icon="AttachCamera"
Foreground="Black" Width="50" Height="50"></AppBarButton>
</StackPanel>
</Popup>
<Popup x:Name="FontGroupPopup" HorizontalAlignment="Right" HorizontalOffset="-170"
VerticalOffset="50" IsOpen="False">
<StackPanel Orientation="Horizontal" Width="170" Height="60" Background="LightGray"
HorizontalAlignment="Center">
<AppBarButton x:Name="FontGroupBoldButton" Icon="Bold" Width="50" Height="50"
Foreground="Black"/>
<AppBarButton x:Name="FontGroupFontButton" Icon="Font"
Foreground="Black" Width="50" Height="50"></AppBarButton>
<AppBarButton x:Name="FontGroupItalicButton" Icon="Italic"
Foreground="Black" Width="50" Height="50"></AppBarButton>
</StackPanel>
</Popup>
</Grid>
Code behind contains 2 fields and 2 events used to hide/show relevant Popups, change chevrons to up/down arrow.
public sealed partial class MainPage : Page
{
public bool CameraGroupIsOpen { get; set; } = false;
public bool FontGroupIsOpen { get; set; } = false;
public MainPage()
{
this.InitializeComponent();
}
private void CameraGroupButton_Click(object sender, RoutedEventArgs e)
{
if (CameraGroupIsOpen)
{
CameraGroupPopup.IsOpen = false;
CameraGroupButton.Content = "\uE019";
CameraGroupIsOpen = false;
}
else
{
CameraGroupPopup.IsOpen = true;
CameraGroupButton.Content = "\uE018";
CameraGroupIsOpen = true;
}
}
private void FontGroupButton_Click(object sender, RoutedEventArgs e)
{
if (FontGroupIsOpen)
{
FontGroupPopup.IsOpen = false;
FontGroupButton.Content = "\uE019";
FontGroupIsOpen = false;
}
else
{
FontGroupPopup.IsOpen = true;
FontGroupButton.Content = "\uE018";
FontGroupIsOpen = true;
}
}
}
Quite a bit of code that could no doubt be vastly improved as a CustomControl but wanted to show the idea. Hope it helps
It's probably not super complicated, but it can be a lot of work nevertheless. It probably derives from the the ribbon where there are multiple groups with item collapse and drop priorities (I think) and items get dropped in priority order.
You would need to have a container panel that would read priorities from the groups included in it and in MeasureOverride - it would drive collapsing items in the groups when space pressure happens.
The ToolStrip control in my toolkit has code to move items between the toolstrip and the overflow dropdown (check sample here) and could be a good first step of implementing support for ribbon-like collapsing.
IIRC the official sample for CommandBar also has logic implemented to move items between PrimaryCommands and SecondaryCommands which you could look into.
In a UWP app, I have a property that returns 3 values. I want to show a different grid based on this value using a converter. What is the best way to a achieve this? The direction I think I am going to head towards is to create 3 different grid templates, and then set the style to one of these 3 templates based on what the converter returns. Does anyone know if this will work? My grid doesn have to be a grid, it can be a contentcontrol or something like that. I basically want to show a different section of UI based on a property
Thanks
I would use the WindowsStateTriggers NuGet package.
Once installed you can reference at the top of your xaml
xmlns:triggers="using:WindowsStateTriggers"
and say you had a property in your Backend class called IsBusy
public bool IsBusy { get; set; } = false;
and for example you had 2 simple Grids in your xaml.
<StackPanel x:Name="root">
<Grid x:Name="RedGrid" Background="Red" Width="200" Height="100" />
<Grid x:Name="GreenGrid" Background="Green" Width="200" Height="100" Visibility="Collapsed"/>
</StackPanel>
You could setup a Visual State Group to show the Grids based on the IsBusy property. We want the Green Grid to be visible and the Red Grid to be collapsed when IsBusy = true
<StackPanel x:Name="root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="IsBusy">
<VisualState.StateTriggers>
<triggers:EqualsStateTrigger EqualTo="True" Value="{Binding IsBusy, ElementName=root}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RedGrid.Visibilty" Value="Collapsed"/>
<Setter Target="GreenGrid.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="RedGrid" Background="Red" Width="200" Height="100" />
<Grid x:Name="GreenGrid" Background="Green" Width="200" Height="100" />
</StackPanel>
NB The {Binding IsBusy, ElementName=root} depends on your DataContext and location of IsBusy property. Here its just in the code behind for the page.
Hope that gives you an idea.
Given the following code, I would have expected the red and green boxes to end up next to eachother, but as you can see in the screenshot of the result, they do not. Instead, the grid rows are sized to accommodate their full size, even though there is a render transform that scales them to half their height.
Is there a way to make the grid rows actually resize themselves and adjust to their contents?
I want this because I couldn't animate the heights of the rows, so I want to animate the heights of their contents instead.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Height="300"
Grid.Row="0"
Background="Red">
<Border.RenderTransform>
<ScaleTransform ScaleY="0.5" />
</Border.RenderTransform>
</Border>
<Border Height="200"
Grid.Row="1"
Background="Green">
<Border.RenderTransform>
<ScaleTransform ScaleY=".5" />
</Border.RenderTransform>
</Border>
</Grid>
As Sheridan says, a RenderTransform is just moving stuff around - independent of other elements. The neat thing is that it's fully hardware accelerated.
To have the system perform animations AND affect layout is a bit more tricky. You probably want to aim for not doing a pure resize, but instead use render transforms to have one element move on top of another, hence hiding it.
But, if you really do want a true resize of content, here's a way to do it.
First, I've added a row with height * at the bottom to allow your Auto rows to just use the size they need.
Secondly, I've created an animation (using Blend of course :) ) - and named the key frames in it, to be able to access them from code behind.
Lastly I modify the animation in the .cs file, and run the animation - voila! Note, this will not be hardware accelerated, but should work for simple UIs.
Code can also be found at: https://github.com/andyhammar/Wp81ResizeRowsTestApp
.xaml
<Page.Resources>
<Storyboard x:Name="AnimateRed">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="_redBorder">
<EasingDoubleKeyFrame KeyTime="0" x:Name="redAnimationFromKeyFrame" Value="300"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" x:Name="redAnimationToKeyFrame" Value="200">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
x:Name="_redBorder"
Height="300"
Grid.Row="0"
Background="Red">
</Border>
<Border
x:Name="_greenBorder"
Height="200"
Grid.Row="1"
Background="Green">
</Border>
<StackPanel
Grid.Row="2"
VerticalAlignment="Bottom"
HorizontalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="redSmallButton"
Content="red small"
Click="RedSmallButton_OnClick"/>
<Button
x:Name="redLargeButton"
Content="red large"
Click="RedLargeButton_OnClick"/>
</StackPanel>
</Grid>
.xaml.cs
private void RedSmallButton_OnClick(object sender, RoutedEventArgs e)
{
redAnimationFromKeyFrame.Value = 300;
redAnimationToKeyFrame.Value = 200;
AnimateRed.Begin();
}
private void RedLargeButton_OnClick(object sender, RoutedEventArgs e)
{
redAnimationFromKeyFrame.Value = 200;
redAnimationToKeyFrame.Value = 300;
AnimateRed.Begin();
}
They do, but I'd suggest that you use the LayoutTransform Property instead. The difference is that LayoutTransform is performed before the Arrange and Measure methods are called, so the new size is taken into consideration and RenderTransform is performed afterwards, so it ignores any dimension changes.
You can find a full explanation of the differences in the LayoutTransform vs. RenderTransform - What's the Difference? page on the Scott Logic website.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Height="300"
Grid.Row="0"
Background="Red">
<Border.LayoutTransform>
<ScaleTransform ScaleY="0.5" />
</Border.LayoutTransform>
</Border>
<Border Height="200"
Grid.Row="1"
Background="Green">
<Border.LayoutTransform>
<ScaleTransform ScaleY=".5" />
</Border.LayoutTransform>
</Border>
</Grid>
I see many win store apps have this but I couldn't figure out how it's done. Where basically I have TextBlock and it is pressable, by pressing on it something happens. How do you achieve this? Below is what I saw but it doesn't work.
<Grid x:Name="TitleGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<Storyboard x:Name="TitlePressAnimation">
<PointerDownThemeAnimation Storyboard.TargetName="TitlePanel" AutoReverse="True" />
</Storyboard>
</Grid.Resources>
<StackPanel x:ConnectionId='4' x:Name="TitlePanel" Orientation="Horizontal" Margin="120,0,0,0">
<TextBlock x:Name="TitleTextBlock" Text="{Binding DisplayName}" Style="{StaticResource TitleStyle}"/>
</StackPanel>
</Grid>
open your app in blend. Click on textblock you have created and click on 'event handlers for selected item'. its located on the top righthand corner beside 'Name'.
now find a property called 'Tapped'. enter any name there such as 'tap' and doule click. this will create and open the function in c# page. Enter code which you want to be executed there.
here is the function created.
private void Tap(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
// Add code here you want to execute when textblock is clicked.
}
I am trying to get a listview to display a list of items made up of textblocks...
when the listview item is clicked i would like to show instead a list made up of textboxes...
Below is what i have come up with, it does not work.
I have two grids within the templates and was hoping to simply show and hide the grids depending on if the listview item is selected. Where have i gone wrong?
I ripped these visual states from the listview's template itself but i must admit im not sure how they work, or how they are meant to be triggered. Should there be some code behind to do this?
<ListView Grid.Row="2" ItemsSource="{Binding Lines}" HorizontalAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Name="Readonly">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding One}" Grid.Column="0"/>
<TextBlock Text="{Binding Two}" Grid.Column="1"/>
</Grid>
<Grid Name="Editing" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding One}" Grid.Column="0"/>
<TextBox Text="{Binding Two}" Grid.Column="1"/>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Editing" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Readonly" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Many thanks,
Kohan
You are setting the Storyboard Animation up outside the Items that are being rendered. The targets you are specifying are not only out of the scope of the outer page, but they potentially do not exist yet. As a result, the Storyboard cannot be setup when the page is rendered.
Here's what you want to do.
Create a user control that will represent the layout you want in your ListView item. When you define your ListView, be sure to include your UserControl in your DataTemplate, like this:
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<local:MyUserControl />
</DataTemplate>
</ListView.ItemTemplate>
</ListView.ItemsPanel>
</ListView>
Now, for the VisualStates. You need to set the states up inside the UserControl. That means a state for Edit and a state for View. A state needs to be localized like this. Think of the Button control. The states in a button are defined in each Button not some shared location.
When you are ready to change the state of one of the items, you need to wire it to your code behind. In your code behind, you need to loop through the items in your ListView and call a method you create, something like MakeStateEdit() and MakeStateView(). It will be your implementation of those methods that sets the states of the user control. The outside code just trusts it to happen.
This means you need to call VisualStateManager.GoToState(this, "Edit", true); (or whatever state you create) inside your UserControl, in the code-behind. Conversely you might set the "View" state when the MakeStateView() is called.
To iterate a ListView Items property, you need to use a technique like this (http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html). You'll find that once you start down this path, it really isn't very complicated. You might be disappointed that you can't do all of this in XAML. You can't. But it can be done!
Best of luck!
I don't know if visual state changes propagate, so maybe your solution should somehow work, but I would edit the visual states in the ListViewItem template instead (through ItemContainerStyle).