Setting resource styles dynamically through binding in WPF - wpf

I am trying to make the color scheme of my application dynamic so that I can have a color value in a property(hopefully coming from the database) that determines the color scheme of my application.
I have a Resources.xaml file where I set my colors and styles for the application, which I then use throughout all my controls and windows. I would like to bind the Color of a SolidColorBrush in the resources file to a property in my ViewModel(s) so that this color can change based on the current application value. Here is what I have so far, but it isn't working so I must be missing something.
Code in the Resources.xaml file:
<SolidColorBrush x:Key="ApplicationMainBackgroundBrush" Color="{Binding Path=MainApplicationColor, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ApplicationArchitecture:ViewModelBase}, Mode=FindAncestor}, FallbackValue=CornflowerBlue}"/>
Code in the MainWindow.xaml file:
<Grid Grid.Row="0" x:Name="gridControl" Background="{DynamicResource ApplicationMainBackgroundBrush}">
The DataContext of my MainWindow.xaml is a class called ApplicationViewModel, which inherits from ViewModelBase, which has a property "MainApplicationColor" returning the string "Teal" to change the color of that SolidColorBrush from it's FallbackValue. I'm hard coding the color for now, but this is where I would like to get my value from the database in the future. The color is currently not changing, so I'm assuming there is something wrong in my binding source as it is clearly not working like I think it should.
Thanks,
Klara

The problem seems to be your SolidColorBrush.Color property's Binding.
There the ancestor type should be ApplicationArchitecture:MainWindow and not ApplicationArchitecture:ViewModelBase.
The Path should include the DataContext in it.
Like this....
<SolidColorBrush x:Key="ApplicationMainBackgroundBrush"
Color="{Binding Path=DataContext.MainApplicationColor,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ApplicationArchitecture:MainWindow},
Mode=FindAncestor},
FallbackValue=CornflowerBlue}"/>
Let me know if this helps.

Related

WPF: UserControl not displaying at design time inside Datatemplate for a ContentControl

I spent quite some days trying to figure out what is not working here, and I suspect it is eighter a bug in VS or something plain simple I oversee...
I have a ContentControl which serves a view model:
<!-- Learning control from DataTemplate -->
<ContentControl Content = "{Binding learningViewModel}"
Grid.Row = "1"
Grid.Column = "1"
Height = "300"
Margin = "20, 0, 0, 0"/>
The property it is bound to is actually an interface to a set of view models.
Then I have a definition of DataTemplate for the possible ViewModels:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
<local:viewLearnSpeak CommandNotKnown = "{Binding DataContext.cmdNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandAlmostKnown = "{Binding DataContext.cmdAlmostKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandKnown = "{Binding DataContext.cmdKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnWrite}">
<local:viewLearnWrite CommandNotKnown = "{Binding DataContext.cmdNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandAlmostKnown = "{Binding DataContext.cmdAlmostKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandKnown = "{Binding DataContext.cmdKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnListen}">
<local:viewLearnListen CommandNotKnown = "{Binding DataContext.cmdNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandAlmostKnown = "{Binding DataContext.cmdAlmostKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandKnown = "{Binding DataContext.cmdKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
</DataTemplate>
</Window.Resources>
During programm execution the binding works as expected.
However during design time the ContentControl will only display a plain string representing the path of the view model (eg. "MyApp.ViewModels.vmLearnSpeak").
All view models have default constructors which initialize some dummy data and if I change that dummy initialization in the ViewModel that has the property the ContentControl is bound to, then the DataTemplate is also changed in designer as expected (the string changes to "MyApp.ViewModels.vmLearnWrite" for example).
If I replace the DataTemplate content by another control like a button, I get the button displayed if the correcponding Data for the DataTemplate with the button is set.
So in all, DataTemplate is basically working.
The custom controls work too: if I put the code from the DataTemplate directly in the layout, then the control appears as expected.
Hence it appears that the control will only fail to display during design time if placed inside a DataTemplate...
I hope I provided enough information, else let me know... Thanking you in advance for your support!
BTW: running Visual Studio 2016 communities, just in case there might be a known bug (After spending so much time trying to fix I am not sure but I believe that it used to work some time ago...)
EDIT:
The issue really is with the UserControl, if I add a simple empty or with only a button UserControl the same issue is there. Hoewver if i put a CustomControl or a regular Button, the DataTemplate is shown...
Just for information: apparently this was a bug in Visual studio that has been fixed, while I am not sure when it will release:
https://developercommunity.visualstudio.com/content/problem/1004742/usercontrol-is-not-displayed-inside-datatemplate-i.html

Bind the current instance of a control to an attached property

I'm looking to see if you can bind the current instance of a usercontrol or window to an attached property defined in its xaml, eg:
<Window MyAttachedProp.Value="{Binding Self}"/>
You want the MyAttachedProp.Value to have the Window object reference?
You can use any of these methods:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}
give your Window an x:Name="XXXXX"...and then use {Binding ElementName=XXXXX} to find it.
{Binding RelativeSource={RelativeSource Self}}
{Binding RelativeSource={x:Static RelativeSource.Self}}
With example 4, it avoids the creation of a new RelativeSource object (with the Mode set to Self)...instead it points to the Static one already created in the RelativeSource class....(this is a VERY minor and premature optimization).
Most people use example 3 as it's less to type and clearer to read.
http://www.c-sharpcorner.com/uploadfile/yougerthen/relativesources-in-wpf/
{Binding RelativeSource={RelativeSource Self}}

Exposing read-only dependency properties on a control who's values come from children controls in the ControlTemplate

I have a Control, FooControl. It needs to expose a read-only DependencyProperty called HasError. The value of this property is actually just the value from a control in FooControl's ControlTemplate.
The following code accomplishes exactly what I want, except it forces me to declare FooControl.HasError as a read-write DependencyProperty (the Binding cannot set the value otherwise.)
<ControlTemplate TargetType="FooControl">
<ChildControl HasError="{Binding HasError, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
I'm trying to get around having to do annoying stuff like using PART_'s to find the child control, attach to its HasError ValueChanged event, and copy the value. Because that's obnoxious, and I have quite a few properties like this.
My best guess is that the dependency property ChildControl.HasError defines the framework property metadata option BindsTwoWayByDefault. This means that the mode of {Binding HasError, RelativeSource={RelativeSource TemplatedParent}} is TwoWay, which does not work when the source property is read-only.
Therefore, change the binding to
{Binding HasError, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}
This overrides BindsTwoWayByDefault and the binding should work.

XAML: Convert a Brush to a Color?

I am creating a custom control with two text colors, ColorA and ColorB. ColorA is wired to the Foreground property of the control, and ColorB is wired to a custom dependency property called ForegroundAlt. Both properties are Brush objects. The control's XAML gets the property values using this markup:
<SolidColorBrush x:Key="BrushA" Color="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
<SolidColorBrush x:Key="BrushB" Color="{Binding Path=ForegroundAlt, RelativeSource={RelativeSource TemplatedParent}}" />
I need to animate sme text between the two colors in the control template, and that's where I am running into problems.
Normally, I would simply create a data binding to each Brush.Color property, like this:
To="{Binding Source={StaticResource BrushB}, Path=Color}"
But that won't work here. It turns out that you can't use bindings on an animation inside a control template.
As a workaround, I would like to create a pair of Color resources to go along with the Brush resources:
<Color x:Key="ColorA" ??? />
<Color x:Key="ColorB" ??? />
Each Color resource should have the color of its corresponding brush. I could then reference the colors as static resources, and avoid having to data bind from within the animation.
So, here are my questions:
-- How would I declare the Color resources?
-- Is there a simpler way to get the job done?
Thanks for your help.
If I've understood this correctly, what you are trying will not work. Even if you define the Colors as resources, you will still have to bind them to the brush resources and you are back to square one!
One solution is to do it in code behind rather than in the template. Since its a custom control you are building its should be pretty straightforward to add it in th code behind without screwing up the design.

How do I databind to the control's property rather than the datacontext?

I have a sub control embedded inside my main control, it allows the user to edit an address. Because this is reused all over the place (sometimes in multiple places on one control) I bind it like so
<Controls:EditAddressUserControl DataContext="{Binding Path=HomeAddress}"/>
<Controls:EditAddressUserControl DataContext="{Binding Path=WorkAddress}"/>
But the EditAddressUserControl needs access to the main control's list of CountrySummary objects so it can choose which country the address belongs to.
I have added a Countries DependencyProperty to EditAddressUserControl and added
Countries="{Binding Countries}"
So far all is going well, the EditAddressUserControl.Countries property has the correct countries in it. However, how do I databind my Combobox.ItemsSource to that in XAML?
I still want everything on my EditAddressUserControl to bind to its DataContext, but the ComboBoxCountries.ItemsSource needs to bind to "this.Countries".
How do I do that?
I've tried this
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:EditAddressUserControl}}, Path=Countries}" />
I saw no binding errors in the output box, but I also saw no items in the combobox.
You can accomplish this by using a RelativeSource for the binding source, instead of the DataContext.
This would most likely look something like:
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:EditAddressUserControl}}, Path=Countries}" />
The way to do it was to stop using DataContext completely. Instead I added a DependencyProperty to my control
public static DependencyProperty AddressProperty = DependencyProperty.Register("Address", typeof(EditPostalAddressDto), typeof(EditPostalAddressControl));
Then in the parent control instead of setting DataContext="..." I set Address="..." - The XAML for the control is then changed to include an ElementName on the binding
<UserControl ..... x:Name="MainControl">
<TextBox Text="{Binding ElementName=MainControl,Path=Address.Region}"/>
Now I can specifically bind to the Address property, but also bind to properties on the main data context.

Resources