How Can I Reuse an Icon in Resources in UWP? In WPF, I'd use x:Shared=false - xaml

I am trying to create a button Style that I can use for a "Lookup" button throughout my UWP app. However, the icon only appears on the first button on the screen. I tried this solution using templates, but it is not working for me. Thanks for the help.
Code:
<Page.Resources>
<ControlTemplate x:Key="FindSymbolTemplate">
<SymbolIcon Symbol="Find" Foreground="White" />
</ControlTemplate>
<Style TargetType="Button" x:Key="LookupButton">
<Setter Property="Content">
<Setter.Value>
<ContentControl Template="{StaticResource FindSymbolTemplate}" />
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
....
<Button x:Name="tourNumLookup"
Style="{StaticResource LookupButton}"
Grid.Column="1"
Margin="10,0"
VerticalAlignment="Center" />
....
<Button x:Name="customerIdLookup"
Style="{StaticResource LookupButton}"
VerticalAlignment="Center"
Grid.Column="1"
Grid.Row="1"
Margin="10,0" />
The two buttons in the UI. Only the first has the SymbolIcon content.

#Romasz's solution absolutely works, but what if you want a lightly different Foreground on the SymbolIcon inside another Button?
Here's a potentially more flexible way that I normally go with.
First let's create a base Style that holds some default values for all the icons.
<Style x:Key="Style-Icon-Base"
TargetType="ContentControl">
<!-- If you don't specify the Foreground, it will use its ancestor's -->
<!--<Setter Property="Foreground"
Value="White" />-->
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="Width"
Value="20" />
<Setter Property="Height"
Value="20" />
<Setter Property="Padding"
Value="0" />
</Style>
Then we create a new icon Style which inherits from the one above. Note within the ControlTemplate I have used TemplateBinding to make property values dynamic. TemplateBinding isn't available inside a DataTemplate.
<Style x:Key="Style-Icon-Find"
BasedOn="{StaticResource Style-Icon-Base}"
TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<!--
'cause you cannot change the size of the SymbolIcon, we insert a Viewbox here,
otherwise you don't need it.
-->
<Viewbox Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<SymbolIcon Symbol="Find"
Foreground="{TemplateBinding Foreground}" />
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This way you have created a highly reusable icon Style, to use it, have a look at the following Buttons:
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Margin="4"
Padding="8"
BorderBrush="LightBlue">
<ContentControl Width="36"
Height="36"
Foreground="DarkCyan"
Style="{StaticResource Style-Icon-Find}" />
</Button>
<!-- Note how I defined the Foreground at the Button level and it flows down to the icon -->
<Button Foreground="DarkGoldenrod"
Margin="4">
<StackPanel Orientation="Horizontal">
<ContentControl Style="{StaticResource Style-Icon-Find}"
Width="16"
Height="16" />
<TextBlock Text="Search"
VerticalAlignment="Center"
Margin="8,0,0,0" />
</StackPanel>
</Button>
<Button Margin="4"
Padding="4">
<ContentControl Style="{StaticResource Style-Icon-Find}" />
</Button>
</StackPanel>
And they look like:

Generally UI elements can be used once (or saying different - have only one parent) - this is probably why it only works for the first button in your case. One solution may be to define DataTemplate and use it as ContentTemplate, so each button creates its own icon:
<Page.Resources>
<DataTemplate x:Key="FindTemplate">
<SymbolIcon Symbol="Find" Foreground="White" />
</DataTemplate>
</Page.Resources>
...
<Button x:Name="tourNumLookup" ContentTemplate="{StaticResource FindTemplate}"
Grid.Column="1" Margin="10,0" VerticalAlignment="Center" />
<Button x:Name="customerIdLookup" ContentTemplate="{StaticResource FindTemplate}"
VerticalAlignment="Center" Grid.Column="1" Grid.Row="1" Margin="10,0" />

You don't need to create ControlTemplate to reuse the icon. You can simply put this SymbolIcon to the resource dictionary and use as StaticResource for the buttons' Content.
<Page.Resources>
<SymbolIcon x:Key="FindSymbol" Symbol="Find" Foreground="White" />
</Page.Resources>
<Button x:Name="tourNumLookup"
Content="{StaticResource FindSymbol}"
Grid.Column="1"
Margin="10,0"
VerticalAlignment="Center" />
<Button x:Name="customerIdLookup"
Content="{StaticResource FindSymbol}"
VerticalAlignment="Center"
Grid.Column="1"
Grid.Row="1"
Margin="10,0" />
UPDATE
BTW this is possibly a bug in the UWP platform, because I tried the following code and only the first Button rendered the icon at desing time and none of the at runtime.
<Page.Resources>
<SymbolIcon x:Key="FindSymbol" Symbol="Find" Foreground="White" />
<Style TargetType="Button" x:Key="LookupButton">
<Setter Property="Content" Value="{StaticResource FindSymbol}"/>
</Style>
</Page.Resources>
<StackPanel>
<Button x:Name="tourNumLookup"
Style="{StaticResource LookupButton}"
Margin="10,0"
VerticalAlignment="Center" />
<Button x:Name="customerIdLookup"
Style="{StaticResource LookupButton}"
VerticalAlignment="Center"
Margin="10,0" />
</StackPanel>
I tried to assign the Setter's Value directly but I got the same result. And also tried with FontIcon.

Related

Can Maui configure a shadow in a line?

I´m developing an app in .net maui and i have a question i have not answered myself through browsing.
I'm trying to apply a shadow to a border and the following code works perfectly.
<Border
Style="{StaticResource light-theme-border}"
Grid.Column="1"
Grid.Row="3"
Grid.ColumnSpan="7">
<Border.Shadow>
<Shadow
Brush="red"
Offset="1,11"
Radius="20"
Opacity="0.25"/>
</Border.Shadow>
</Border>
But when i write shadow directly inside the border properties it catches the property but i don't know how to dump the [brush, offset, radius & opacity] info:
<Border
Style="{StaticResource light-theme-border}"
Grid.Column="1"
Grid.Row="3"
Grid.ColumnSpan="7"
Shadow="???????????????????????????">
</Border>
You can then define and consume it as a resource (same what you did with Style property):
<ContentPage.Resources>
<ResourceDictionary>
<Shadow
x:Key="CommonShadow"
Brush="red"
Offset="1,11"
Radius="20"
Opacity="0.25"/>
<Border
Style="{StaticResource light-theme-border}"
Grid.Column="1"
Grid.Row="3"
Grid.ColumnSpan="7"
Shadow="{StaticResource CommonShadow}">
</Border>
Or integrate it in your existing style of Border
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="light-theme-border" TargetType="Border">
<Setter Property="WidthRequest" Value="20" />
<Setter Property="Shadow">
<Setter.Value>
<Shadow
Brush="red"
Opacity="1"
Radius="50"
Offset="20,20" />
</Setter.Value>
</Setter>
</Style>
<Border
Style="{StaticResource light-theme-border}"
Grid.Column="1"
Grid.Row="3"
Grid.ColumnSpan="7">
</Border>
Or integrate it in your existing style of Border as a static resource
<ContentPage.Resources>
<ResourceDictionary>
<Shadow
x:Key="CommonShadow"
Brush="red"
Offset="1,11"
Radius="20"
Opacity="0.25"/>
<Style x:Key="light-theme-border" TargetType="Border">
<Setter Property="WidthRequest" Value="20" />
<Setter Property="Shadow" Value="{StaticResource CommonShadow}"/>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<Border
Style="{StaticResource light-theme-border}"
Grid.Column="1"
Grid.Row="3"
Grid.ColumnSpan="7">
</Border>

Datatrigger with DynamicResource works only on last item in ItemsControl

My DashBoardSimpleCountObject has 2 values: MyName and MyValue.
I use an ObservableCollection<DashboardSimpleCountObject> called MyData.
I want to show a picture, as long as MyValue is null.
However, the picture ("loading") is only shown at the last item of my ObservableCollection (no matter, how many items are in there). As soon as a MyValue is set (with anything other than null), it is automatically updated and shown correctly - that works fine at all items.
<ItemsControl x:Name="_control" ItemsSource="{Binding MyData}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="25">
<Label FontSize="14" Content="{Binding MyName}" />
<Label Margin="0,25" HorizontalContentAlignment="Right" FontSize="29" FontWeight="ExtraBold" Foreground="{Binding MyColor}">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Content" Value="{Binding MyValue}" />
<Style.Triggers>
<DataTrigger Binding="{Binding MyValue}" Value="{x:Null}">
<Setter Property="Content">
<Setter.Value>
<Image Width="32" Height="32" Source="{DynamicResource loading}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What am I doing wrong?
Thank you very much in advance! :)
It seems, the nature of DynamicResource causes this issue.
Simply changing DynamicResource to StaticResource did the trick.
So, the DataTrigger worked just fine from the beginning. The image simply did only show up once due to being loaded as a DynamicResource.

How to make a Button expand to show its Text in Xamarin.Forms

I have created a Style for my Buttons but when I use longer texts, they are truncated.
This is the style:
<Style x:Key="DefaultButton" TargetType="Button">
<!--<Setter Property="WidthRequest">
<Setter.Value>
<OnIdiom x:TypeArguments="x:Double"
Phone="150"
Tablet="200" />
</Setter.Value>
</Setter>
<Setter Property="HeightRequest">
<Setter.Value>
<OnIdiom x:TypeArguments="x:Double"
Phone="70"
Tablet="100" />
</Setter.Value>
</Setter>-->
<Setter Property="BackgroundColor" Value="{StaticResource BaseColor}" />
<Setter Property="TextColor" Value="White" />
<Setter Property="HorizontalOptions" Value="CenterAndExpand" />
<Setter Property="VerticalOptions" Value="FillAndExpand" />
<Setter Property="Margin" Value="5" />
<Setter Property="FontSize">
<Setter.Value>
<OnIdiom x:TypeArguments="x:Double"
Phone="20"
Tablet="25" />
</Setter.Value>
</Setter>
</Style>
The button:
<Button Grid.Row="1"
Grid.Column="1"
Style="{DynamicResource DefaultButton}"
Text="Basic button with long text" />
This is how that button with a longer text looks:
I could set a very large HeightRequest for the button but that would be very bad practice.
What should I do about this?
In this situation I will often used a Grid with a BoxView and a Label to make up the button and then put a GestureRecognizer on the Grid. Add all of this to a custom control for easy reuse if you want.
I did the GestureRecognizer below from memory so it might need some fixing:
<Grid x:Name="BasicButtonGrid"
VerticalOptions="End">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.MinimumHeightRequest>
<OnIdiom x:TypeArguments="x:Double"
Phone="40"
Tablet="70"/>
</Grid.MinimumHeightRequest>
<Grid.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBasicButtonTapped"/>
</Grid.GestureRecognizers>
<BoxView BackgroundColor="Blue"
VerticalOptions="EndAndExpand"
InputTransparent="True"
Grid.Row="0"
Grid.Column="0"/>
<Label Text="Basic Button with long text"
TextColor="White"
FontSize="Medium"
Grid.Row="0"
Grid.Column="0"/>
</Grid>
You can use the LineBreakModeproperty. Here is the documentation to help you choose the most suitable mode.
https://developer.xamarin.com/api/type/Xamarin.Forms.LineBreakMode/

Button Menu flyout horizontal alignment in UWP

I need to set the menuflyout to get right aligned. I used the code, but it appears at left only. Should I need to modify in the style?
<Button Content="Click" HorizontalAlignment="Right" Height="30" Width="30">
<Button.Flyout>
<MenuFlyout Placement="Bottom">
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="MaxWidth" Value="50" />
<Setter Property="MaxHeight" Value="50" />
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem Text="Item" />
</MenuFlyout>
</Button.Flyout>
</Button>
From the screenshot, it seems you are develpoing a Windows Phone 8.1 app not a UWP app.
While using UWP, your code (I remove Height and Width in the Button) looks like:
I think this is what you want.
But in Windows Phone 8.1, we have to modify the template of MenuFlyoutPresenter to achieve this.
To modify the template of MenuFlyoutPresenter, we can select the "[MenuFlyout]" in "Document Outline" and right click, then select "Edit Additional Templates" → "Edit MenuFlyoutPresenterStyle" → "Edit a Copy...".
In the template, we need to set the HorizontalAlignment of OuterBorder, CenterBorder and InnerBorder to Right:
<Border x:Name="OuterBorder" BorderBrush="{TemplateBinding BorderBrush}" FlowDirection="LeftToRight" HorizontalAlignment="Right">
...
<Border x:Name="CenterBorder" BorderBrush="{TemplateBinding Background}" FlowDirection="LeftToRight" HorizontalAlignment="Right">
<StackPanel x:Name="InnerBorder" Background="{TemplateBinding Background}" FlowDirection="{TemplateBinding FlowDirection}" HorizontalAlignment="Right">
...
</StackPanel>
</Border>
</Border>
Then in the Button, we can use code like following:
<Button HorizontalAlignment="Right" Content="Click">
<Button.Flyout>
<MenuFlyout MenuFlyoutPresenterStyle="{StaticResource MenuFlyoutPresenterStyle1}" Placement="Bottom">
<MenuFlyoutItem Text="Item" />
</MenuFlyout>
</Button.Flyout>
</Button>
It looks like:
You can also change other properties in the Style of MenuFlyoutPresenter to beautify it.
Actually if you use default settings and set your Placement property of MenuFlyout, position should look fine. When I modify your code it should work properly as what you need. Yıu can check from screenshot.
<Button Content="Click" HorizontalAlignment="Right" Height="30" Width="114" Margin="0,200,0,0" VerticalAlignment="Top" >
<Button.Flyout>
<MenuFlyout Placement="Bottom">
<MenuFlyoutItem Text="Item" HorizontalAlignment="Right" />
</MenuFlyout>
</Button.Flyout>
</Button>

hit is not Visible on a button in windows phone 8

I am making a button with a item template but when I click on Button its does not show that whether it is hit or not.
I want that it should look clicked like normal button.
I tried to set the ishitvisible property of the button but its not working.
Can anyone help ??
<Button Name="BtnSignUp" Grid.Row="3" VerticalAlignment="Top" Click="BtnSignUp_Click" >
<Button.Template>
<ControlTemplate>
<Border Margin="5,15,0,0" BorderThickness="2" BorderBrush="#866DA9">
<StackPanel Orientation="Horizontal" Background="#491776" IsHitTestVisible="True" >
<TextBlock Text="Sign Up Now -" Margin="35,5,0,0" FontSize="23" FontWeight="Medium"/>
<TextBlock Text=" it's free" Margin="0,5,35,10" FontSize="23" FontStyle="Italic" FontWeight="Normal" />
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
As you said, you are not using Item Template. You override Control Template. So you cannot get the default pressed effects of button. But still you can add that using Visual State Managers or through triggers.
I have posted an example of how to adjust opacity on mouse over and pressed.
<Button Name="BtnSignUp" Grid.Row="3" VerticalAlignment="Top" Click="BtnSignUp_Click" >
<Button.Template>
<ControlTemplate>
<Border Margin="5,15,0,0" BorderThickness="2" BorderBrush="#866DA9">
<StackPanel x:Name="bor" Orientation="Horizontal" Background="#491776" IsHitTestVisible="True" >
<TextBlock Text="Sign Up Now -" Margin="35,5,0,0" FontSize="23" FontWeight="Medium"/>
<TextBlock Text=" it's free" Margin="0,5,35,10" FontSize="23" FontStyle="Italic" FontWeight="Normal" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bor" Property="Opacity" Value="0.7"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Opacity" TargetName="bor" Value="0.6"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>