I'm porting a Wp81 app to UWP, and it seems that the Hub layout strategy changed and it's no longer adapting HubSections width to stretch it (almost) to the width of the screen. How can I achieve that in UWP without setting absolute width on HubSections?
Details about my use case:
In my HubSection, I have an Image, that instead of being shrunk to screen width (almost - so that the next section is visible) it stretches to its native width. The same happens with TextBlock. It seems that in UWP HubSection doesn't have Width or MaxWith set according to screen width.
The purpose of a hub is to allow the user to discover stuff. So the section is not stretched to full screen by default. For you case, you'd better try with Pivot control.
Here is a design guideline for Pivot and Hub.
Besides, if you still want to use Hub and want the style to adapt to different screen size, below is a simple solution by VisualStateManager:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Phone" >
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="HeroImg.Width" Value="100" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Tablet" >
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="HeroImg.Width" Value="800" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Hub Header="News" >
<HubSection Header="Hero" x:Name ="HeroImg" >
<HubSection.Background>
<ImageBrush ImageSource="Assets/circle_hero.jpg" Stretch="UniformToFill"/>
</HubSection.Background>
</HubSection>
</Hub>
</Grid>
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.
I have a ScrollViewer which Width property binds to another page element. I want to apply this only on certain AdaptiveTrigger MinWindowWidth
<ScrollViewer x:Name="scrollViewerEditor" Width="{Binding ElementName=stackPanelSearch, Path=ActualWidth}">
...
</ScrollViewer>
I want to place the Width binding in AdaptiveTrigger like the following. What's the proper way to do it?
<VisualState x:Name="VisualStateWide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="scrollViewerEditor.(FrameworkElement.Width)" Value="Binding ElementName=stackPanelSearch, Path=ActualWidth}" />
If you don't mind to do this work in the code behind, there is an easy way to solve this problem, using the VisualStateGroup.CurrentStateChanged event for example like this:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup CurrentStateChanged="VisualStateGroup_CurrentStateChanged">
<VisualState x:Name="VisualStateNarrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="scrollViewerEditor.Style" Value="{StaticResource ScrollViewerStyle1}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateWide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="scrollViewerEditor.Style" Value="{StaticResource ScrollViewerStyle2}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
In code behind:
private void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
var state = e.NewState;
if (state.Name == "VisualStateNarrow")
{
scrollViewerEditor.Width = 300;
}
else
{
if (tb.ActualWidth != 0)
scrollViewerEditor.Width = tb.ActualWidth;
else
scrollViewerEditor.Width = 600;
}
}
It is not allowed to use data binding in Setter, usually the right way to solve this problem is to create a custom AttachedProperty for your ScrollViewer and bind this attached Property to the WidthProperty, but the support for custom attached property inside the VisualState is not quite ideal, I will keep researching on this and see if it works with custom AttachedProperty.
I'm making an UAP Windows 10 App. Like app News from Microsoft, i'm trying to put the CommandBar in top on the page in desktop view, and in bottom of the page in mobile view.
How could I do this ? I think I have an independant <CommandBar xxx> with empty <Page.TopAppBar> and <Page.BottomAppBar>. And depending of the size of the view, I attach the CommandBar to TopAppBar or BottomAppBar... ?
Having ideas ? Thanks you.
You can use the VisualState class to adjust alignment of things depending on the size of the screen, using the StateTriggers property. Here's some documentation about the class including some sample code here https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.visualstate.statetriggers.aspx
This sample code demonstrates the use: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlStateTriggers
Simple implementation:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!--VisualState to be triggered when window width is >=720 effective pixels.-->
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="myPanel.VerticalAlignment" Value="Bottom" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel x:Name="myPanel" VerticalAlignment="Top">
<TextBlock Text="This is a block of text for an example. "
Style="{ThemeResource BodyTextBlockStyle}"/>
</StackPanel>
(that's this example but with the values changed for your situation. https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.visualstate.statetriggers.aspx)
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}"
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")]