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.
Related
I have a problem concerning a CollectionView with a GridItemsLayout. In my application, I have a StackLayout with a horizontal CollectionView(GridItemsLayout), which contains a Button bound to a certain category.
Whenever a button is clicked/tapped, it filters the ListView (of type Surgery) below based on the category. All of that is working fine, however, I would like to highlight the Category/Button by changing it BackgroundColor to see which category is currently used for filtering.
<CollectionView HeightRequest="70"
x:Name="categoryCollection"
ItemsSource="{Binding Categories}"
Margin="20,0,20,0"
SelectionMode="Single">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal" Span="1" HorizontalItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0,5,0,5">
<Button x:Name="categoryButton" Text="{Binding Name}"
Command="{Binding Path=BindingContext.FilterCommand, Source={x:Reference Name=SurgeryListView}}"
CommandParameter="{Binding .}"
CornerRadius="15"
FontFamily="OpenSans" Margin="0,5,10,5"
Opacity="0.6" FontSize="16" TextTransform="None">
</Button>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ListView x:Name="listView"
ItemsSource="{Binding Surgeries}"
RefreshCommand="{Binding LoadSurgeriesCommand}"
IsRefreshing="{Binding IsRefreshing}"
IsPullToRefreshEnabled="True"
RowHeight="70"
BackgroundColor="{StaticResource darkThemeBackground}"
ItemTemplate="{StaticResource surgeryDataTemplateSelector}">
<ListView.Behaviors>
<behaviors:EventToCommandBehavior
EventName="ItemTapped"
Command="{Binding SurgerySelectedCommand}"
EventArgsConverter="{StaticResource ItemTappedConverter}">
</behaviors:EventToCommandBehavior>
</ListView.Behaviors>
</ListView>
I tried to use VisualStates but that was not working, because tapping the button does not actually change the SelectedItem (one would need to click the surrounding/parent grid element for that). Moreover, the altered VisualState was only applied to the Grid's BackgroundColor, not to that of the actual button.
Question: How can I highlight the current Category/Button by changing its Background Color?
Since the selection is working good, remains only the ui/xaml part, if you look at the documentation about VisualStateManager, you can add the following style:
<CollectionView.Resources>
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="LightSkyBlue" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</CollectionView.Resources>
I would like to create the segmented control in the image below.
What i currently have or attempted using is the library in the following link : https://github.com/1iveowl/Plugin.SegmentedControl
How ever as you can see the final result ends up being a horizontal segmented UI, which is what I do not want.
I have checked the documentation of the plugin to see if there is a way of changing the orientation and it seems that is the current limitation of the plugin
<control:SegmentedControl
x:Name="SegmentedGenderControl"
TintColor="#F2EBF9"
SelectedTextColor="#6F1AC1"
TextColor="Black"
DisabledColor="White"
BorderColor="#6F1AC1"
BorderWidth="1.0"
FontSize="Medium"
Margin="8,8,8,8">
<control:SegmentedControl.Children >
<control:SegmentedControlOption Text="Male"/>
<control:SegmentedControlOption Text="Female"/>
<control:SegmentedControlOption Text="Female"/>
</control:SegmentedControl.Children>
</control:SegmentedControl>
The second alternative that I have thought about is using a grid with 3 rows :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>
And then manually handle the selection based on the selection. Is there a simpler or plugin that is available to the public that is not the one above that I can use ?
So I finally managed to solve this problem as suggested by an external party through the use of a collection view
See the code that follows :
<CollectionView
HeightRequest="250"
x:Name="OptionsCollectionView"
ItemsSource="{Binding SelectionOptions}"
VerticalOptions="Start"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<yummy:PancakeView
x:Name="optionPancake"
Padding="20">
<yummy:PancakeView.Border>
<yummy:Border
Color="{StaticResource CollectionViewBorderColor}"
Thickness="2" />
</yummy:PancakeView.Border>
<StackLayout
Orientation="Horizontal">
<Label
x:Name="optionLabel"
Text="{Binding Option}"
FontSize="15"
FontFamily="EuclidCircularASemibold"
TextColor="{StaticResource SubHeadingColor}"
FontAttributes="Bold" />
</StackLayout>
</yummy:PancakeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The code that follows is for styling the visual state group, when an item is selected:
<Style TargetType="yummy:PancakeView">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState Name="Normal"/>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="{StaticResource FrameSelectedColor}"/>
<Setter
Property="yummy:PancakeView.Border"
Value="{yummy:BorderMarkup Color={StaticResource SelectedLabelColor}, Thickness='2'}"/>
<Setter TargetName="optionLabel"
Property="Label.TextColor"
Value="{StaticResource SelectedLabelColor}"/>
<Setter Property="CornerRadius"
Value="5"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
I want to highlight current page by changing background color of a flyoutitem, but I also need to change text color inside a frame. My Template
<Grid ...>
<Frame CornerRadius="10"
Padding="0"
BackgroundColor="White"
Grid.Column="1">
<Label Text="{Binding Title}"
Margin="50,0,0,0"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center"
TextColor="Black"
Grid.Column="1"/>
</Frame>
</Grid>
I need to change Frame color to darker green and label text color to white whenever I navigate to a certain page. I've tried visual state manager, but it doesn't work even just with background-only. I found a solution to attach trigger on a Frame, but nothing happens
protected override void Invoke(Frame sender)
{
sender.BackgroundColor = Color.FromHex("#229904");
(sender.Content as Label).TextColor = Color.White;
}
Firstly, you need to add a custom style in your resources:
<Style x:Key="FloutItemStyle" TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="xxx"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
Then consume it in your Shell:
<Shell.ItemTemplate>
<DataTemplate >
<Grid Style="{StaticResource FloutItemStyle}">
//...
</Grid>
</DataTemplate>
</Shell.ItemTemplate>
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.
I've been struggeling with this a few days now and can't get it to work.
Maybe I'm not as good XAML programmer that I hoped I would be :)
Anyhow, my problem is that i want to bind a number of elements to a GridView and make them appear as squares without setting any width and height. The reason for this is that I want my GridView items to grow/shrink and expand to maximum size as the resolution or screen size vary.
Here is my XAML:
<UserControl.Resources>
<Style x:Key="MyItemContainerStyle" TargetType="ListViewItem">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<!--<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Self}, Path=Width}" />-->
</Style>
<DataTemplate x:Key="MyItemTemplate">
<Border CornerRadius="4"
BorderBrush="Black"
BorderThickness="1"
Background="Blue">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">X</TextBlock>
</Border>
</DataTemplate>
<ItemsPanelTemplate x:Key="MyItemsPanelTemplate">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid Background="White">
<GridView x:Name="MyGrid"
UseLayoutRounding="True"
ItemTemplate="{StaticResource MyItemTemplate}"
ItemsPanel="{StaticResource MyItemsPanelTemplate}"
ItemContainerStyle="{StaticResource MyItemContainerStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
</GridView>
</Grid>
And this is my code-behind:
public sealed partial class BlankPage : Page
{
public BlankPage()
{
this.InitializeComponent();
MyGrid.Items.Add(new ListViewItem { Content = 1 });
MyGrid.Items.Add(new ListViewItem { Content = 2 });
MyGrid.Items.Add(new ListViewItem { Content = 3 });
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
This however produces an output like this (rectangular items, not squares):
I would appreciate if someone who knows a bit more about XAML and WinRT (metro) development than I do, could explain this for me and maybe give me a working example.
Thanx!
EDIT
I got a tip to wrap my Border in a Viewbox as it seems to have some scaling/stretching abilities.
I played around a couple of hours but I can't really get it to work 100%.
This is my XAML now:
<UserControl.Resources>
<Style x:Key="MyItemContainerStyle" TargetType="ListViewItem">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="MyItemTemplate">
<Viewbox>
<Border CornerRadius="3"
BorderBrush="Black"
BorderThickness="1">
<Grid Background="Blue" MinHeight="50" MinWidth="50">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">X</TextBlock>
</Grid>
</Border>
</Viewbox>
</DataTemplate>
<ItemsPanelTemplate x:Key="MyItemsPanelTemplate">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
</StackPanel>
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid Background="White">
<GridView x:Name="MyGrid"
UseLayoutRounding="True"
ItemTemplate="{StaticResource MyItemTemplate}"
ItemsPanel="{StaticResource MyItemsPanelTemplate}"
ItemContainerStyle="{StaticResource MyItemContainerStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled">
</GridView>
</Grid>
This produces this output:
Now it seems to stretch itself to a sqaure, but it sretches itself outside the screen. I also ran this example in several resoultions and screen sizes and it shows the same output, which means it scales correctly.
Help would be appreciated.
Thanx!
Your commented out binding is close to something that could sort of work - only you would need to bind to ActualWidth:
<Setter Property="Height" Value="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" />
Overall I would suggest using hardcoded values - they make things easier on the CPU to layout, easier to deterministically design and will get scaled by Windows when screen size and resolution will require that. If you want more control over that - you can bind both Width and Height to the same value from a view model that you change depending on the need or write a converter that will convert a hardcoded Width/Height value to actual value depending on detected screen size/resolution or settings.