WP supports a dark and a light theme. The user can change the theme on the phone to either light or dark, and the specific attributes will change accordingly as well, such as text colour and so on.
I am trying to create buttons that have icon images in them, to automatically change to support the theme that the user chooses.
Is there an easy way of doing this?
Here is the way I am doing my current buttons:
<Button Name="button" Margin="250,443,86,44" VerticalAlignment="Center" Style="{StaticResource roundButton}" Height="120" Click="button_click" HorizontalAlignment="Center" Width="120">
<Image Source="/Assets/Tiles/picture.png" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Button>
You can do something like this. You can detect which theme is set when the app runs and change your UI elements accordingly. Im doing this in the OnNavigatedTo method in the MainPage.xaml page to change the elements in the MainPage.xaml
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var theme = (Visibility)Resources["PhoneLightThemeVisibility"];
if (theme == System.Windows.Visibility.Visible)
{
// Change the UI for Light theme
}
else
{
// Change the UI for Dark theme
}
}
try this. This should work for you.
Related
In our application we have some controls that are Entry controls that take in numbers, so we want the user to be able to type into them. Other controls are popup selections such as a Date, or a Picker control, of which we code as Label controls.
To give the user the same consistency as Entry controls, for iOS we put the same frames around them as an Entry control in iOS such as this:
Standard Entry Control Xaml:
<Entry Grid.Row="0" Grid.Column="1" Keyboard="Numeric" ReturnType="Done" WidthRequest="60"
Text="{Binding SocketItem.QuantityPerSys, Converter={StaticResource IntComma}}"
TextChanged="OnIntegerEntryChanging" Placeholder="0"
AutomationId="SocketQtySysValueEntry" />
Mimic'ed Entry Control for iOS Rendering:
<Frame Grid.Row="1" Grid.Column="1" AutomationId="SocketDecisionDateEntryFrame">
<Label Text="{Binding SocketItem.DecisionDate, StringFormat='{0:M/d/yy}', Converter={StaticResource LocalTime}}"
HorizontalTextAlignment="Center" HorizontalOptions="FillAndExpand" AutomationId="SocketDecisionDateValueEntry"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnSocketDateTapped" CommandParameter="DecisionDate" />
</Frame.GestureRecognizers>
</Frame>
However in Android the Entry controls have a line underneath them:
Android Entry Control
We want to render these label controls the same as the Entry controls so they are consistent:
Label and Entry controls
My assumption is that I have to use a custom renderer of which I already have setup, but with just a normal underline of the text (which is of course what I don't want):
class UnderlinedLabelRenderer : LabelRenderer
{
public UnderlinedLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null && Control is FormsTextView textView)
{
textView.PaintFlags |= PaintFlags.UnderlineText;
}
}
}
I've tried investigating the actual Xamarin.Forms renderer code for the Entry control, but I'm not familiar enough with it or know exactly which class to look at to figure it out (I'll continue that route in the mean time). I am assuming I'll have to somehow add a new box control to the label cell or something.
I've also looked at this question: Xamarin Forms Android EntryCell Underline which is related but the opposite of what I want to do.
I also tried making the controls disabled Entry controls:
<StackLayout Grid.Row="1" Grid.Column="1" HorizontalOptions="End" AutomationId="OppApplicationValueFrame">
<controls:ReadonlyEntry Text="{Binding Opportunity.Application}" IsEnabled="False"
AutomationId="OppApplicationValueEntry"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OnApplicationTapped" />
</StackLayout.GestureRecognizers>
</StackLayout>
but that yielded light gray text (due to the disabled). I then created a custom renderer to fix the text:
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null && Control is FormsEditText editText)
{
editText.SetTextColor(Android.Graphics.Color.Gray);
//editText.SetBackgroundColor(Android.Graphics.Color.Gray);
}
}
but the underline on the control is still light grey (disabled color). I referenced this question on how to remove the underline: Xamarin Forms Android EntryCell Underline. But doing the opposite of setting it to another color makes the entire cell Gray.
How do I change the color of the underline in the CustomRenderer?
Any help would be great or a place to start looking.
I think the easiest way to achieve your requirement is to use a Entry instead of a Label, disable the edit ability of the entry and add a TapGestureRecognizers there to handle event:
<StackLayout>
<Entry IsEnabled="False" Text="test" HorizontalTextAlignment="Center" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
</StackLayout>
And I don't think you can achieve this by using a custom renderer of Lable, native TextView does not has this feature.
Another way is to create a custom control, like a view contains a label and a view(height =1) with black background color which stay under the label.
Update:
Code in the custom renderer to change the color of underline:
[assembly: ExportRenderer(typeof(myEntry), typeof(MyEntryRenderer))]
namespace App557.Android
{
class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
EditText editText = Control as EditText;
editText.Enabled = false;
editText.SetTextColor(global::Android.Graphics.Color.LightGreen);
editText.Background.SetColorFilter(global::Android.Graphics.Color.LightGreen, PorterDuff.Mode.SrcIn);
editText.SetSingleLine(true);
editText.Ellipsize = TruncateAt.End;
}
}
}
}
After updating the Xamarin Library and MAC OS to latest system, facing issue in Picker Background color and color of item that is selected/focused in picker for MAC Platform in my visual studio 2017.
Picker that is filled with multiple values
On opening the picker can not able to set the background color and the item that is selected is also not visible because of its color
How can I set the background color of that picker and the color of that focused/selected item of picker?
This seems to be caused for the new Theme used by Mojave.
One way to overcome this issue is by setting a value that will be visible on both Light and Dark them, for me it worked the Green.
Adding this to your XAML should be sufficient
<Picker.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="macOS" Value="Green"/>
</OnPlatform>
</Picker.TextColor>
Making the change only on your MacOs project leaving the others as they are.
<Picker HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Picker.Items>
<x:String>Dell</x:String>
<x:String>HP</x:String>
<x:String>Mac</x:String>
<x:String>Asus</x:String>
<x:String>Lenovo</x:String>
<x:String>Acer</x:String>
<x:String>Micrsoft</x:String>
</Picker.Items>
<Picker.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="macOS" Value="Green"/>
</OnPlatform>
</Picker.TextColor>
</Picker>
Note: The TextColor will only affect the selected item text color.
Hope this helps.-
To change BackGroundColor of all Picker items for iOS you need to use custom renderer.
Your Picker in shared project
<StackLayout>
<Picker x:Name="picCities" ></Picker>
</StackLayout>
In your ContentPage cs file assign ItemsSource to Picker
public MainPage()
{
InitializeComponent();
picCities.ItemsSource = new List<string> { "Hyderabad", "Bhopal", "Indore", "Jabalpur", "Mumbai", "Ahmedabad" };
picCities.SelectedIndex = 0;
}
Now in your iOS project add one .cs file name PickerCustomRenderer & add this code
[assembly: ExportRendererAttribute(typeof(Picker), typeof(PickerCustomRenderer))]
namespace picker.iOS
{
public class PickerCustomRenderer : PickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
UITextField textField = Control;
UIPickerView pickerView = textField.InputView as UIPickerView;
pickerView.BackgroundColor = UIColor.Red;
}
}
}
Output
I have a text editor in iOS and with Done button so when i tap it after typing something for edit the editor covers the whole area of the app and unable to edit.
I want to fix the height of the editor so that will be able to edit and save it.
I have used custom renderer for this and tried setting the height and width but not working as expected
my xaml code
<controls:XEditor x:Name="Editor" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" />
my custom renderer
public class XEditorRenderer:EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.SelectAll(Self);
//this.Control.InputAccessoryView = null;
}
}
This is and Editor issue so i have tweaked it to creating a custom renderer and fix it
I've failed certification because I have an image that's all white. So when a user switches to the light theme it fails because you can't see it. How do I swap this imaged based on the theme applied?
Thank you in advance
Question Making an image control invert its colors depending on theme has a simple XAML only answer:
<Image HorizontalAlignment="Center"Stretch="None" Visibility="{StaticResource PhoneLightThemeVisibility}" Source="/MyApplication;component/imageDarkTheme.png" />
<Image HorizontalAlignment="Center" Stretch="None" Visibility="{StaticResource PhoneDarkThemeVisibility}"Source="/MyApplication;component/imageLightTheme.png" />
In XAML
<Image Source="{Binding ImageSource}" />
In the view model that contains property ImageSource
public string ImageSource
{
get
{
if( (Visibility)App.Current.Resources["PhoneDarkThemeVisibility"]
== Visibility.Visible ) {
return "/path/to/dark/image.png";
} else {
return "/path/to/light/image.png";
}
}
private set {}
}
This may not change the picture if the user tombstones your app, changes the theme and switches back to the app.
One way to handle that scenario is to cache the current theme setting in the App class constructor, then compare it with the current setting within App.Application_Activated, if they're different you'll need to somehow indicate that the above view model needs to fire a property changed notification for ImageSource.
For anyone following my comments above - I had to switch from ImageBrush to Image directly (xaml below)
<Button Tag="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="-10,20,0,0" BorderThickness="0" Width="105" Height="102" Click="ShowKioskOnMap_Click">
<Image Source="../images/arrow.png" Width="55" Height="53" ImageOpened="Image_ImageOpened"/>
</Button>
Next in the consructor of the page I'm using --I did the theme lookup to prevent any issues when the app state is restored from taking a phone call for instance (or just loading up the view for the first time)
InitializeComponent();
theme = ""; //field level var (could make it dark by default if needed)
if ((Visibility)App.Current.Resources["PhoneDarkThemeVisibility"] == Visibility.Visible)
{
theme = "dark";
}
else
{
theme = "light";
}
And I had to implement the following in my open event to switch based on theme
private void Image_ImageOpened(object sender, RoutedEventArgs e)
{
var brush = (sender as Image);
if (brush.Stretch == Stretch.Uniform)
{
if (theme == "light")
brush.Source = new BitmapImage(new Uri("../images/arrowLight.png", UriKind.Relative));
brush.Stretch = Stretch.Fill;
}
}
I have a control with white text foreground color and transparent background color.
Later on this usercontrol will be added into a different control that carries the real background color.
However during designing this, control due white foreground on white background in VS 2010, I can't obviously see anything. In there anyway to define a different color for just the design time?
I have tried this:
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
}
But this doesn't work. Any tips?
UPDATE:
I dont understand how this works for you guys. I have created a new Silverlight 4.0 Application and have inserted this line of code into the ctor:
public MainPage()
{
InitializeComponent();
LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
}
<UserControl x:Class="SilverlightApplication3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot">
</Grid>
</UserControl>
When I go into Designer, I still dont see it as blue. And I dont even have any isInDesignTime Condition there. What I am missing here?
Thanks,
Kave
Here's one way:
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
LayoutRoot.Background = new SolidColorBrush(Colors.Yellow);
}
If you switch to creating a templated control, you'll need to wait to set things up in OnApplyTemplate, like in this example:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Border b = this.GetTemplateChild("backBorder") as Border;
if (b != null && System.ComponentModel.DesignerProperties.IsInDesignTool)
{
b.Background = new SolidColorBrush(Colors.Orange);
}
}
Assuming this is the template:
<Style TargetType="local:TemplatedControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TemplatedControl1">
<Border x:Name="backBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I also like to add conditional compile directives around code like this, as it's only for the developer/designer and is never needed at run-time.
#if DEBUG
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
LayoutRoot.Background = new SolidColorBrush(Colors.Yellow);
}
#endif
Note that this entire technique works only when the UserControl you're creating is used within* another* UserControl/Control at design time. So, if the code I suggested above is placed in a UserControl named UserControlWithDesignMode, then you must have another UserControl, UserControlHost, that contains an instance of the UserControlWithDesignMode control to see the behavior work at design time. The code-behind for the currently edited control does not execute when you're editing it. It only executes when it's contained within another host (in Silverlight, another UserControl for example).
One option would be to give the UserControl a background color, and then override that where you use it. So when you're editing the UserControl in isolation, it would have a background color; but when you're editing a control that contains that UserControl, you would see it with the transparent background like you want.
So the UserControl's XAML file would look like this:
<UserControl x:Class="MyUserControl" ... Background="DarkBlue">
And then in some other screen, where you use it, you could do:
<my:MyUserControl Background="Transparent" ...>
Inelegant, but simple.
Alternate approach that doesn't involve code:
Install the "Visual Studio 2012 Color Theme Editor" found here:
http://visualstudiogallery.msdn.microsoft.com/366ad100-0003-4c9a-81a8-337d4e7ace05
Or for VS 2010: http://visualstudiogallery.msdn.microsoft.com/20cd93a2-c435-4d00-a797-499f16402378
Create a new custom theme based on the one you want to modify.
Click the "Show All Elements" filter button in the upper-left of the theme editor
Type "artboard" in the search-box in the upper-right of the theme editor
Set the "Cider -> ArtboardBackground" color to a different color of your choice.
Yay! :D
Note: the "Cider -> ArtboardBackground" color theme field is found in VS2012 but I cannot confirm whether it has the same name in VS2010
You can use following code within UserControl constructor:
For WPF:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
LayoutRoot.Background = new SolidColorBrush( Colors.Blue );
}
For WPF / Silverlight:
if (DesignerProperties.GetIsInDesignMode( this ))
{
LayoutRoot.Background = new SolidColorBrush( Colors.Blue );
}
Good thread, especially when doing some MVVM the fact that UserControls appear on white when they are transparent is very annoying.
I'm sure you could do this in Blend with a state defined based on whether a designer is running, but I don't think that'd reduce the amount of work.
Still not sure how to avoid the code in the code behind and avoiding having to open blend, so if anybody has suggestions thanks in advance for posting.
I would suggest to use Opacity
<my:MyUserControl Background="Transparent" ...>
That doesn't work, since it will make any child controls inside the usercontrol invisible at run time.
One option would be to give the UserControl a background color, and then override that where you use it.
Did you try to set the Background on the UserControl? Not sure why but for me it doesn't work.
What does work is to set the Background of the Content, like so
<UserControl x:Class="...">
<StackPanel Background="{StaticResource PhoneChromeBrush}">
...
then putting the following code in the constructor of the view
public View() {
InitializeComponent();
var c = Content as Panel;
if (c != null) c.Background = new SolidColorBrush(Colors.Transparent);
}
Another technique mentioned in this SO question is to use the undocumented property d:DesignerProperties.DesignStyle, which works great for applying a design-time-only style to a single control, but which doesn't appear to work for a Style in a ResourceDictionary that would apply to all of the appropriately-typed controls or elements under the scope of the dictionary.
To solve this, on that same page I provide a simple solution for deploying a designer-only style into a ResourceDictionary. Here is a summary of that answer:
First, put the desired style in a XAML dictionary in the normal way.
<Window.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</Window.Resources>
Then, in the C# code, remove the style from the ResourceDictionary when design mode is not detected. Do this is in the OnInitialized override:
protected override void OnInitialized(EventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(this) == false)
Resources.Remove(typeof(TreeViewItem));
base.OnInitialized(e);
}
Design Mode: Runtime Mode: