I am trying to detect changes in the Application.Resources Resource dictionary, so I can automatically change the Titlebar to the Accent Colour when it updates. All of the XAML controls and elements change automatically, and when setting a solid colour brush to the address of the DSDFS brush, its internal value changes.
This is the code I have tried to use to detect the change:
public static DependencyProperty accent = DependencyProperty.Register("DictChange", typeof(ResourceDictionary), typeof(Shell), new PropertyMetadata(Application.Current.Resources, new PropertyChangedCallback(accent_PropertyChanged)));
public ResourceDictionary DictChange
{
get { return (ResourceDictionary)GetValue(accent); }
set { SetValue(accent, value); }
}
private static void accent_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
_app.SetTitlebar();
}
I'm assuming its wrong though, or I'm not sure if that is the right thing to do to detect changes. There was a previous iteration where I used Application.Current.Resources["SystemControlBackgroundAccentBrush"] as SolidColorBrush and tried to detect its property, but that didn't work either.
What am I doing wrong? Please help :)
It might not be wrong, but it's probably not the best solution available.
In WinRT XAML, we have this new ThemeResource that updates the resources automatically. The tricky bit is to find a way to bind the ApplicationView.GetForCurrentView().TitleBar.BackgroundColor to SystemControlBackgroundAccentBrush.
In my answer to this question, I created a Behavior that attaches a custom TitleBar to the page. If you modify the Background property to something like this -
<local:FullScreenModeTitleBarBehavior Background="{ThemeResource SystemControlBackgroundAccentBrush}" />
Now run the app and you will see the background color gets updated when you change the accent color of the system, as shown in the picture below -
Basically in your case, you just need to create a similar (& simpler?) Behavior that acts like a bridge to link the BackgroundColor of the TitleBar to the SystemControlBackgroundAccentBrush, via ThemeResource binding.
Hope this helps!
I'm assuming its wrong though, or I'm not sure if that is the right thing to do to detect changes.
You cannot detect the key-value changes in the Resource Dictionary by registering a DependencyProperty because the ResourceDictionay is not an ObservableCollection Class.
There are no build-in support to detect the key-value changes in a Resource Dictionary.
As a workaround, you can consider create an internal observable collection to detect the changes.
Related
Overview
I am currently working on a theme system for my application allowing Light and Dark themes for users to choose from (similar to visual studio). The entire process is pretty straight forward so far; however there are a few things that I'm still having a couple issues (well, more of wanting to know if there are better ways to do it). During the process of development I have put in quite a bit of research into changing the selection color of controls that support it (ListView, ComboBox, ListBox, TextBox, RichTextBox, etc). I have found that the most straight forward way of accomplishing this (especially with list style controls) is to perform custom drawing of the items by utilizing the events available for each control. I know that RichTextBox has the SelectionBackColor property that will allow you to change it's selection color, however the TextBox control does not offer this same property, nor anything similar that I have found.
With the ListBox control, it does not contain any sort of selection color property for further customization, meaning I have to utilize OwnerDraw and the DrawItem/DrawSubItem events to custom paint the back color while setting the HideSelection property to false to help with the change when focus is lost.
The ComboBox and ListBox controls are simple enough to change by utilizing the DrawItem event and setting it up for OwnerDraw like the ListBox control.
As we all know there are many different controls that support user selection, and I could go on and on into my research, but I think what I have supplied is enough to help describe why I am here.
The Question
Since the code to manipulate all of this via a single class is seriously tedious, and restrictive to future developers (especially if they are performing custom drawing of items in these controls). I am wondering if there is a simplified way to manipulate just the selection color. I have already found a post here on StackOverflow that utilizes DllImport to manipulate the system highlight color; however, changing things at the OS level doesn't seem like a smart, nor safe thing to do for something so simple. I am just as reluctant to manipulate system code as I am to blindly manipulate kernel registers in MIPS Assembly.
The post I found on StackOverflow utilized:
[DllImport("user32.dll")]
static extern bool SetSysColors(int cElements, int[] lpaElements, uint[] lpaRgbValues);
void ChangeSelectColour(Color color) {
const int COLOR_HIGHLIGHT = 13;
const int COLOR_HIGHLIGHTTEXT = 14;
// You will have to set the HighlightText colour if you want to change that as well.
//array of elements to change
int[] elements = { COLOR_HIGHLIGHT };
List<uint> colours = new List<uint>();
colours.Add((uint)ColorTranslator.ToWin32(color));
//set the desktop color using p/invoke
SetSysColors(elements.Length, elements, colours.ToArray());
}
Is this the only way of doing this? If so I can abandon the idea of changing the selection color throughout the application since this doesn't seem too safe (unless someone can prove otherwise).
My current implementation (the hard way referred to in the overview section) is like this (just a small sample):
public static void ApplyStyles(Control c) {
if (c is Button) {
Button b = (Button)c;
b.FlatStyle = FlatStyle.Flat;
b.FlatAppearance.BorderSize = 0;
}
if (c is RoundedPanel || c is PictureBox) {
// Do nothing.
} else {
if (c is Label) {
if (c.Parent is RoundedPanel) {
// Do nothing.
} else {
c.BackColor = BackColor;
c.ForeColor = ForeColor;
}
} else {
c.BackColor = BackColor;
c.ForeColor = ForeColor;
}
if (c is ListView) {
ListView lv = (ListView)c;
// PSEUDO BEGIN
lv.OwnerDraw = true;
lv.DrawItem += DrawListViewItem;
lv.DrawSubItem += DrawListViewSubItem;
// PSEUDO END
if (Style = Themes.Dark)
lv.GridLines = false;
foreach (ListViewItem lvi in lv.Items) {
lvi.BackColor = BackColor;
lvi.ForeColor = ForeColor;
}
}
}
}
Now if you could imagine having to do that for every type that supports user selection, you end up with several conditions on a generic type that then create custom events to paint the items contained inside the control, which can get even more difficult when you start looking into the support for images, check boxes, so on, and so forth. This code can expand far and wide pretty rapidly if there aren't any generic methods to change the selection color, and in my case wouldn't be worth the effort to do (unless I made it a very low priority and worked on it occasionally through the years).
Also, keep in mind when you make suggestions; I work in a government environment so third party products all have to be approved, and in my case, a 100% solid reason as to why we need that product would have to be supplied for approval so third party theming products wouldn't really help in my case.
I thank you all for taking the time to read this, and if anyone has any ideas, please feel free to let me know!
NOTE
If you feel I left out any needed information, or if I should be any clearer on certain things, feel free to comment and let me know so that I can update the clarity of the post for future readers.
References
RichTextBox.SelectionBackColor
Use SelectionBackColor to get or set the color of selected text in the RichTextBox. If no text is currently selected, the SelectionBackColor property applies to the current position of the caret. Characters that are entered from that position have the specified SelectionBackColor.
ComboBox.DrawItem
This event is used by an owner-drawn ComboBox. You can use this event to perform the tasks needed to draw items in the ComboBox.
ListBox.DrawItem
This event is used by an owner-drawn ListBox. The event is only raised when the DrawMode property is set to DrawMode.OwnerDrawFixed or DrawMode.OwnerDrawVariable. You can use this event to perform the tasks needed to draw items in the ListBox.
Additional Links
ComboBox.DrawItem Event
ListBox.DrawItem Event
Friends,
I am assigning the Background of RootFrame to application resources, It works when you explicitly write the Resource name like below
App.RootFrame.Background = (System.Windows.Media.ImageBrush)App.Current.Resources["Theme_6"];
but If I use below it doesn't work:
string themeName = "Theme_6";
App.RootFrame.Background = (System.Windows.Media.ImageBrush)App.Current.Resources[themeName];
Is it possible to use the 2nd options in wp8?
Thanks!
Panorama control (and I think Pivot controls as well) has some problem supporting late binding for background images.
When you hard-code the image paths, there is no problem displaying a static background image.
To assign background images 'on the fly' you should follow these steps;
Create a Property (MainBackGroundImage) within your associated
ViewModel, that implements INotifyPropertyChanged interface (if you
are using MVVM pattern you already have this infrastructure).
Assign any image path (this can be a remote URL as well) to this
property whenever you want to change the background image.
In your View hook up to your ViewModel's property changed event and
update the layout of the control where background image is going to
appear:
void viewModel_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (e.PropertyName == “MainBackGroundImage”)
{
this.MainPanorama.UpdateLayout();
}
}
You might perhaps take a look at my open-source WP8 application where I did achieve dynamic background images.
I have a C# WPF application with a bunch of labels.
When I run my program it does some checks and wether it the check was positive or not it sets it's corrisponding label to green og red.
These changes is done in my .cs file like:
lblCheck14.Foreground = new SolidColorBrush(Colors.Green);
I would like to add a "Reset" button, that reset the application to it's initial start.
How can I easiest implement this?
One way - but I really hope there is a smarter way, is to set them all like:
lblCheck14.Foreground = new SolidColorBrush(Colors.Black);
lblCheck21.Foreground = new SolidColorBrush(Colors.Black);
lblCheck42.Foreground = new SolidColorBrush(Colors.Black);
Etc..
But isn't there a function which I can call that strips away any changes the .cs file have done to the controls in the XAML file? Like make the XAML back to stock?
Sorry for my back explanation. Hope you understand me :)
Best regards
Implement styles. You can have a default style to roll back to when you hit reset.
Take a look at this tutorial if you're unfamiliar with them: http://wpftutorial.net/Styles.html
Do not manipulate UIElements' properties in code. WPF is not winforms. As Yatrix's answer said, implement styles, or even datatemplates and triggers to manipulate different properties of different UIElements acording to some logic (defined in ViewModel or somewhere else). I suggest you to take a look at WPFTutorial.net
I have made a custom trackbar control, mostly as an exercise. I know i could/should have just inherited what i needed instead of reinventing the wheel, but i have learned a lot during my endeavour.
Now, i have a lot of properties, and all of them shows up in the designer apart from a couple of image properties. This is what i have, modelled on the other working properties (those are ints and Colors and what not, and they all work as expected...), so maybe i should be doing Images in some other way. Bottom line, i don't know what i'm doing :)
EDIT : My custom control is in a Windows Forms solution (VC# 2008 Express), and to clarify, my problem is that some of my control's properties (the Image properties) isn't showing in the properties-tab during design-time.
EDIT 2 : After reading up on DependencyProperty and totally failed to understand that concept (I'm very noob at programmin teh codez or what you gurus call this black voodoo magic??). I have gotten used to letting the IDE fix all my troubles, and i was pleased to see the IDE happily showing my other properties, such as Color BarColor, int Value etc. etc. Why would Image LeftImage be any different, a lot of the standard controls have Image properties and maybe it's naïve of me to think that the IDE can figure all of my mistakes out, but surely the guys at Microsoft did not construct a new editor every time they had to set an image property in their controls. My bet is that they reused something, which i should be able to do as well.
I'm stuck :(
Here's my crappy CoD3Z anyway:
private Image _LeftImage;
/// <summary>
/// Sets the small image appearing to the left of the trackbar
/// </summary>
[Description("The small image appearing to the left of the trackbar"),
Category("Appearance"),
DefaultValue(typeof(Image),"null"),
Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public Image LeftImage
{
private get { return _LeftImage; }
set
{
if (value.Height != 16 || value.Width != 16)
{
_LeftImage = new Bitmap(value,new Size(16,16));
}
else _LeftImage = value;
Invalidate();
}
}
By the way, what is the difference between DefaultValue and DefaultValueAttribute?
Thanks for any pointers and help!
/Mikael
I have found something here:
http://msdn.microsoft.com/en-us/library/system.drawing.design.imageeditor.aspx
Consider this solved.
For Show the Image in the Designer you need to overrides the OnPaint Method drawing it.
DefaultValue is the same that DefaultValueAttribute. All Attributes in .NET are clases that inherits from System.Attribute, and all those class are something like Name*Attribute* where Attribute is fixed. Then "DefaultValue" is like an Alias.
Your example doesn't show what exactly is your problem but if you create custom control and want to access its properties from designer you should use smth like that:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(CustomControl),
new FrameworkPropertyMetadata(null, ValueChanged));
hope this helps you
2 Questions :
Firstly:
Is it possible to Toggle Transparency on a WPF window? Any pointers greatly appreciated!
Secondly:
Most controls on my window inherit their Transparancy from the parent window, however I have a Datagrid control with its own style - The style is in an external file that I reference (Style="{DynamicResource MyDGStyle}")..... in the xaml code behind can I switch Styles? (Ideally I would achieve this using a Style Trigger, but don't think I can).
Thanks very much
Joe
Edit (can't seem to reply)
Thanks alex, NVM
Regarding the Toggling Transparency, as long as I can set the 'Background' property of the Window at runtime from a color to 'Transparent' at that runtime, thats fine.
Regarding switching styles, just extending your code alex, presumably I can do something like
void OnButtonPress()
{
var transparentStyle = Themes.CurrentTheme.MyDGNonTransparentStyle;
var nonTransparentStyle = Themes.CurrentTheme.MyDGNonTransparentStyle;
if (isTransparent) // Change to Non-Transparent
this.MyGrid.Style = (Style)this.FindResource(nonTransparentStyle);
else // Change to Transparent
this.MyGrid.Style = (Style)this.FindResource(nonTransparentStyle);
}
?
Thanks
Joe
3rd Edit
Thanks guys,
Sorry to confuse you - my second question was since my datagrid has its own style (and doesn't inherit from the window) I will need to set its style depending on the current state (Transparent / Non-ransparent) - so I need to change the datagrid style at runtime - now since this can be done with a window, can I assume it can be done with a datagrid?
Thanks
Joe
Is it possible to Toggle Transparency on a WPF window?
Yes, it is:
<Window WindowStyle="None"
AllowsTransparency="True"
Background="#88aa3366">
</Window/>
The bad news is that you have to implement the logic of window header by yourself.
This article might be helpfull.
in the xaml code behind can I switch Styles?
The question is a little bit unclear, maybe this helps:
var key = Themes.CurrentTheme.MyDGStyle;
this.MyGrid.Style = (Style)this.FindResource(key);