Finally managed to get the Colorpickers down and sorted and they currently work on a OnNavigateTo basis.
When I pick a color from color picker I would like to to be applied eith instantly to the Foreground of my NavigationViewItems OR apply once i click the button within the settings page called TextColourApply_Click.
The mentioned color picker is on the settingspage currently and the NavigationViewItems are on the MainPage.
I was looking at doing a UI refresh but this doesnt work with UWP as far as i can tell. As a work around, i was looking at doing a current Frame navigate but this doesnt work
I have the following that allows the selected colour to apply when navigating back to the "MainPage":
protected override void OnNavigatedTo(NavigationEventArgs e)
{
SolidColorBrush DefaultTextColour = Application.Current.Resources["DefaultTextColour"] as SolidColorBrush;
if (ColourSelections.TextColour != null)
{
DefaultTextColour = ColourSelections.TextColour;
}
foreach (var item in NavView.MenuItems.OfType<NavigationViewItem>())
{
item.Foreground = DefaultTextColour;
}
}
Any ideas on how to impliment this would be appreciated. Thank you
if your desired behaviour is following :
When I pick a color from color picker it should be applied instantly to the Foreground of my NavigationViewItems and color picker is on settings page.
In that case you do not need OnNavigatedTo on your MainPage and you do not need the Apply as well, So remove your OnNavigatedTo method also remove the Apply button from your settings page and then Just do the following:
Create a public static property within your ShellPage (the page where your NavigationView exists) that will expose your NavigationView, and make sure to initialize it within the constructor of your ShellPage.
public static NavigationView MyNavView;
public ShellPage()
{
this.InitializeComponent();
MyNavView = NavView; //here you assign your navigation view to the public static property so you can access it outside this shell page as well.
}
Now within the colorChanged event (in your settings page) of your color picker assign the color to the foreground of your navigationmenuItems.
private void TextColourPicker_ColorChanged(ColorPicker sender, ColorChangedEventArgs args)
{
SolidColorBrush DefaultTextColour = new SolidColorBrush(TextColourPicker.Color);
foreach (var item in ShellPage.MyNavView.MenuItems.OfType<NavigationViewItem>())
{
item.Foreground = DefaultTextColour;
}
}
and just to make sure that whenever your app is loaded for the first time you get the default color set in your resources, assign a Loaded event to your NavigationView and set the default color in there.
add the loaded event in xaml like this :
<NavigationView x:Name="NavView" Loaded="NavView_Loaded">
and the event in your backend will be :
private void NavView_Loaded(object sender, object args)
{
SolidColorBrush DefaultTextColour = Application.Current.Resources["DefaultTextColour"] as SolidColorBrush;
foreach (var item in NavView.MenuItems.OfType<NavigationViewItem>())
{
item.Foreground = DefaultTextColour;
}
}
Please note that now you do not even need the public static class you were using before for saving the colors, so you can remove that class as well.
Related
I'm trying to add a Label to a Windows Form by using another class programmatically. My Label does not appear inside the Form.
I don't know where I'm going wrong.
private void Form1_Load(object sender, EventArgs e)
{
Ticker ticker = new Ticker("ASDF");
ticker.display();
}
public class Ticker : Label
{
string labelText;
Label label = new Label();
public Ticker(string _labelText)
{
labelText = _labelText;
}
public void display()
{
label.Text = labelText;
Controls.Add(label);
}
}
You can make a few changes to your Ticker Custom Control:
You don't need to create a new Label inside your Custom Control: your Control is already a Label, use the this reference to set its properties (see also this keyword (C# Reference)).
The Text is the current Label's Text (this.Text). Store it if you need a copy of it for other reasons (custom painting, usually, so sometimes you need to clear the Text).
Controls is referring to the current class object: it's a Control, so it has a Controls property, which gets the ControlCollection of the child Controls of a Control.
You need to also specify a Point that defines the position of your Custom Control inside its Parent's ClientRectangle.
Even if it's not always required, add a parameter-less Constructor to your Custom Control: if/when it's actually needed, you'll have it already there.
If you don't want to set the Parent Control from the outside, as usual (e.g., var label = new Label(); this.Controls.Add(label);), you need to pass the reference of the Control which will become the Parent Control of your custom Label.
You can the use this reference - a Control type of reference - and add your Label to the Controls collection of the Control reference you receive:
// You want to store a reference to this Control if you need it later...
private Ticker ticker = null;
private void Form1_Load(object sender, EventArgs e)
{
//... or just declare it with: var ticker = new Ticker() if you don't
ticker = new Ticker("The Label's Text");
// [this] of course refers the current class object, Form1
ticker.Display(this, new Point(100, 100));
// Or, display the Label inside a Panel, child of Form1
// Note: if you don't comment the next line, the Label will be moved to panel1
ticker.Display(this.panel1, new Point(10, 50));
}
Here, I'm overloading the Display() method, so it accepts both a Parent reference and a Point value, used to position the Control inside its Parent's Client Area.
The Custom Label also calls BringToFront() on itself, to avoid showing up under some other, already existing, child Control of the new Parent.
public class Ticker : Label
{
public Ticker() : this("ticker") { }
public Ticker(string labelText) => this.Text = labelText;
public void Display(Control parent) => Display(parent, Point.Empty);
public void Display(Control parent, Point position)
{
this.Location = position;
parent.Controls.Add(this);
this.BringToFront();
}
}
The app has a GridView in which each item is a color that the user can choose to customize the UI overriding the default SystemAccentColor (the one is defined by user on their system).
I managed to get the color of the item but even though I assign it as new value for SystemAccentColor I am not able to update the UI.
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
// FIRST APROACH -----
GridViewItem gridViewItem = GVColors.ContainerFromItem(e.ClickedItem) as GridViewItem;
Ellipse ellipseItem = gridViewItem.FindDescendant<Ellipse>();
var theColor = (SolidColorBrush)ellipseItem.Fill;
Application.Current.Resources["SystemAccentColor"] = theColor;
// SECOND APPROACH ----
Windows.UI.Color theColor2 = new Windows.UI.Color
{
A = 1,
R = 176,
G = 37,
B = 37
};
var root = (FrameworkElement)Window.Current.Content;
root.Resources["SystemAccentColor"] = theColor2;
}
I'm currently reading this blog entry XAML Brewer, by Diederik Krols: Using a Dynamic System Accent Color in UWP but I want to know if the community knows another approach to change the accent color at runtime (or a method that I'm not aware of to Update/refresh the UI).
I assign it as new value for SystemAccentColor I am not able to update the UI.
Since you statically bind SystemAccentColor and it doesn't implement INotifyPropertyChanged interface, event though the value of SystemAccentColor changes, the UI which bound with it won't update directly.
Based on your requirement, you can add a class which implements INotifyPropertyChanged interface and add the SystemAccentColor as property in it. Then init the class instance in Application.Resources. After that, bind the UI with the SystemAccentColor property. For example, I create a class named SystemAccentColorSetting.
SystemAccentColorSetting.cs:
public class SystemAccentColorSetting : INotifyPropertyChanged
{
private SolidColorBrush systemAccentColor = new SolidColorBrush(Colors.Red);
public SolidColorBrush SystemAccentColor
{
get {
return systemAccentColor;
}
set {
systemAccentColor = value; OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
App.xaml:
<Application.Resources>
<ResourceDictionary>
<local:SystemAccentColorSetting x:Key="SystemAccentColorSetting"/>
</ResourceDictionary>
</Application.Resources>
Usage:
Assume that we bind the Background of Button with the SystemAccentColor property.
.xaml:
<Button x:Name="MyButton" Background="{Binding SystemAccentColor, Source={StaticResource SystemAccentColorSetting}}">hello</Button>
.cs:
If you want to change the value of Background, just change the SystemAccentColor property.
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
GridViewItem gridViewItem = GVColors.ContainerFromItem(e.ClickedItem) as GridViewItem;
Ellipse ellipseItem = gridViewItem.FindDescendant<Ellipse>();
var theColor = (SolidColorBrush)ellipseItem.Fill;
((SystemAccentColorSetting)Application.Current.Resources["SystemAccentColorSetting"]).SystemAccentColor = theColor;
}
Starting from Win 10 1809 (build 17763), you can use the ColorPaletteResources Class.
By using it, you can change not only the Accent color at runtime, but also all the other system default colors for UI controls.
Due to a bug, you forst have to declare that ColorPaletteResources object into your App's XAML resources (with a key) and then you can use it at runtime.
Another bug is that, from what I have experimented some time ago, you can only change the Accent color for now, so you are lucky.
To see it in action, you can download Fluent XAML Theme Editor from the Windows Store or from GitHub.
Here is the link to the class itself and to some guidelines on how to use it.
I'm a beginner, and I think that the solution is very simple, but I can't find it all over the Internet.
I'm looking for a way of setting the back color of forms and controls to a certain color variable so if I change its value to green for exapmple, every control that its back color set to mainColor will turn green and that the changes will show up in the designer.
public class MainForm:Form
{
public static Color mainColor=[some color];
public static Color secColor=[some color];
public Main()
{
InitializeComponent();
BackColor=mainColor;
control1.BackColor=secColor;
control2.BackColor=secColor;
control3.BackColor=secColor;
}
}
Like that by changing mainColor and secColor. The controls are changed, but it wont show up in the designer. What is the right way of doing it?
Use the Colors class.
public static Color redColor = Colors.Red;
public static Color greenColor = Colors.Green;
public static Color blueColor = Colors.Blue;
public static Color whiteColor = Colors.White;
Here is a pallete of the available colors:
If you want to create a new color, use Color.FromArgb();
Check out this answer for more information on new colors.
Yes, that is called DataBinding, and it's done partly with the Designer and partly with code.
Instead of declaring
public static Color mainColor=[some color];
declare it as a property:
public Color MyColor
{
get
{
return myColor;
}
set
{
myColor = value;
}
}
In your main form, edit each of the controls that you want to be influenced by this variable to bind their color property to it. I'll use a Panel as the sample control, so add some Panel objects to your form. Then in each Panel object, in the Properties panel, click the + next to DataBindings, then click in the empty box next to (Advanced). Click the ellipsis (...) and select the BackColor property. Then, under the Binding dropdown, select Add Project Data Source. Select Object in the next dialog and navigate to your form, and select that. A list of properties will then appear in the Formatting and Advanced Binding dialog box and you can select your property MyColor.
Note that once you have created this data source for the first one, you won't need to recreate it for each Panel - just reference the one you created already.
You can then change the BackColor of any of the controls you did this databinding on by changing the value of the MyColor property in your program. They will all change together.
You will probably also need to arrange that property to broadcast the message that it has changed, so add this line to the set() method.
form1BindingSource.ResetBindings(false);
so that the set method looks like this.
set
{
myColor = value;
form1BindingSource.ResetBindings(false);
}
That tells the binding source object to notify all subscribers to update themselves, and it will happen automatically every time the MyColor property is changed.
I've got a void that analyses an image, extracts two dominant colors, and replaces "SystemControlForegroundAccentBrush" and "SystemControlHighlightAccentBrush", that I'm overriding in App.xaml. It works well, except that it takes a bit of time to analyse the image, so it changes the colors once all the controls in my page have already loaded.
As a result, they stay the old accent color. How can I get them to bind dynamically to that ThemeRessource?
Here's my app.xaml:
<Application.Resources>
<ResourceDictionary x:Name="resdic">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="SystemControlForegroundAccentBrush"/>
<SolidColorBrush x:Key="SystemControlHighlightAccentBrush"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
This is the (very simplified) part of the ColorExtractor.cs class that changes the colors:
public async static void Analyse()
{
Application.Current.Resources["SystemControlForegroundAccentBrush"] = new SolidColorBrush(color);
Application.Current.Resources["SystemControlHighlightAccentBrush"] = new SolidColorBrush(color2);
}
And I've got a bunch of controls in Page.xaml that have their Foreground set as such:
<TextBlock Foreground="{ThemeResource SystemControlForegroundAccentBrush]"/>
I call ColorExtractor.Analyse() in my Page.xaml.cs (at the OnNavigatedTo event). I can always create an event that gets fired once the colors are set, but I need to find a way to update the colors of all the controls in my page once that's done.
You can have a look at how Template 10 does theming changes, but in their case they are defining two different themes in resource dictionaries in advance. You can find their code in the repo on Github, but here are some of the code used:
(Window.Current.Content as FrameworkElement).RequestedTheme = value.ToElementTheme();
Views.Shell.SetRequestedTheme(value, UseBackgroundChecked);
From here
public static void SetRequestedTheme(ApplicationTheme theme, bool UseBackgroundChecked)
{
WindowWrapper.Current().Dispatcher.Dispatch(() =>
{
(Window.Current.Content as FrameworkElement).RequestedTheme = theme.ToElementTheme();
ParseStyleforThemes(theme);
HamburgerMenu.NavButtonCheckedForeground = NavButtonCheckedForegroundBrush;
HamburgerMenu.NavButtonCheckedBackground = (UseBackgroundChecked) ?
NavButtonCheckedBackgroundBrush : NavButtonBackgroundBrush;
HamburgerMenu.NavButtonCheckedIndicatorBrush = (UseBackgroundChecked) ?
Colors.Transparent.ToSolidColorBrush() : NavButtonCheckedIndicatorBrush;
HamburgerMenu.SecondarySeparator = SecondarySeparatorBrush;
List<HamburgerButtonInfo> NavButtons = HamburgerMenu.PrimaryButtons.ToList();
NavButtons.InsertRange(NavButtons.Count, HamburgerMenu.SecondaryButtons.ToList());
List<HamburgerMenu.InfoElement> LoadedNavButtons = new List<HamburgerMenu.InfoElement>();
foreach (var hbi in NavButtons)
{
StackPanel sp = hbi.Content as StackPanel;
if (hbi.ButtonType == HamburgerButtonInfo.ButtonTypes.Literal) continue;
ToggleButton tBtn = sp.Parent as ToggleButton;
Button btn = sp.Parent as Button;
if (tBtn != null)
{
var button = new HamburgerMenu.InfoElement(tBtn);
LoadedNavButtons.Add(button);
}
else if (btn != null)
{
var button = new HamburgerMenu.InfoElement(btn);
LoadedNavButtons.Add(button);
continue;
}
else
{
continue;
}
Rectangle indicator = tBtn.FirstChild<Rectangle>();
indicator.Visibility = ((!hbi.IsChecked ?? false)) ? Visibility.Collapsed : Visibility.Visible;
if (!hbi.IsChecked ?? false) continue;
ContentPresenter cp = tBtn.FirstAncestor<ContentPresenter>();
cp.Background = NavButtonCheckedBackgroundBrush;
cp.Foreground = NavButtonCheckedForegroundBrush;
}
LoadedNavButtons.ForEach(x => x.RefreshVisualState());
});
}
From here
I've got a void that analyses an image, extracts two dominant colors, and replaces "SystemControlForegroundAccentBrush" and "SystemControlHighlightAccentBrush", that I'm overriding in App.xaml.
First I don't think your code in app.xaml can override the ThemeResource, and you used this Brush in the Page like this:
<TextBlock Foreground="{ThemeResource SystemControlForegroundAccentBrush}" Text="Hello World!" FontSize="50" />
If you press "F12" on the SystemControlForegroundAccentBrush, you can find this resource actually in the "generic.xaml" file.
Now suppose your ColorExtractor.cs class works fine and ColorExtractor.Analyse() can override the color of those two brushes, and you have many controls in the page uses these two resources, refreshing the Page here can solve your problem.
But I think it's better that not put this operation in OnNavagateTo event or Page.Loaded event, there is no refresh method in UWP, we use navigating again to this page to refresh, so if putting this operation in OnNavagateTo event or Page.Loaded event, every time you navigate to this page, the resources will be overrided and it will navigate again. So I put this operation in a Button click event like this:
public bool Reload()
{
return Reload(null);
}
private bool Reload(object param)
{
Type type = this.Frame.CurrentSourcePageType;
if (this.Frame.BackStack.Any())
{
type = this.Frame.BackStack.Last().SourcePageType;
param = this.Frame.BackStack.Last().Parameter;
}
try { return this.Frame.Navigate(type, param); }
finally
{
this.Frame.BackStack.Remove(this.Frame.BackStack.Last());
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ColorExtractor.Analyse();
Reload();
}
In the end, I decided to create an event in ColorExtractor.cs :
public static event EventHandler Analysed;
public async static void Analyse(BitmapImage poster)
{
//Analyse colors
Analysed(null, null);
}
And then, on my MainPage.xaml.cs:
ColorExtractor.Analyse(bmp);
ColorExtractor.Analysed += (sender, EventArgs) =>
{
//Set Page foreground color, as a lot of controls are dynamically binded to their parent's foreground brush.
//If a control isn't automatically binded, all I have to do is say: Foreground="{Binding Foreground, ElementName=page}"
page.Foreground = Application.Current.Resources["SystemControlForegroundAccentBrush"] as SolidColorBrush;
page.BorderBrush = Application.Current.Resources["SystemControlHighlightAccentBrush"] as SolidColorBrush;
//Reload any custom user control that sets it's children's color when it's loaded.
backdrop.UserControl_Loaded(null, null);
};
So I'm not actually binding my controls to the ForegroundAccentBrush directly, but this works without needing to re-navigate to the page.
I'm trying to achieve an effect where more items are appended to the list when the user scrolls down to the last item. I haven't found a way to determine if the user has scrolled to the end of the list. I don't see a event on ListBox that is fired when the user reaches the bottom of the list. Something that tells me when an item has been scrolled into view would be great, but as far as I can tell, there is nothing like that.
Is this even possible in WP7?
Edit: Another way of saying this is, can we detect when a list has "bounced"?
Daniel Vaughan has posted an example of how to detect for this at http://danielvaughan.orpius.com/post/Scroll-Based-Data-Loading-in-Windows-Phone-7.aspx
It isn't super easy to get going since there are a lot of moving parts, but here is what you can do, assuming you want a short list that loads more from your data as you get scrolling down, similar to a lot of twitter apps, etc.
Write your own subclass of ObservableCollection that only offers up a few items (like 20), keeping the rest held back until requested
Hook up to the scroll viewer (inside the listbox or container) and its visual state changed events, you can get the NotScrolling and Scrolling changes; for an example see this code by ptorr
When scrolling stops, use viewer scroll extensions code to see where things are extended (at the bottom or not) or just the raw scroll viewer properties to see if it is extended to the bottom
If so, trigger your observable collection to release another set of items.
Sorry I don't have a complete sample ready to blog yet. Good luck!
I've just implemented this for Overflow7.
The approach I took was similar to http://blog.slimcode.com/2010/09/11/detect-when-a-listbox-scrolls-to-its-end-wp7/
However, instead of using a Style I did the hook up in code.
Basically derived my parent UserControl from:
public class BaseExtendedListUserControl : UserControl
{
DependencyProperty ListVerticalOffsetProperty = DependencyProperty.Register(
"ListVerticalOffset",
typeof(double),
typeof(BaseExtendedListUserControl),
new PropertyMetadata(new PropertyChangedCallback(OnListVerticalOffsetChanged)));
private ScrollViewer _listScrollViewer;
protected void EnsureBoundToScrollViewer()
{
if (_listScrollViewer != null)
return;
var elements = VisualTreeHelper.FindElementsInHostCoordinates(new Rect(0,0,this.Width, this.Height), this);
_listScrollViewer = elements.Where(x => x is ScrollViewer).FirstOrDefault() as ScrollViewer;
if (_listScrollViewer == null)
return;
Binding binding = new Binding();
binding.Source = _listScrollViewer;
binding.Path = new PropertyPath("VerticalOffset");
binding.Mode = BindingMode.OneWay;
this.SetBinding(ListVerticalOffsetProperty, binding);
}
public double ListVerticalOffset
{
get { return (double)this.GetValue(ListVerticalOffsetProperty); }
set { this.SetValue(ListVerticalOffsetProperty, value); }
}
private static void OnListVerticalOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
BaseExtendedListUserControl control = obj as BaseExtendedListUserControl;
control.OnListVerticalOffsetChanged();
}
private void OnListVerticalOffsetChanged()
{
OnListVerticalOffsetChanged(_listScrollViewer);
}
protected virtual void OnListVerticalOffsetChanged(ScrollViewer s)
{
// do nothing
}
}
this then meant that in the user control itself I could just use:
protected override void OnListVerticalOffsetChanged(ScrollViewer viewer)
{
// Trigger when at the end of the viewport
if (viewer.VerticalOffset >= viewer.ScrollableHeight)
{
if (MoreClick != null)
{
MoreClick(this, new RoutedEventArgs());
}
}
}
private void ListBox1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
EnsureBoundToScrollViewer();
}
The "hacky" thing here was that I had to use ListBox1_ManipulationCompleted and VisualTreeHelper to find my ScrollViewer - I'm sure there are better ways...
Have a look at this detect Listbox compression state from msdn blog
Use the DeferredLoadListBox.