When writing a custom ControlTemplate (XAML) for a Win 8 Metro control we need to use the VisualStateManager to update the control according to VisualState transitions. I see the below sample all over MSDN, but I can't find where the VisualStateGroup "CommonStates" is documented and what other VisualStates are defined other than "PointerOver" and "Normal"? Do you have to go dig in the SDK to find the default ControlTemplate for a button? If so, where?
<ControlTemplate TargetType="Button">
<Grid >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<!--Take one half second to transition to the PointerOver state.-->
<VisualTransition To="PointerOver"
GeneratedDuration="0:0:0.5"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<!--Change the SolidColorBrush, ButtonBrush, to red when the
Pointer is over the button.-->
<VisualState x:Name="PointerOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="ButtonBrush"
Storyboard.TargetProperty="Color" To="Red" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.Background>
<SolidColorBrush x:Name="ButtonBrush" Color="Green"/>
</Grid.Background>
</Grid>
</ControlTemplate>
You can go to the design view of your xaml file and with the Button control selected - right click/Edit Template/Edit Current - will get you the default template extracted. Normally controls should be annotated with attributes that indicate which visual states should be used in the template like below, but I can't see them when I just navigate to definition of a control like Button.
[TemplateVisualState(GroupName="CommonStates", Name="Normal")]
[TemplateVisualState(GroupName="CommonStates", Name="PointerOver")]
Related
When styling controls that have a ContentPresenter (like GridViewItem), and it has a bunch of visual states like Focused, Unfocused, PointerFocused, Disabled etc. Is there a proper way to pass the active visual state down through to the DataTemplate? IOW, is there a nice way for any UI in the DataTemplate to react to the same visual states as its parent (GridViewItem as an example, but would want a solution for any ControlTemplate that uses a ContentPresenter really)? Ex)
<DataTemplate>
<UserControl>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<!-- These states are copied from the ControlTemplate,
but obviously don't work by default. -->
<VisualState x:Name="Focused" />
<VisualState x:Name="Unfocused" />
<VisualState x:Name="PointerFocused" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="foo.Fill" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="foo" />
</Grid>
</UserControl>
</DataTemplate>
My initial naive approach would be to extend ContentPresenter and add a custom property like ActiveVisualState then call VisualStateManager.GoToState((Control)contentPresentorChild, ActiveVisualState, true);. Then use that presenter in the GridViewItem template style (or whatever control I was styling). But I feel like there should be a better, more natural and not so specific, way to achieve this.
We cannot see where you put your ContentPresenter and we also cannot see where you modified your template. So it's not clear what you want.
But if your simply want to modify a property value based on different state. why not just change the status from the setter like this:
<VisualState.Setters>
<Setter Target="NameofYourContentPresenter.(UIElement.Opacity)"
Value="0.5" />
</VisualState.Setters>
For customized property you just need to specify it from your XAML.
In UWP, every time a ListViewItem is selected, a storyboard is triggered to give the user the feeling the component is reacting to the pressure of a touch. That storyboard also changes the ListViewItem's background color until it gets released.
I have designed a UserControl which uses a ListView internally but would like to override this behavior as it doesn't really fit the application's proposed design.
Bellow are the VisualStateGroups I tried to apply both to the ListView through its ControlTemplate and to ListViewItem through its DataTemplate definition as the former attempt failed.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver" />
<VisualState x:Name="Pressed" />
<VisualState x:Name="PointerOverPressed" />
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused" />
<VisualState x:Name="UnFocused" />
<VisualState x:Name="PointerFocused" />
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselecting" />
<VisualState x:Name="Unselected" />
<VisualState x:Name="UnselectedPointerOver" />
<VisualState x:Name="UnselectedSwiping" />
<VisualState x:Name="Selecting" />
<VisualState x:Name="Selected" />
<VisualState x:Name="SelectedSwiping" />
<VisualState x:Name="SelectedUnfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
One possibility could be to bind the root grid of the DataTemplate to the ItemsContainer so that I could override the default behavior every time an item was selected. But I am not that versed in XAML and couldn't figure out the proper way of doing this by myself.
This uses the ListViewItemPresenter as I am sure you have discovered. And, I am sure you have discovered that it is not template-able. The purpose of this more encapsulated approach is purely performance. As a result, items in a ListView are measurably faster to render. However, it does mean that when you want to change it, you are sort of stuck. Does it mean you cannot change it to use your own presenter and unwind the performance improvements? Does it mean you will have a lot of custom code? Yes. Does it means it is not possible? No. Does it mean it is a good idea? Well. I don't know how to answer that one. But, at least you know why it appears to be a brick wall.
It took some time, but in the end it was simpler than expected. I just had to add a ControlTemplate targeting the ListViewItem, which I didn't before as I was stuck fiddling with the ControlTemplate of the ListView.
As we do not declare the ListViewItem directly, the way is to use ListView.ItemContainerStyle. The final result is as follows:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
As you can see, an empty ListViewItemPresenter is good enough to override most of the default behavior. I couldn't get rid of the pressing animation, but I'm happy with the current results.
I'm trying to change the appearance of gridview items when they are selected.
(Before, I used a trick with an IsSelected property in the ViewModel object bound to the containing grid and a bool-to-color converter, but I recognize that it is bad)
To do so, I do:
<GridView ItemContainerStyle="{StaticResource GridViewItemContainerStyle}" ...> ...
and
<Style x:Key="GridViewItemContainerStyle" TargetType="GridViewItem">
<Setter Property="Background" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Background)" Storyboard.TargetName="itemGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="Black"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="UnselectedSwiping"/>
<VisualState x:Name="UnselectedPointerOver"/>
<VisualState x:Name="Selecting"/>
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Background)" Storyboard.TargetName="itemGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedSwiping"/>
<VisualState x:Name="Unselecting"/>
<VisualState x:Name="Unselected"/>
<VisualState x:Name="SelectedUnfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid ... x:Name="itemGrid">
<!-- HERE MY DATA TEMPLATE -->
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I run the app, the items are Black (as in the "normal" state). But selecting them does not turn them into White. Where am I wrong?
Moreover, it there a way to set "ItemContainerStyle" without having it to "overwrite" the "ItemTemplate" ???
You DataTemplate should be inside the ItemTemplate property of the GridView element in your page's XAML. Make a separate XAML file (ResourceDictionary), for example CustomStyles.xaml. Reference it in App.xaml like this:
<Application.Resources>
<!-- Application-specific resources -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="PathToCustomStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
You can find on MSDN the default template for GridViewItem (http://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj709915.aspx), under the Default style section (second, longer XAML).
Copy that and paste it into CustomStyles.xaml. Just give it some key like:
<Style TargetType="GridViewItem" x:Key="CustomGridViewItemStyleWithWhiteSelectionBackground">...
As you can see, Selected visual state changes the opacity of three targets, SelectionBackground, SelectedBorder and SelectedCheckMark. So, these elements are not visible in Normal state because their opacity is zero. Find those three elements down below, and change their properties if needed. For the background change the Fill property of the SelectionBackground rectangle:
<Rectangle x:Name="SelectionBackground"
Margin="4"
Fill="White"
Opacity="0" />
Now, when the selection occurs, this element's opacity will be changed to 1 and since you set it's Fill to be white, the background of the selected item will be white. And don't forget to reference this style in the definition of the GridView:
<GridView ItemContainerStyle="{StaticResource CustomGridViewItemStyleWithWhiteSelectionBackground}" ...>
<GridView.ItemTemplate>
<DataTemplate>
...define your template here...
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Edit:
This is the expanded style XAML, probably more suitable for some more complicated style changes. If you want to change only the background, you should take the first style from that MSDN link above under the Default style section, and just edit this (and give it some style key, so you don't overwrite the default one):
SelectedBackground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"
so from this free software, I could make myself my own metro button as seen below:
the icon is white though, so may not see it properly, and I put it in my Grid (written in XAML) here:
Still it is technically an image, so I made it into Button, here's a code of transformed image into button:
<Button x:Name="Button_CreateAccount" Content="" HorizontalAlignment="Center" Height="65" Margin="0" Style="{StaticResource Button_CreateAccount}" Width="65" Click="Button_CreateAccount_Clicked"/>
see I name it "Button_CreateAccount", add a Clicked event handler "Button_CreateAccount_Clicked", and using a custom style "{StaticResource Button_CreateAccount}"
it works as I expected, but unlike any other button, it won't blink when pressed and release the blink when released, maybe because it is technically an image. So I reckon I could programmatically make it "blinked" when being pressed by changing its style. Here's the unedited style added automatically by Blend in Visual Studio 2012:
<Style x:Key="Button_CreateAccount" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="PointerOver"/>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="PointerFocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image Source="Assets/Icons_White/add_user.png" Stretch="Fill"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
However, I do not speak XAML language :( I don't have any idea how to simply change the color of the background of the image once being pressed. Any help would be deeply appreciated, thanks!
First, you should make the image have a transparent Background and not a green background. After that do not use your style and change your button to be this
<Button x:Name="Button_CreateAccount" HorizontalAlignment="Center"
Height="65" Margin="0" Width="65" Click="Button_CreateAccount_Clicked"
Background="Green">
<Image Source="Assets/Icons_White/add_user.png" Stretch="Fill"/>
</Button>
From here you will start to see the color changing when you press. If you want to change what the color is then give the button a new style. The best way is to use Visual Studio or Blend and right click the Button (in design view or in the document outline) and select Edit Template -> Edit a copy...
Change the colors within the Pressed VisualState to change the color when the button is pressed.
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Border">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="Blue"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonPressedForegroundThemeBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
I am trying to change the rowstyle for a datagrid to change the background color when a row is selected(not the row background but the color of the selection rectangle, which by default is a bit too shiny for my application). However the behaviour i'm seeing on applying to style to the grid is that all the rows are getting shrinked together like a folded venetian blind. This is the style that i have written:
<Style x:Key="DataGridRowStyle" TargetType="sdk:DataGridRow">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="sdk:DataGridRow">
<sdk:DataGrid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal Selected">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="ButtonBrush"
Storyboard.TargetProperty="Color"
To="Red" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</sdk:DataGrid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Can someone tell me what's going on here? tbh I havn't really understood how the visual states work and kind of just shooting in the dark.
Thanks
Checking the template for the DataGrid (check here) the background color (that blue) is hard coded in the template. When the row is selected all that happens is that a rectangle becomes visible (Opacity=1). Sadly have to say that you have to re-template the whole DataGridRow... in the provided link you'll find all you might need for this.
In the template look for some code like this:
<Rectangle x:Name="BackgroundRectangle" Grid.RowSpan="2" Grid.ColumnSpan="2" Opacity="0" Fill="#FFBADDE9"/>