Update resource in Universal Windows App XAML - c#

I need to change a color of application's TextBlocks at runtime in an Universal Windows App.
Universal Windows Apps don't support Dynamic Resources and I've been unsuccessfully exploring a few different ways to change color of TextBlock
<TextBlock Text="Test" Style="{StaticResource MyText}"/>
using the style
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
</Style>
My question is: How can I change the color of the TextBlock at runtime?
What follows are all attempts to change the color:
Initially, I followed this article+video Dynamically Skinning Your Windows 8 App and I stored TextColor in a separate dictionary file that I can swap in and out of MergedDictionaries
Day.xaml contains <SolidColorBrush x:Key="TextColor" Color="#FFDDEEFF" />
Night.xaml contains <SolidColorBrush x:Key="TextColor" Color="#FFFFDD99" />
In code:
ResourceDictionary _nightTheme = new ResourceDictionary() { Source = new Uri("ms-appx:///Themes/Night.xaml") };
ResourceDictionary _baseTheme = new ResourceDictionary() { Source = new Uri("ms-appx:///Themes/MyApp.xaml") };
// OnLaunched - I set a default theme to prevent exceptions
Application.Current.Resources.MergedDictionaries.Add(_dayTheme);
// Method that changes theme:
if (NightFall)
{
Application.Current.Resources.MergedDictionaries.Remove(_dayTheme);
Application.Current.Resources.MergedDictionaries.Add(_nightTheme);
}
else
{
Application.Current.Resources.MergedDictionaries.Remove(_nightTheme);
Application.Current.Resources.MergedDictionaries.Add(_dayTheme);
}
When this didn't work, I thought I need to clear the dictionaries:
ResourceDictionary _baseTheme = new ResourceDictionary() { Source = new Uri("ms-appx:///Themes/MyApp.xaml") };
// Method that changes theme:
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(_baseTheme);
if (NightFall)
{
Application.Current.Resources.MergedDictionaries.Add(_nightTheme);
}
else
{
Application.Current.Resources.MergedDictionaries.Add(_dayTheme);
}
I also tried to refresh the frame in the method that changes dictionaries, to no avail
var frame = Window.Current.Content as Frame;
frame.Navigate(frame.Content.GetType());
In another attempt I tried to create a dictionary at runtime and update it
ResourceDictionary _dynamicTheme = new ResourceDictionary();
// OnLaunched
_dynamicTheme.Add("TextColor", new SolidColorBrush(Windows.UI.Colors.Chocolate));
Application.Current.Resources.MergedDictionaries.Add(_dynamicTheme);
// Method that changes theme
_dynamicTheme.Remove("TextColor");
_dynamicTheme.Add("TextColor", new SolidColorBrush(NightFall ? Windows.UI.Colors.Chocolate : Windows.UI.Colors.Cornsilk));
Finally, I realized that perhaps StaticResource makes the color immutable, so I decided to give ThemeResource a try. I've modified my themes:
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource MyTextColor}" />
</Style>
Day.xaml
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="MyTextColor" Color="#FFDDEEFF" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
Night.xaml
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="MyTextColor" Color="#FFFFDD99" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
I swapped the methods in and out of the Application.Current.Resources.MergedDictionaries just like in previous attempts.
Again, the color doesn't change, even if I fake-refresh the Frame

I was facing the same issue several month ago, i couldn't fix the problem until i came across the following blog post which propose a pretty good generic solution.
Basically what you need to do is :
First
add the following helper Frame class, which will replace your default Frame
public class ThemeAwareFrame : Frame
{
private static readonly ThemeProxyClass _themeProxyClass = new ThemeProxyClass();
public static readonly DependencyProperty AppThemeProperty = DependencyProperty.Register(
"AppTheme", typeof(ElementTheme), typeof(ThemeAwareFrame), new PropertyMetadata(default(ElementTheme), (d, e) => _themeProxyClass.Theme = (ElementTheme)e.NewValue));
public ElementTheme AppTheme
{
get { return (ElementTheme)GetValue(AppThemeProperty); }
set { SetValue(AppThemeProperty, value); }
}
public ThemeAwareFrame(ElementTheme appTheme)
{
var themeBinding = new Binding { Source = _themeProxyClass, Path = new PropertyPath("Theme"), Mode = BindingMode.OneWay };
SetBinding(RequestedThemeProperty, themeBinding);
AppTheme = appTheme;
}
sealed class ThemeProxyClass : INotifyPropertyChanged
{
private ElementTheme _theme;
public ElementTheme Theme
{
get { return _theme; }
set
{
_theme = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The idea behind the ThemeAwareFrame class as explained in by the blog post writer is:
I create a proxy class that will just be used to store the current theme, and,
if the theme is changed, to propagate it. It is a static field, so is
shared with all ThemeAwareFrame.
I add an AppTheme dependency property. When it will be changed, it
will changed in the proxy class.
In the ThemeAwareFrame constructor, I bind the ThemeRequested property
to the proxy class Theme property.
Second
Create your Light and Dark theme resources in the App.xaml :
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="MyTextColor" Color="DarkGray" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="MyTextColor" Color="White" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Third
in the App.Xaml.cs change the rootFrame to a ThemeAwareFrame instead of a simple Frame:
rootFrame = new ThemeAwareFrame(ElementTheme.Dark);
in the OnLaunched method :
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}
#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new ThemeAwareFrame(ElementTheme.Dark);
// TODO: change this value to a cache size that is appropriate for your application
rootFrame.CacheSize = 1;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
// TODO: Load state from previously suspended application
}
//..
Forth
Use ThemeResource instead of staticResource when using a Theme related resource :
<Page.Resources>
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource MyTextColor}" />
</Style>
</Page.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Test" Style="{StaticResource MyText}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Button Content="Dark Theme" Click="ChangeThemeToDarkClick" Grid.Row="1"></Button>
<Button Content="Light Theme" Click="ChangeThemeToLightClick" Grid.Row="2"></Button>
</Grid>
Finally
To change your app theme simply change the AppTheme property of your rootFrame like this:
private void ChangeThemeToLightClick(object sender, RoutedEventArgs e)
{
(Window.Current.Content as ThemeAwareFrame).AppTheme = ElementTheme.Light;
}
private void ChangeThemeToDarkClick(object sender, RoutedEventArgs e)
{
(Window.Current.Content as ThemeAwareFrame).AppTheme = ElementTheme.Dark;
}

Related

My resource dictionary is omitting my label from being showed

I have a wpf app and I am messing with loading themes (light and dark), I made two simple resource dictionary files which are created in a shared assembly:
Dark Theme (same structure for the light theme, but with different color values):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="#FF1E1E1E" x:Key="Background"/>
<SolidColorBrush x:Key="TextColorBrush" Color="White"/>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextColorBrush}"/>
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="{StaticResource Background}"/>
</Style>
</ResourceDictionary>
In my main application, App.xaml I am referencing my 2 theme dictionaries as such
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_DarkTheme.xaml" x:Name="DarkTheme"/>
<ResourceDictionary Source="pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_LightTheme.xaml" x:Name="LightTheme"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The way I am setting up the resources based on which theme I am choosing is done in the App.xaml.cs
public enum Skin { Light, Dark }
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public static Skin Skin { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ChangeSkin(Skin.Light);
}
public void ChangeSkin(Skin newSkin)
{
Skin = newSkin;
if (Skin == Skin.Dark)
ApplyResources(Resources.MergedDictionaries[0].Source.ToString());
else if (Skin == Skin.Light)
ApplyResources(Resources.MergedDictionaries[1].Source.ToString());
}
private void ApplyResources(string src)
{
var dict = new ResourceDictionary() { Source = new Uri(src, UriKind.RelativeOrAbsolute) };
foreach (var mergeDict in dict.MergedDictionaries)
{
Resources.MergedDictionaries.Add(mergeDict);
}
foreach (var key in dict.Keys)
{
Resources[key] = dict[key];
}
}
}
And finally, my main window. Since I want these particular styles to be global I am not using any keys to identify them.
<Window x:Class="Foo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Label Content="hello"/>
</Grid>
</Window>
But my main issue is that the Label control doesn't show up in my application. I can see my background change color appropriately but my label control is just gone! What am I doing wrong? Many thanks in advance!
Do not add all theme ResourceDictionaries from the start to Application.Resources.MergedDictionaries, i.e. start with empty Application.Resources:
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Then change the theme by replacing Application.Current.Resources.MergedDictionaries with the current theme:
private void ChangeSkin(Skin skin)
{
ResourceDictionary theme = null;
switch (skin)
{
case Skin.Light:
theme = new ResourceDictionary { Source = new Uri("pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_LightTheme.xaml") };
break;
case Skin.Dark:
theme = new ResourceDictionary { Source = new Uri("pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_DarkTheme.xaml") };
break;
}
if (theme != null)
{
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(theme);
}
}
When changing themes only means to replace Colors and Brushes, you may also move your Styles to Application.Resources and use DynamicResource in the Style Setters.
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextColorBrush}"/>
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="{DynamicResource Background}"/>
</Style>
</Application.Resources>
</Application>
Then your theme ResourceDictionaries would only contain Color and Brush resources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="#FF1E1E1E" x:Key="Background"/>
<SolidColorBrush x:Key="TextColorBrush" Color="White"/>
</ResourceDictionary>

How to override resource in ThemeDictionary using code (UWP)

I would like to have the equivalent of the XAML, but in C# code:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="SystemControlHighlightBaseMediumLowBrush" Color="White" />
<SolidColorBrush x:Key="SystemControlHighlightBaseHighBrush" Color="White" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
Add the following to your Application OnLaunched method:
ResourceDictionary lightTheme = new ResourceDictionary();
lightTheme["SystemControlHighlightBaseMediumLowBrush"] = new SolidColorBrush(Windows.UI.Colors.White);
lightTheme["SystemControlHighlightBaseHighBrush"] = new SolidColorBrush(Windows.UI.Colors.White);
App.Current.Resources.ThemeDictionaries.Add("Light", lightTheme);
You can use
Brush SystemControlHighlightBaseHighBrush = new SolidColorBrush(Colors.White);
Brush SystemControlHighlightBaseHighBrush = new SolidColorBrush(Colors.White);
Then you could use it like this to set the colour of a button
myButton.Background = SystemControlHighlightBaseHighBrush;
If, as your comment suggests you want to change the colour on mouse hover you need to capture the mousehover event and then you could change the colour to one of your named styles. For example to change the button colour on mousehover you could do this
private void myButton_MouseHover(object sender, System.EventArgs e)
{
myButton.Background = SystemControlHighlightBaseHighBrush;
}

Ways to address WPF Touch Screen Sensitivity

I am trying to address issues with the sensitivity of a capacitive touch screen where WPF buttons are being triggered if the users fingers pass too close to the surface of the screen.
This issue is that many users end up with fingers or parts of their hands, other than their primary touch finger, close to the surface of the screen and this causes incorrect buttons to be triggered.
Adjusting the sensitivity of the screen seems to make little difference to I thought I could try modifying the button pressed events to only trigger a Click if the button is pressed for more than a certain amount of time.
Can anyone explain how I might create a custom button that would have an adjustable 'pressed' time before triggering a Clicked event.
If possible perhaps you would be kind enough to include a very simple C#/WPF application with such a custom button.
EDIT
OK, so I have created a subclassed Button using the code below, as per #kidshaw's answer but I think I must be missing a few things because nothing is getting called except the default Click event.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace AppName
{
public class TouchButton : Button
{
DoubleAnimationUsingKeyFrames _animation;
public static readonly DependencyProperty DelayElapsedProperty =
DependencyProperty.Register("DelayElapsed", typeof(double), typeof(TouchButton), new PropertyMetadata(0d));
public static readonly DependencyProperty DelayMillisecondsProperty =
DependencyProperty.Register("DelayMilliseconds", typeof(int), typeof(TouchButton), new PropertyMetadata(100));
public double DelayElapsed
{
get { return (double)this.GetValue(DelayElapsedProperty); }
set { this.SetValue(DelayElapsedProperty, value); }
}
public int DelayMilliseconds
{
get { return (int)this.GetValue(DelayMillisecondsProperty); }
set { this.SetValue(DelayMillisecondsProperty, value); }
}
private void BeginDelay()
{
this._animation = new DoubleAnimationUsingKeyFrames() { FillBehavior = FillBehavior.Stop };
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(this.DelayMilliseconds)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.Completed += (o, e) =>
{
this.DelayElapsed = 0d;
//this.Command.Execute(this.CommandParameter); // Replace with whatever action you want to perform
Console.Beep();
this.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
};
this.BeginAnimation(DelayElapsedProperty, this._animation);
}
private void CancelDelay()
{
// Cancel animation
this.BeginAnimation(DelayElapsedProperty, null);
}
private void TouchButton_TouchDown(object sender, System.Windows.Input.TouchEventArgs e)
{
this.BeginDelay();
}
private void TouchButton_TouchUp(object sender, System.Windows.Input.TouchEventArgs e)
{
this.CancelDelay();
}
}
}
How does the TouchButton_TouchDown method ever get called ? Don't I have to assign this to the TouchDown even handler somehow?
OK, I added a constructor and set the TouchDown/Up event handlers so that works but the CancelDelay() does not stop the event from being fired. It seems work OK and gets called when the user lift their finger but does not prevent the event from being triggered.
A time delay button would be the best option.
I have provided an example in this other stack overflow answer.
It uses an animation to delay triggering a command.
Hope it helps.
Do wpf have touch and gold gesture
You could almost certainly come up with a solution to do this. There are two approaches I would look at:
Create a specialisation derived from Button. You would override various handlers to implement your own behaviour.
Create an attached dependency property that subscribes to the preview mouse events. The preview events would allow you to intercept the up/down events to inject your own behaviour before the standard button handling can generate the click events.
Option #1 is probably the easiest to get your head around. The handling to generate click events lives in ButtonBase in both the OnMouseLeftButtonDown and OnMouseLeftButtonUp handlers. If you implement (override) your own version of both these handlers you should be able to fairly easily introduce a timer that only calls OnClick to generate the click event once a certain time has expired since the user pressed (and held down) the button.
PS: If you don't have it already, I highly recommend you get a copy of .NET Reflector. It will allow you to easily view the code for the WPF button implementation. I quickly used it to have a look at the WPF button implementation to get an idea of how it works in order to answer this question.
For completeness here is the full solution I used based on #kidshaw's original answer. Might save someone else some time fiddling around pulling the pieces together.
Note that I am getting VS Designer errors with it complaining about not finding the custom classes in the apps namespace. Strangely this does not seem to happen on an earlier version of VS so perhaps its a bug in VS.
TouchButton.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace TouchButtonApp
{
public class TouchButton : Button
{
DoubleAnimationUsingKeyFrames _animation;
bool _isCancelled = false;
public static readonly DependencyProperty DelayElapsedProperty =
DependencyProperty.Register("DelayElapsed", typeof(double), typeof(TouchButton), new PropertyMetadata(0d));
public static readonly DependencyProperty DelayMillisecondsProperty =
DependencyProperty.Register("DelayMilliseconds", typeof(int), typeof(TouchButton), new PropertyMetadata(Properties.Settings.Default.ButtonTouchDelay));
public static readonly DependencyProperty IsTouchedProperty =
DependencyProperty.Register("IsTouched", typeof(bool), typeof(TouchButton), new PropertyMetadata(false));
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
"Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TouchButton));
public TouchButton()
{
this.TouchDown +=TouchButton_TouchDown;
this.TouchUp +=TouchButton_TouchUp;
}
// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
add { AddHandler(TapEvent, value); }
remove { RemoveHandler(TapEvent, value); }
}
// This method raises the Tap event
void RaiseTapEvent()
{
if (!_isCancelled)
{
//Console.Beep();
this.IsTouched = true;
Console.WriteLine("RaiseTapEvent");
RoutedEventArgs newEventArgs = new RoutedEventArgs(TouchButton.TapEvent);
RaiseEvent(newEventArgs);
}
}
public bool IsTouched
{
get { return (bool)this.GetValue(IsTouchedProperty); }
set { this.SetValue(IsTouchedProperty, value); }
}
public double DelayElapsed
{
get { return (double)this.GetValue(DelayElapsedProperty); }
set { this.SetValue(DelayElapsedProperty, value); }
}
public int DelayMilliseconds
{
get { return (int)this.GetValue(DelayMillisecondsProperty); }
set { this.SetValue(DelayMillisecondsProperty, value); }
}
//Start the animation and raise the event unless its cancelled
private void BeginDelay()
{
_isCancelled = false;
Console.WriteLine("BeginDelay ");
this._animation = new DoubleAnimationUsingKeyFrames() { FillBehavior = FillBehavior.Stop };
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(this.DelayMilliseconds)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.Completed += (o, e) =>
{
this.DelayElapsed = 0d;
//this.Command.Execute(this.CommandParameter); // Replace with whatever action you want to perform
RaiseTapEvent();
this.IsTouched = false;
};
this.BeginAnimation(DelayElapsedProperty, this._animation);
}
private void CancelDelay()
{
// Cancel animation
_isCancelled = true;
Console.WriteLine("CancelDelay ");
this.BeginAnimation(DelayElapsedProperty, null);
}
private void TouchButton_TouchDown(object sender, System.Windows.Input.TouchEventArgs e)
{
this.BeginDelay();
}
private void TouchButton_TouchUp(object sender, System.Windows.Input.TouchEventArgs e)
{
this.CancelDelay();
}
}
}
Custom animation when IsTouched event is triggered in App.xaml
<Style x:Key="characterKeyT" TargetType="{x:Type local:TouchButton}">
<Setter Property="Focusable" Value="False" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Margin" Value="6,4,8,4"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TouchButton}">
<Grid x:Name="grid">
<Border x:Name="border" CornerRadius="0">
<Border.Background>
<SolidColorBrush x:Name="BackgroundBrush" Color="{Binding Source={StaticResource settingsProvider}, Path=Default.ThemeColorPaleGray2}"/>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.Foreground="Black"
TextElement.FontSize="24"></ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Resources>
<Storyboard x:Key="FadeTimeLine" BeginTime="00:00:00.000" Duration="00:00:02.10">
<ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color"
To="#FF22B0E6"
Duration="00:00:00.10"/>
<ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color"
To="#FFECE8E8"
Duration="00:00:02.00"/>
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsTouched" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FadeTimeLine}"/>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ThemeSolidColorBrushPaleGray}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" TargetName="border" Value="{StaticResource ThemeSolidColorBrushPaleGray2}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
XAML Usage
<UserControl x:Class="TouchButtonApp.Keyboard1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TouchButtonApp"
mc:Ignorable="d"
d:DesignHeight="352" d:DesignWidth="1024">
<Grid>
<Grid Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="90*"/>
<RowDefinition Height="90*"/>
<RowDefinition Height="90*"/>
<RowDefinition Height="90*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
</Grid.ColumnDefinitions>
<local:TouchButton x:Name="qButton" Tap="Button_Click" Content="Q" Grid.Row="1" Style="{DynamicResource characterKeyT}" />
<local:TouchButton x:Name="wButton" Tap="Button_Click" Content="W" Grid.Column="1" Grid.Row="1" Style="{DynamicResource characterKeyT}" />
...

Reference to a basic style from another ResourceDictionary not found

I have this custom control with my own logic
public class BackButton : Button
{
public BackButton()
{
this.DefaultStyleKey = typeof(Button);
this.Click += (s, e) => {
Services.NavigationService.GoBack();
};
}
}
I want to apply to it the default style BackButtonStyle. I do not want to edit StandardStyles.xaml so it's in another ResourceDictionary
<Style TargetType="controls:BackButton" BasedOn="{StaticResource BackButtonStyle}"/>
Referenced in App.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Common/StandardStyles.xaml"/>
<ResourceDictionary Source="Common/FrameworkStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
It is building but this exception is thrown :
Message = "Cannot find a Resource with the Name/Key BackButtonStyle [Line: 15 Position: 44]"
What am i doing wrong ?
BackButtonStyle has target type as Button, so you can't use BackButtonStyle as base style for target type of controls:BackButton.
<Style x:Key="BackButtonStyle" TargetType="Button">

Remove usercontrol resources

I change the style of my user control by changing resource dictionaries. In other words I have:
Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Grid">
<Setter Property="Background" Value="Green"></Setter>
</Style>
<SolidColorBrush x:Key="Foo" Color="Blue"></SolidColorBrush>
</ResourceDictionary>
Dictionary2.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Grid">
<Setter Property="Background" Value="Black"></Setter>
</Style>
<SolidColorBrush x:Key="Foo" Color="Orange"></SolidColorBrush>
</ResourceDictionary>
UserControl1:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="103" d:DesignWidth="101">
<Grid >
<Ellipse Fill="{DynamicResource Foo}" />
</Grid>
</UserControl>
Code Behind
namespace WpfApplication1
{
using System; using System.Windows; using System.Windows.Controls;
public partial class UserControl1 : UserControl
{
public enum ControlTheme
{
Theme1 , Theme2
}
public UserControl1 ( )
{
InitializeComponent( );
}
public void ChangeTheme ( ControlTheme theme )
{
Resources.MergedDictionaries.Clear( );
int dic = 2;
if ( theme == ControlTheme.Theme1 )
dic = 1;
ResourceDictionary rd = new ResourceDictionary( );
rd.Source = new Uri( #"pack://application:,,,/WpfApplication1;component/Dictionary" + dic + ".xaml" );
Resources.MergedDictionaries.Add( rd );
}
}
}
Now I am able to change themes dynamically by calling the method: ChangeTheme
The problem that I have now is that if I place:
<UserControl.Resources>
<ResourceDictionary Source="Dictionary1.xaml" ></ResourceDictionary>
</UserControl.Resources>
on UserControl1 The method ChangeTheme no longer works. I am looking for a method that does something like:
//PseudoCode
var itemToRemove = this.UserControlResources.resources.where(x=> x.isDictionary==true);
this.UserControlResources.Remove(itemToRemove);
You are setting your a Dictionary in the Xaml without any MergedDictionary's so when you create your Merged Dictinarys they are being overridden by the base Dictionary. You can try one of two things.
The first being is to Create a MergedDictionary in your UserControls Xaml. This will work without changing your CodeBehind.
i.e.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
The second would be to assign your newly created ResourceDictionary to the UserControls Resources thus overwriting the pre-existing ResourceDictionary. This will work without changing your Xaml.
i.e.
public void ChangeTheme(ControlTheme theme)
{
int dic = 2;
if (theme == ControlTheme.Theme1)
dic = 1;
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri(#"pack://application:,,,/WpfApplication1;component/Dictionary" + dic + ".xaml");
this.Resources.Clear();
this.Resources = rd;
}

Categories

Resources