WPF Dynamic resource reference - wpf

HI
Am load a string xaml with DynamicResource assigned to a Background property. Is there a way to get the reference of the dynamic resource.
Background="{DynamicResource Color1}"
I want to get the resource reference assigned to a Dependency property at runtime
Pl help

Use FrameworkElement.FindResource Method
this.FindResource("Color1");

Where is the DependencyProperty defined? On the same Window/UserControl? If you simply want to bind to the value of a DependencyProperty you probably want to use regular {Binding ...} syntax instead.
Example 1: If you are binding to a dependency property on a particular control named myControl you can declare it like below.
Background="{Binding ElementName=myControl, Path=Color1}"
Example 2: If you don't want to rely on naming controls because it is so pasay in WPF and you are referencing a property defined on your Window you could do something like below.
Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=Color1}"

Related

WPF define ConverterCulture/Language in resources

I would like default culture/language to be defined as resource in order to provide consistent displaying of selected labels. It seems, however it is not possible to define neither Language(XMLLanguage) nor ConverterCulture(CultureInfor) resource, it does not seem to be possible to use a string resource neither:
<TextBlock Text="{Binding Source={x:Static sys:DateTime.Now},
Mode=OneWay, StringFormat={StaticResource DateFormat},
ConverterCulture={StaticResource DefaultCulture},
Language={StaticResource DefaultLang}/>
//....
<Grid.Resources>
<sys:String x:Key="DefaultCutureString">en-GB</sys:String>
<win:XmlLanguage xmlns:win="clr-namespace:System.Windows.Markup;assembly=PresentationFramework" x:Key="DefaultLang">en-GB</win:XmlLanguage>
<g:CultureInfo xmlns:g="clr-namespace:System.Globalization;assembly=mscorlib" x:Key="DefaultCuture">
<x:Arguments>
<sys:String>en-GB</sys:String>
</x:Arguments>
</g:CultureInfo>
</Grid.Resources>
How can I define and apply Culture/Language using Resource?
XmlLanguage and CultureInfo have no default parameterless constructors which means you cannot instantiate them in XAML.
But you could create the resources programmatically and then add them to Grid.Resources (just give the Grid and x:Name of "grid" or similar in the XAML markup to be able to identify it in the code):
grid.Resources["DefaultLang"] = XmlLanguage.GetLanguage("en-GB");
grid.Resources["DefaultCuture"] = new System.Globalization.CultureInfo("en-GB");

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.

In WPF, why doesn't TemplateBinding work where Binding does?

Ok... this is leaving me scratching my head. I have two WPF controls--one's a user control and the other's a custom control. Let's call them UserFoo and CustomFoo. In the control template for CustomFoo, I use an instance of UserFoo which is a named part so I can get to it after the template is applied. That works fine.
Now both UserFoo and CustomFoo have a Text property defined on them (independently, i.e. not a shared DP using AddOwner. Don't ask...) that are both declared like this...
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(UserFoo), // The other is CustomFoo
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
null,
null,
true,
UpdateSourceTrigger.PropertyChanged
)
);
Notice specifically that the mode is set to TwoWay and the UpdateSourceTrigger is set to PropertyChanged, again for both.
So in the style template for CustomFoo, I want to bind CustomFoo's Text property as the source to the internal UserFoo's Text property. Normally, this is easy. You just set UserFoo's text property to "{TemplateBinding Text}" but for some reason it's only going one way (i.e. UserFoo is properly set from CustomFoo, but not the reverse), even though again, both DPs are set for two-way! However, when using a relative source binding instead of a template binding, it works great! Um... wha??
// This one works
Text="{Binding Text, RelativeSource={RelativeSource AncestorType={local:CustomFoo}}, Mode=TwoWay}"
// As does this too...
Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
// But not this one!
Text="{TemplateBinding Text}"
So what gives? What am I missing?
Found this forum post on MSDN: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0bb3858c-30d6-4c3d-93bd-35ad0bb36bb4/
It says this:
A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with
{Binding RelativeSource={RelativeSource TemplatedParent}}
Note from OP: Contrary to what it says in the documentation, in actuality, it should be this...
{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}
I filed a complaint against the docs, and while they did add a sentence now stating they are always one-way, the code example still doesn't list the mode, but I guess it's better than nothing.)
The TemplateBinding transfers data from the templated parent to the property that is template bound. If you need to transfer data in the opposite direction or both ways, create a Binding with RelativeSource of TemplatedParent with the Mode property set to OneWayToSource or TwoWay.
More in: http://msdn.microsoft.com/en-us/library/ms742882.aspx
Looks like Mode=OneWay is one of the "Optimizations" of using a TemplateBinding
TemplateBinding does not support two-way binding, only Binding does that. Even with your BindsTwoWayBeDefault option, it won't support two-way binding.
More info can be found here, but to summarize:
However, a TemplateBinding can only
transfer data in one direction: from
the templated parent to the element
with the TemplateBinding. If you need
to transfer data in the opposite
direction or both ways, a Binding with
RelativeSource of TemplatedParent is
your only option. For example,
interaction with a TextBox or Slider
within a template will only change a
property on the templated parent if
you use a two-way Binding.

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.

WPF won't let me put a binding on the path of a binding -- is there another way?

I have a DataTemplate that I'm using as the CellTemplate for a GridViewColumn.
I want to write something like this for the DataTemplate:
<DataTemplate
x:Key="_myTemplate">
<TextBlock
Text="{Binding Path={Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumn}}, Path=Header}}" />
</DataTemplate>
My GridView is bound to a DataTable, and I want to bind to the column of the DataTable whose name is equal to the Header of the GridViewColumn the DataTemplate is attached to. [I hope that made sense!]
Unfortunately, this doesn't work. I get a XamlParseException: "A 'Binding' cannot be set on the 'Path' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependenceyObject."
How can I set this up?
Edit (elevating comment by DanM to the question)
I basically need a DataTemplate whose binding is determined by the DataContext and which column the DataTemplate is attached to. Is there an alternative?
You cannot assign a Binding to just any property. The property must either of the type Binding or be implemented as a Dependency Property on the object.
The Path property of the Binding class is of type PropertyPath and Binding does not implement the Path property as a dependency property. Hence you cannot dynamically bind the Path in the way you are attempting to.
Edit
You basically want to embed metadata in your bound data which drives the configuration of the DataTemplate. This can't be done in XAML alone. You would need at least some support from code.
It would seem to me that the best approach would be to use a ViewModel. That makes the binding in the XAML straight-forward and pushes this "what to bind with what" decision down into the code of the ViewModel.
Don't you just want this?
{Binding Path=Header, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumn}}}

Resources