Styles on buttons with children - button

I have a question about styles. In my Windows Universal application I have certain buttons. These buttons have a stackpanel as a child and the stackpanel has an image as a child. The purpose of this is that the button needs to contain an image.
Now my employer wants to ignore the theme settings of the user. So whenever a button is clicked he doesn't want the background color of the button to change. The only way I've found so far to do this is with a style. However when I use a style the button is displayed without it's children. The style I made looks like this:
<Style x:Key="Button_style" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Pressed">
</VisualState>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="PointerOver">
</VisualState>
<VisualState x:Name="Pointerleave">
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="PointerFocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This was a proof of concept. However since the children of the buttons dissapear I'm unable to go on. How would I rewrite this style to make the children of the button stay where they are supposed to be?
Here is some sample code of the button I have:
<Button Name="MyButton" Click="MyButton_Click" ClickMode="Press" HorizontalAlignment="Center" VerticalAlignment="Stretch" >
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left" >
<Image Name="MyImage" x:Uid="MyImage" Source="" VerticalAlignment="Top" HorizontalAlignment="Left" MaxWidth="30" />
</StackPanel>
</Button>

If you want to ignore user settings at all the best option will be just override PhoneAccentBrush value. You can do it in App.xaml, or use ResourceDistionary.
<Color x:Key="PhoneAccentColor">Blue</Color>
Also it is possible to request light or dark theme for your application in App.xaml:
<Application x:Class="App1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App2"
RequestedTheme="Light">
</Application>

Related

Visualize press the button action and hovering over it in MAUI.NET MVVM

I use MAUI.NET with MVVM architecture. Because of this I do not want to manage things through an events.
I want to visualize two moments:
when the user hover over the button
when the user click on the button
After these moments I want to button looks normal as nothing happened.
I just found a way to manage second moment (after the button is pressed).
What I managed to do is to set VisualState in a global ResourceDirectory for a buttons, like this:
<Style TargetType="Button" x:Key="HideButtonStyle">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="{StaticResource MediumDarkColor}" />
<Setter Property="Scale" Value="1" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="{StaticResource HideColor}" />
<Setter Property="Scale" Value="1.07" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
I tried other state like "Focused".
And the buttons seems to be normal button:
<Button Grid.Row="2" Style="{StaticResource HideButtonStyle}" Text="123" Command="{Binding GetSourceFilePathCommand}"/>
The problem is that some buttons backs to the "normal state" immediately and some not.
When the user click on something else it backs to normal, finally.
I have investigated if this is not due to async in Command, but is not.
It seems that the buttons that come back to normal state (proper in my opinion) are the buttons that fires the dialog.
I would like to that after user press the button, it got a colour etc. for a 2-3 seconds and backs automatically to normal state.
It would be fantastic if I could signalize that user hover over the button with let's say scaling it, and signalize when the user press the button with a colouring it for a moment.
There is currently no IsMouseOver detection mouseover event in maui, you can continue to pay attention to this GitHub proposal.
For clicking button to change color you can use Trigger, refer to the following code:
<Button Text="Click Me">
<Button.Triggers>
<Trigger TargetType="Button"
Property="IsFocused" Value="True">
<Setter Property="BackgroundColor" Value="Pink"/>
</Trigger>
</Button.Triggers>
</Button>
You also change the property to Scale to implement click-to-zoom functionality.
I would like to that after user press the button, it got a colour etc. for a 2-3 seconds and backs automatically to normal state.
Why not use animations then? They are pretty easy to use in MAUI, and since you are not happy with your VisualState "Pressed" approach, you can give it a try.
Example:
private async Task AnimateElement(VisualElement element)
{
isAnimating = true;
await element.FadeTo(0, 300);
await element.FadeTo(1, 300);
isAnimating = false;
}

Global styles for FontImageSource property

I have following button in my Xamarin Forms application
<Button
Grid.Column="1"
Grid.Row="0">
<Button.ImageSource>
<FontImageSource Glyph="" FontFamily="{StaticResource IconFont}" Color="{StaticResource BaseColor}"/>
</Button.ImageSource>
</Button>
where IconFont is:
<OnPlatform x:TypeArguments="x:String" x:Key="IconFont">
<On Platform="Android" Value="materialdesignicons-webfont.ttf#Material design" />
<On Platform="iOS" Value="Material Design Icons" />
</OnPlatform>
Everything is working fine. Now, because all of my icons are in materialdesignicons-webfont and I will never use different icons I decided to move entire styling to App.xaml. I would like to set default properties like that:
<Style TargetType="FontImageSource">
<Setter Property="FontFamily" Value="{StaticResource IconFont}" />
<Setter Property="Color" Value="{StaticResource BaseColor}" />
</Style>
And use button by passing glyph only.
<Button
Grid.Column="1"
Grid.Row="0">
<Button.ImageSource>
<FontImageSource Glyph=""/>
</Button.ImageSource>
</Button>
For some reasons Icon is not rendered without specifying FontFamily and Color directly in FontImageSource. Am I missing something?
Is there some way to call my button in simpler way? F.e.
<Button
Grid.Column="1"
Grid.Row="0"
MDGlyph="" />
Unfortunately it is not possible to style a FontImageSource currently. However, there is a github issue requesting this capability.
have you tried this:
<Style x:Key="ButtonWithFontImageSource" TargetType="Button">
<Setter Property="ImageSource">
<FontImageSource
FontFamily="{StaticResource FontIcons}"
Glyph="{DynamicResource IconText}">
</FontImageSource>
<Setter>
</Style>

Why is my ContentPresenter taking up more space than the content it's presenting

Now I don't know too much about control templates, but I'm trying to implement one following the article at: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/control-templates/creating
Question 1:
As far as I can tell, the ContentPresenter of a ControlTemplate simply displays exactly the content defined in the view that consumes the ControlTemplate, nothing more or less. Is my understanding correct?
Question 2 (I'll try my best to explain):
If the answer to (1) is yes, why is my ContentPresenter taking up more space than my content? See
My content is a StackLayout (green background) with Grid elements (pink background). Its dimension and position in the screenshot shown are exactly as I need. However, my ContentPresenter (red background) is expanding way beyond than its content (which is the green StackLayout).
If the answer to (1) is no, could the parent elements of the ContentPresenter (i.e, other elements in the ControlTemplate) be causing this?
Any help would be much appreciated. Thanks!
Edit 1 (Code I'm using):
The ContentPresenter in question belongs to a ControlTemplate targetting Xamarin.Forms.Platform.UWP.MasterDetailControl, as follows:
<Style TargetType="uwp:MasterDetailControl">
<Setter Property="ToolbarForeground" Value="{ThemeResource DefaultTextForegroundThemeBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="uwp:MasterDetailControl">
<SplitView PaneBackground="Transparent" x:Name="SplitView" IsPaneOpen="{Binding IsPaneOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" DisplayMode="Overlay">
<SplitView.Pane>
<Grid HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Visibility="{TemplateBinding MasterToolbarVisibility}">
<Button Name="PaneTogglePane" Style="{StaticResource MenuButton}"/>
</StackPanel>
<controls:DropShadowPanel Grid.Row="1" Style="{StaticResource MasterMenuDropShadow}" Margin="12.5,0,0,0">
<StackPanel Background="Yellow">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="Assets\master_menu_pointer.png" />
<StackPanel Grid.Column="1" />
</Grid>
<ContentPresenter Padding="0"
Background="Red"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Master}"
Margin="0,-1,0,0" />
</StackPanel>
</controls:DropShadowPanel>
</Grid>
</SplitView.Pane>
Master (in Path=Master above) derives from a ContentPage with Content defined in its code behind as follows:
Content = new StackLayout
{
Children = {_firstPinkChild, _secondPinkChild},
Padding = 15,
Margin = 0,
Orientation = StackOrientation.Horizontal,
BackgroundColor = Color.Green,
HorizontalOptions = LayoutOptions.Start,
VerticalOptions = LayoutOptions.Start
};
Edit 2 (Some more styles being used):
<Style TargetType="controls:DropShadowPanel" x:Key="MasterMenuDropShadow">
<Setter Property="Color" Value="#595959"/>
<Setter Property="OffsetX" Value="-2"/>
<Setter Property="OffsetY" Value="0"/>
<Setter Property="BlurRadius" Value="50"/>
<Setter Property="ShadowOpacity" Value=".7"/>
</Style>
<Style TargetType="Button" x:Key="MenuButton">
<Setter Property="Width" Value="{StaticResource AppBarThemeCompactHeight}" />
<Setter Property="Height" Value="{StaticResource AppBarThemeCompactHeight}" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Source" Storyboard.TargetName="icon">
<DiscreteObjectKeyFrame KeyTime="0" Value="Assets\hamburger_onhover.icon.png"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Source" Storyboard.TargetName="icon">
<DiscreteObjectKeyFrame KeyTime="0" Value="Assets\hamburger.icon.png"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image Name="icon" Source="Assets\hamburger.icon.png" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
From official document,
The ContentView.Content property is set to a StackLayout that defines the content to be displayed on the ContentPage. This content will be displayed by the ContentPresenter contained in the TealTemplate.
For better understand please look the follow screenshot. The display air of ContentPresenter is the red block that placed in the row 2, column 0-1.
Your content will be displayed in the ContentPresenter.
<StackLayout BackgroundColor="Green">
<Label Text="Welcome to the app!" HorizontalOptions="Center" />
<Button Text="Change Theme" />
</StackLayout>
So, the answer of the first question is true. For your second question, you need to post your xaml code. Maybe you wrongly define the display area of ContentPresenter.

Windows Store: Page elements loading is jumpy, irksome

I have a page with an Image on it, its source is a local .png asset. It loads into view in the most irksome manner.
It loads across the whole grid, like RowSpan="2" and then pops into place after a second or two. It's awful.
Demo from repro app based on new blank Store app.
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="DeleteMeApp1.BasicPage1"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DeleteMeApp1"
xmlns:common="using:DeleteMeApp1.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
<x:String x:Key="AppName">My Application</x:String>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Style="{StaticResource LayoutRootStyle}" Background="Brown">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<!-- Visual states reflect the application's view state -->
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!-- The back button and title have different styles when snapped -->
<VisualState x:Name="Snapped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image Grid.Row="0" Stretch="UniformToFill" Source="Assets/BackgroundTexturedBlackboard.png" />
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
<TextBlock x:Name="pageTitle" Grid.Column="1" Text="{StaticResource AppName}" Style="{StaticResource PageHeaderTextStyle}"/>
</Grid>
<StackPanel Grid.Row="1">
</StackPanel>
</Grid>
</common:LayoutAwarePage>
The problem was down to the layout. Although technically legal, the arrangement doesn't work well.
I solved the problem by adding another Grid around the existing root one and making the new one the new root.
Into the new root I added the Image and the other elements I need to overlay the artwork and this ordering of elements renders better, although, with the transition effects I still need to tweak it.
Update
Actually, this isn't the end of the story. Removing the image and leaving just a rectangle with Opacity 0.5, the rectangle's opacity is set a second after the page has loaded. It's terrible.
I made a repro app, videoed the problem and submitted a connect bug.
https://connect.microsoft.com/VisualStudio/feedback/details/792303/windows-store-xaml-atrocious-obvious-delayed-application-of-opacity#details

Simple hover effect in XAML?

So, I was recently frustrated with a challenge to copy this effect:
<style>
a:hover {background-color:yellow; }
</style>
Using the XAML implementation in WinRT.
What is the most condense solution?
Okay, so here's my attempt:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Hover">
<Storyboard>
<ColorAnimation To="Yellow" Duration="0"
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
Storyboard.TargetName="MyTextBox" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="MyTextBox" Background="White"
PointerEntered="MyTextBox_PointerEntered"
PointerExited="MyTextBox_PointerExited"
Height="114" Width="537">
</Grid>
And this:
private void MyTextBox_PointerEntered(object sender, PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, Hover.Name, false);
}
private void MyTextBox_PointerExited(object sender, PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, Normal.Name, false);
}
But, surely there's a better way!
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<Storyboard>
<ColorAnimation To="Yellow" Duration="0"
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
Storyboard.TargetName="MyTextBox" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
"Hover" state in RT is "PointerOver".
I think you have to look into Visual States and the VisualStateManager. I think the Button control is the only one having visual states, meaning your visual entity must be defined as a Button (although it doesn't have to function as one). In the bottom of that article, you will find an example.
This SO-question might also be helpful, describing how to extract the control template from an existing button. This will give you a starting point you can modify for your needs.
As for "most condense solution", I'd like to see that as well. The examples I've seen all require 20+ lines of XAML code, which to me doesn't feel very condense...

Resources