In my App, i am using a CommandBar with multiple AppBarButtons, on the top of the app. Now if i resize the window, i want that the AppBarButtons go to CommandBar.SecondaryButtons, if they does not fit anymor to the whole width of the window. As an example, in the default weather app, there is such an effect.
Second, i want to switch from the CommandBar on the Top, to a CommandBar on the bottom, like a CommandBar inside Page.BottomAppBar, on specific device families. I dont know, if i should set two CommandBars in my xaml and show the one, which meets the conditions, or if there is a better way. I like to do as much as possible with VisualStates, but i dont know how to achieve this.
I know these are two questions, but both points to the CommandBar, so i hope someone can help me?
Best regards
As an example, in the default weather app, there is such an effect.
You can use VisualStateManager to manages visual states and the logic for transitions between visual states for controls and use the Visibility property of the AppBarButton to show or hide it.
For example:
I add two AppBarButton in the CommandBar.PrimaryCommands and two AppBarButton in the CommandBar.SecondaryCommands. When the width of window less than 720, I set the Visibility property of the AppBarButton in CommandBar.PrimaryCommands to Collapsed. When the width of window large than 720 or equles to 720, I set the Visibility property of the AppBarButton in CommandBar.SecondaryCommands to Collapsed.
The XAML code:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PrimaryHome.Visibility" Value="Collapsed"/>
<Setter Target="PrimaryAdd.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SecondHome.Visibility" Value="Collapsed"/>
<Setter Target="SecondAdd.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
<Page.TopAppBar>
<CommandBar x:Name="TopCommands" >
<CommandBar.PrimaryCommands>
<AppBarButton Name="PrimaryHome" Icon="Home" Label="Home"/>
<AppBarButton Name="PrimaryAdd" Icon="Add" Label="Add" />
</CommandBar.PrimaryCommands>
<CommandBar.SecondaryCommands>
<AppBarButton Name="SecondHome" Icon="Home" Label="Home" />
<AppBarButton Name="SecondAdd" Icon="Add" Label="Add" />
</CommandBar.SecondaryCommands>
</CommandBar>
</Page.TopAppBar>
Second, i want to switch from the CommandBar on the Top, to a CommandBar on the bottom, like a CommandBar inside Page.BottomAppBar, on specific device families.
You can add the Page.TopAppBar and the Page.BottomAppBar in your XAML. And use VisualStateManager to manage which CommandBar should display on the
page.
For example:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="TopCommands.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BottonCommands.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
<Page.TopAppBar>
<CommandBar x:Name="TopCommands" Visibility="Collapsed" >
<CommandBar.PrimaryCommands>
<AppBarButton Name="PrimaryHome" Icon="Home" Label="Home"/>
<AppBarButton Name="PrimaryAdd" Icon="Add" Label="Add" />
</CommandBar.PrimaryCommands>
</CommandBar>
</Page.TopAppBar>
<Page.BottomAppBar>
<CommandBar x:Name="BottonCommands" Visibility="Collapsed">
<CommandBar.PrimaryCommands>
<AppBarButton Name="BottonPrimaryHome" Icon="Home" Label="Home"/>
<AppBarButton Name="BottonPrimaryAdd" Icon="Add" Label="Add" />
</CommandBar.PrimaryCommands>
</CommandBar>
</Page.BottomAppBar>
For your first question: You can setup the buttons in Primary and Secondary sections of the CommandBar. Then use VisualStates to toggle the Visibility of them depend on app's width. OR you can do it in code entirely with SizeChanged event of the page.
Second question, lets create something like
<Page>
<Grid>
<!-- row definition here -->
<!-- Row 1 -->
<!-- Row 2 -->
<!-- Content -->
<Grid Grid.Row="0"/>
<!-- app bar -->
<CommandBar Grid.Row="1"/>
</Grid>
</Page>
Change the attached property Grid.Row to the desired number using VisualStates similar to question one.
Related
I'm trying to bind my icon to a property in my appshell viewmodel. This works for the first time, and I am watching the "flyoutispresented" property change to update the icon - I'm switching between two different png's. The event fires every time, and I can see the property in the viewmodel is updating, but the flyout image is not changing. It seems to stay the way it was when first rendered. In my appshell constructor, I am doing the following:
model = new AppShellViewModel();
this.BindingContext = model;
this.PropertyChanged +=
(obj, args) => model.Shell_PropertyChanged(obj, args);
so I am using the single instance of the view model, and that method is being called properly. Has anyone had luck displaying different icons like this? What am I missing that even though I'm changing the icon property, it's not showing the change?
We could modefy the icon source via VisualStateManager status selected and normal.
<Shell.ItemTemplate>
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.8*" />
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter TargetName="FlyoutItemIcon" Property="Image.Source" Value="icon_about.png"></Setter>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter TargetName="FlyoutItemIcon" Property="Image.Source" Value="cactus_24px.png"></Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
<Image x:Name="FlyoutItemIcon" Source="{Binding FlyoutIcon}"
Margin="5"
HeightRequest="45" >
</Image>
<Label Grid.Column="1"
Text="{Binding Title}"
FontAttributes="Italic"
VerticalTextAlignment="Center" />
</Grid>
</DataTemplate>
</Shell.ItemTemplate>
Update:
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent Title="About" Icon="icon_about.png" Route="AboutPage" ContentTemplate="{DataTemplate local:AboutPage}" />
</FlyoutItem>
Im working on building different layouts for my app. If the app runs in landscape I want to be able to show a map on one side on the screen, but not show a map if in portrait. I'm using VisualStateManager to help me do this.
I need to run some code in c# on the map control I have named "MyMap" but how can I make that bit of the code run only when the map is present - in other words is there a way I can check if it exists in the XAML?
Without the map any code referring to MyMap of course throws an error - is this the best way of going about this or am I missing a better way?
EDIT
I'm using an approach similar to the below to work out what template should be used depending on the MinWindowWidth. So MyMap will only exist in the LargeTemplate etc
<Page.Resources>
<DataTemplate x:Key="SmallTemplate">
<Grid>
<Border Background="LightGray" Height="100" Width="100">
<TextBlock Text="{Binding Text}"
FontSize="48" Foreground="Green" />
</Border>
</Grid>
</DataTemplate>
<DataTemplate x:Key="LargeTemplate">
<Grid>
<Border Background="LightGray" Height="200" Width="200">
<TextBlock Text="{Binding Text}"
FontSize="48" Foreground="Green" />
</Border>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Small">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MyGridView.ItemTemplate" Value="{StaticResource SmallTemplate}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Large">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MyGridView.ItemTemplate" Value="{StaticResource LargeTemplate}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<GridView Name="MyGridView"/>
</Grid>
Add an x:Name to your VisualStateGroup eg. AdaptiveVisualStateGroup, after that you can check for the CurrentState in your code like this:
AdaptiveVisualStateGroup.CurrentState
If the CurrentState equals Large or whatever the name is your VisualState where you have your map, you can run the code that references MyMap, otherwise you just skip it
I'm trying to stretch an image inside a Button which is inside a stackpanel and a grid but it doesn't works. On mobile size it works very well but when the app is for an Desktop it doesn't work anymore, we can see that the image overflows.
And what it looks like on desktop
My code:
<StackPanel x:Name="g5" Grid.Column="1" Grid.Row="1" Padding="20" Orientation="Vertical" >
<TextBlock Text="Site" FontSize="20"/>
<Button x:Name="websiteButton" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" Background="#fc0">
<Image x:Name="websiteImage" Source="Assets/website.png" NineGrid="0" Margin="20,20,20,20"/>
</Button>
</StackPanel>
Someone can tell me what is wrong and how i can stretch the image inside the button ? Btw it's a .png file.
Thanks everyone !!
Stack Panel with Vertical orientation does not impose any limits on the Children in terms of their height. It just ensures that they are stacked one on top of another. This means, that on Desktop the Button gets large width and therefore the image grows vertically to. One way to prevent this is to set MaxHeight of the Button or of the Image inside of it.
A better solution would be to use a Grid:
<Grid x:Name="g5" Height="200" HorizontalAlignment="Stretch" Padding="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Site" FontSize="20"/>
<Button Grid.Row="1" x:Name="websiteButton" HorizontalAlignment="Stretch" Margin="0" Background="#fc0">
<Image x:Name="websiteImage" Source="/Assets/Square150x150Logo.png" NineGrid="0" Margin="20,20,20,20"/>
</Button>
</Grid>
The second row of the Grid has "*" height, which means that it will grow to fill available space. This will limit the height of the button. The default Stretch value of the image is Uniform, which will ensure the image will look good inside the button.
#Romasz and #MZetko are right. But for layout targeting different device platform, my suggestion is that you can also use VisualStateManager to set the Width and Height properties for different app window's size for example like this:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
<!--for desktop-->
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="websiteButton.Width" Value="300" />
<Setter Target="websiteButton.Height" Value="300" />
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
<!--for mobile-->
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="websiteButton.Width" Value="150" />
<Setter Target="websiteButton.Height" Value="150" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
That's kind of a long title.
So i have a Shell.xaml with a for the content .xaml's
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ShellSplitView.DisplayMode" Value="CompactInline"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ShellSplitView.DisplayMode" Value="Overlay"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<SplitView x:Name="ShellSplitView">
<SplitView.Pane>
<Grid>
<ListView x:Name="NavMenuList">
...
</ListView>
</Grid>
</SplitView.Pane>
<Frame x:Name="ContentFrame">
</Frame>
</SplitView>
</Grid>
Inside this Frame other .xaml's are loadad for the actual content. Those also use a (to add further depth, like the mail app: folder->email list->email view) but if i add a to that nested xaml it doesn't do anything.
My goal would be to have it behave like two separate frames when in small view (like phone), so the user can navigate between them (again, like the win10 mail app).
Right now the VisualStateManager of the Frame xaml looks like this, but this was just a test to see it working. As I mentioned I'd prefer a similar behaviour to the mail app.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PeopleSplitView.DisplayMode" Value="Inline"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PeopleSplitView.DisplayMode" Value="CompactInline"/>
<Setter Target="PeopleSplitView.IsPanelOpen" Value="False"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Also when resizing the Window, the content of the frame splitview content doesn't resize itself and stays full width (thus not wrapping the text).
<SplitView x:Name="PeopleSplitView"
DisplayMode="Inline"
IsPaneOpen="True"
OpenPaneLength="400"
CompactPaneLength="48">
<SplitView.Pane></SplitView.Pane>
<SplitView.Content>
<Grid Padding="0">
<ListView.../>
</Grid>
</SplitView.Content>
</SplitView>
you need to delete this element of your c# code that is found in the event of the listview that selects the frames
i think this should help you
private void ListViewSplit_Tapped(object sender,TappedRoutedEventArgs e)
{
//delete those lines
if (MySplitView.IsPaneOpen)
MySplitView.IsPaneOpen = !MySplitView.IsPaneOpen;
}
I read mostly all of this article, but I just can't find out how I can change e.g. the entrance theme transition of the MenuFlyout, like it appears in the calender app. There is something like a horizontal turn instead of the default Animation of the MenuFlyout.
<MenuFlyout>
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style...../>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem Text="Test"/>
</MenuFlyout>
C#:
MenuFlyout mf = (MenuFlyout)this.Resources["AddButtonFlyout"];
mf.Placement = FlyoutPlacementMode.Bottom;
mf.ShowAt(this.CommandBar);
The MenuFlyout has a standard Style which is set for TargetType="MenuFlyoutPresenter" and can be found in ..\Program Files (x86)\Windows Phone Kits\8.1\Include\abi\Xaml\Design\generic.xaml (I will not copy/paste here because it's quite long). This Style defines a ControlTemplate which you can modify to set how the MenuFlyout behaves when it changes to BottomPortrait VisualState.
From what I can see in the Calendar app, the MenuFlyout kind of flips when you open it. In predefined Style it first displays the top border and then draws the rest from top to bottom.
So, first of all you need to copy the whole Style to your resources. Then you need to find the BottomPortrait VisualState and clear everything from the Storyboard to be able to define your own from scratch.
I'll use a PlaneProjection class - it give that sort of a 3D effect which is what you're looking for. I added it to the CenterBorder Border element and set the default value to -90. I set it to -90 because that means it's perpendicular to the screen and the MenuFlyout is therefore not visible when first shown.
// ... rest of the code
<Border x:Name="CenterBorder" FlowDirection="LeftToRight" BorderBrush="{TemplateBinding Background}">
<Border.Projection>
<PlaneProjection RotationX="-90"/>
</Border.Projection>
// ... rest of the code
The next (and final) step is to define the new Storyboard in BottomPortrait VisualState as mentioned earlier - and it's really simple:
// ... rest of the code
<VisualState x:Name="BottomPortrait">
<Storyboard>
<DoubleAnimation Duration="0:0:0.18"
To="0"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
Storyboard.TargetName="CenterBorder" />
</Storyboard>
</VisualState>
// ... rest of the code
It just animates the Border from -90 to 0 degrees in very short period of time, which makes it go from invisible to visible with a nice flip animation, which is what you're looking for.
The Style (with irrelevant parts omitted for brevity - you should still have them!):
<Style TargetType="MenuFlyoutPresenter">
<!-- OTHER PROPERTY SETTERS -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuFlyoutPresenter">
<Border x:Name="OuterBorder" FlowDirection="LeftToRight" BorderBrush="{TemplateBinding BorderBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PlacementStates">
<VisualState x:Name="None" />
<VisualState x:Name="TopPortrait">
<!-- TOP PORTRAIT STORYBOARD -->
</VisualState>
<VisualState x:Name="BottomPortrait">
<Storyboard>
<DoubleAnimation Duration="0:0:0.18"
To="0"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
Storyboard.TargetName="CenterBorder" />
</Storyboard>
</VisualState>
<VisualState x:Name="LeftLandscape">
<!-- LEFT LANDSCAPE STORYBOARD -->
</VisualState>
<VisualState x:Name="RightLandscape">
<!-- RIGHT LANDSCAPE STORYBOARD -->
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.RenderTransform>
<ScaleTransform x:Name="OuterScaleTransform" />
</Border.RenderTransform>
<Border x:Name="CenterBorder" FlowDirection="LeftToRight" BorderBrush="{TemplateBinding Background}">
<Border.Projection>
<PlaneProjection RotationX="-90"/>
</Border.Projection>
<StackPanel x:Name="InnerBorder" FlowDirection="{TemplateBinding FlowDirection}" Background="{TemplateBinding Background}">
<StackPanel.RenderTransform>
<ScaleTransform x:Name="InnerScaleTransform" />
</StackPanel.RenderTransform>
<ItemsPresenter x:Name="ItemsPresenter" Margin="{TemplateBinding Padding}" FlowDirection="{TemplateBinding FlowDirection}" />
</StackPanel>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT:
Showing the MenuFlyout is best done on a frame.
MenuFlyout mf = (MenuFlyout)this.Resources["AddButtonFlyout"];
mf.Placement = FlyoutPlacementMode.Bottom;
Frame fr = Window.Current.Content as Frame;
mf.ShowAt(fr);