bind CRL wrapper or bind direct dependency property on XAML element - c#

I have simple depedency property in window.
public static readonly DependencyProperty UserLastNameProperty =
DependencyProperty.Register("UserLastName",
typeof (string),
typeof (MainWindow),
new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string UserLastName
{
get
{
return (string) GetValue(UserLastNameProperty);
}
set
{
SetValue(UserLastNameProperty, value);
}
}
When I bind direct depedency property on textBox binding doesn’t work.
<TextBox Margin="4" FontSize="14" x:Name="TxbLastName" MinWidth="200"
Text="{Binding UserLastNameProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
But when I bind CLR prop wrapper on textBox binding works.
<TextBox Margin="4" FontSize="14" x:Name="TxbLastName" MinWidth="200"
Text="{Binding UserLastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Why I can’t bind direct depedency property on textBox?

You are confusing static DependencyPropertyIdentifier with instance CLR wrapper for that property.
DependencyPropertyIdentifier is static field which is register at class level and embed in class metadata. Whereas to fetch and set the value for an instance, GetValue() and SetValue() is called on that DP identifier.
From MSDN -
Dependency property identifier: A DependencyProperty instance, which is obtained as a return value when registering a dependency
property, and then stored as a static member of a class. This
identifier is used as a parameter for many of the APIs that interact
with the WPF property system.
CLR "wrapper": The actual get and set implementations for the property. These implementations incorporate the dependency property
identifier by using it in the GetValue and SetValue calls, thus
providing the backing for the property using the WPF property system.
Dependency properties on a given type are accessible as a storage table through the property system. Instance value is stored in that storage table and WPF implementation of XAML processor uses that table to get and set value for an instance object.
I would suggest you to read more about it here and here.

Related

Dependency Property Datacontext

I have a usercontrol, and there is a Datacontext set for it. This usercontrol contains also a Dependency-Property. Now, i want simply bind to this property.
I think the problem has something to do with the wrong datacontext.
The dependency-Property in my usercontrol (called TimePicker) looks like this:
public TimeSpan Time
{
get { return (TimeSpan)GetValue(TimeProperty); }
set
{
SetValue(TimeProperty, value);
OnPropertyChanged();
}
}
public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof (TimeSpan), typeof (TimePicker));
I try to use it like this:
<upDownControlDevelopement:TimePicker Grid.Row="1" Time="{Binding Path=TimeValue}" />
When i do this i get the following binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'TimeValue' property not found on 'object' ''TimePicker' (Name='TimePickerControl')'. BindingExpression:Path=TimeValue; DataItem='TimePicker' (Name='TimePickerControl'); target element is 'TimePicker' (Name='TimePickerControl'); target property is 'Time' (type 'TimeSpan')
Any help would be highly appreciated
Greetings Michael
PS: you can download the code at here
Although this has now been solved there seems to be some, in my opinion, inappropriate use of the DataContext.
When developing a custom reusable control, you should not set DataContext at all. What the DataContext will be, that is for the user of the control to decide, not for the developer. Consider the following common pattern of code:
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
</Grid>
Notice that here, you are using the Grid control. The developer of the control (in this case, the WPF team), didn't touch the DataContext at all - that is up to you. What does it mean for you as a control developer? Your DependencyProperty definition is fine, but you shouldn't touch the DataContext. How will you then bind something inside your control to the DependencyProperty value? A good way is using a template (namespaces omitted):
<MyTimePicker>
<MyTimePicker.Template>
<ControlTemplate TargetType="MyTimePicker">
<!-- Stuff in your control -->
<TextBlock Text="{TemplateBinding Time}" />
<TextBox Text="{Binding Time, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
<MyTimePicker.Template>
</MyTimePicker>
Note that TemplateBinding is always one-way only, so if you need any editing at all, you need to use normal binding (as you can see on the TextBox in the example).
This only means that the TextBlock/Box inside your control will get its Time value from your custom control itself, ignoring any DataContext you might have set.
Then, when you use the control, you do it like this (added to my first example):
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
<MyTimePicker Time="{Binding TimeValue}" />
</Grid>
What just happened here is that the MyTimePicker does not have DataContext set anywhere at all - it gets it from the parent control (the Grid). So the value goes like this: Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text.
And above all, avoid doing this in the constructor of your custom control:
public MyTimePicker()
{
InitializeComponent();
DataContext = this;
}
This will override any DataContext set in XAML, which will make binding a huge pain (because you'll have to always set Source manually). The previous example would not work, and this wouldn't work either:
<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />
You would think this is OK, but the DataContext will be resolved in the InitializeComponent() call, so the value will be immediately overwritten. So the binding to TimeValue will look for it in the control instead (which will, of course, fail).
Just don't touch the DataContext when developing a control and you'll be fine.
You don't need to override the data context of user control. You can use RelativeSource to point your binding source property i.e. TimeValue to any other source you like. E.g. If you have the source property in your window's class. You could simply point your binding target to the source in window's data context as follows:
{Binding Path=DataContext.TimeValue, RelativeSource={ RelativeSource AncestorType={x:Type Window}}}
Your error states that 'TimeValue' property not found on 'object' 'TimePicker', which means that the WPF Framework is looking at the 'TimePicker' object to resolve the 'TimeValue' property value. You must have somehow set the DataContext of the Window or UserControl that contains the 'TimePicker' object to an instance of the 'TimePicker' object.
Instead, it should be set to an instance of the class that declares the 'TimeValue' property. If you're using a view model, then you should set it to an instance of that:
DataContext = new YourViewModel();
If the 'TimeValue' property is declared in the Window or UserControl then you can set the DataContext to itself (although generally not recommended):
DataContext = this;
Please note that when data binding to the 'Time' property from inside your TimePicker control, you should use a RelativeSource Binding:
<TextBlock Text="{Binding Time, RelativeSource={RelativeSource
AncestorType={x:Type YourLocalPrefix:TimePicker}}}" ... />
Normally we are not setting datacontext directly.If u want to set datacontext create an instance of your usercontrol and set datacontext individually to each one.

WPF Binding not updating with UpdateSourceTrigger=PropertyChanged

I have a TabControl in which I set the DataContext to an instance of the this class, It's basicly a wrapper for DependencyProperties of a static class with the same properties.
In my Markup I set the DataContext like this
<TabControl DataContext="{Binding ElementName=self, Path=Settings}">
and binding to the property within the TabControl like this
<TextBox Text="{Binding Path=Url, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
However, this does not lead to any updates of the source when the content of the TextBox is changed. I can change the content of the TextBox, let it loose focus etc. it does just not update the source.
Url is a dependency property and when set from XAML, wrapper property setter won't be called.
From MSDN:
The current WPF implementation of its XAML processor is inherently
dependency property aware. The WPF XAML processor uses property system
methods for dependency properties when loading binary XAML and
processing attributes that are dependency properties. This effectively
bypasses the property wrappers. When you implement custom dependency
properties, you must account for this behavior and should avoid
placing any other code in your property wrapper other than the
property system methods GetValue and SetValue.
In case you want to do something on its property changed you should provide PropertyChangedCallback and write code there.
You can refer to the sample here in case PropertyChangedCallback is new to you. Something like:
public static readonly DependencyProperty UrlProperty =
DependencyProperty.Register(
"Url",
typeof(string),
typeof(SettingsWrapper),
new PropertyMetadata(OnUrlChanged)
)
);
private static void OnUrlChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
SettingsWrapper instance = (SettingsWrapper)d;
instance.Settings.Url = e.NewValue.ToString();
}
You said in a (now deleted) comment that your Window has x:Name="self", however the Window class does not have a property called Settings.
If this is an attached property, you need to reference it by the attached property by the full name, and wrap it in parenthesis.
For example,
<TabControl DataContext="{Binding ElementName=self, Path=(local:MyClass.Settings)}">
See WPF Attached Property Data Binding for more info.

How do I use an explicitly implemented interface property and wpf visiblity together properly?

I have the following situation:
I have a few ViewModel objects, some of which implement an interface ISomeInterface, some don't. The interfaces exposes a property called SomeEnumeration (IEnumerable<T>).
For example:
public sealed class ViewModelA : ViewModelBase, ISomeInterface
{
// ...
IEnumerable<Foo> ISomeInterface.SomeEnumeration
{
get { ...; }
}
}
public sealed class ViewModelB : ViewModelBase
{
// ...
}
My XAML, so far, has been designed in a way that both of the ViewModels happen to have the properties I am binding against (i.e. PropertyA, PropertyB, etc.). I haven't ran into the situation yet where a property I am binding against does not exist on the ViewModels that I am setting as the DataContext. But, now I will... and it will be against a property that is explicitly implemented (I'm not sure if that makes any difference in the WPF Binding Engine).
Basically, my xaml will look like the following:
<StackPanel
Visiblity="{Binding Path=SomeEnumeration, Converter={StaticResource AnyConverter}">
...
</StackPanel>
I'm not sure if this will even work because:
Not every DataContext will contain the property (in case it doesn't, it should be hidden) ... What should I do in this case?
For the DataContexts that do contain the property, it is explicitly implemented ... do you have to cast first or something?
Generally, when you want to use the WPF DataBinding Engine, you'll want to also utilize the FallbackValue and the TargetNullValue binding properties. What do these exactly do?
FallbackValue: Gets or sets the value when the binding is unable to
return a value.
TargetNullValue: Gets or sets the value that is used
in the target when the value of the source is null.
Jon explains the binding engine pretty well in this answer:
Binding.DoNothing is an object instance that you actively return from
a value converter; it instructs the binding engine to not update the
value of the target property at all. Here's a nice example by Josh
Smith of what you might use this for.
FallbackValue is a property that you set on bindings; it allows you to
specify the value to be applied to the target property if:
the binding source cannot be resolved (e.g. wrong binding path), or
the binding property value is equal to DependencyProperty.UnsetValue, or
a value converter used for the binding throws an exception, or
a value converter used for the binding returns DependencyProperty.UnsetValue, or
the value produced by the binding pipeline is not valid for the target property (e.g. wrong type)
TargetNullValue is also a property you set on bindings; it allows you
to specify the value to be applied to the target property if the value
of the source property is null. For example, if you bind a text box to
a string property TargetNullValue lets you pick what appears in the
text box if the source string is null.
As far as binding to "explicitly implemented interface", the real question should be how do you set the path to an interface property, because how that interface is implemented does not matter. This is actually quite easy to do in XAML, and here is an example:
<TextBox Text="{Binding Path=(local:ISomeInterface.SomeProperty)}" />
So, to answer your questions directly:
Utilize FallbackValue (and optionally TargetNullValue if necessary). For example, pass in null when the binding value can not be resolved due to a binding error.
Utilize the correct pattern for binding the Path property to an interface's property (see above example).
XAML usage:
<StackPanel Visiblity="{Binding Path=(local:ISomeInterface.SomeEnumeration),
Converter={StaticResource AnyConverter},
FallbackValue={x:Null}}">
...
</StackPanel>
One final note: If the binding fails early, the null FallbackValue would not be the value passed into the converter, it would be the final value used whether the binding fails at the property level or the converter level or etc. So do not expect that the converter will still run while passing in null into it.
A quick and good fix for you situation would be to place all your logic in the converter which is already in place .
xaml : (your binding)
<StackPanel
Visiblity="{Binding Path=., Converter={StaticResource AnyConverter}">
...
</StackPanel>
cs : (your Converter)
Convert()
{
return value Is ISomeInterface ?
(((ISomeInterface)value).SomeEnumeration == SomeEnumeration.SomeValue ?
Visibility.Visible : Visibility.Collapsed) : Visibility.Collapsed;
}

How to bind to property of object returned by converter

I'm trying to bind to a value, run a converter over it, and then display a property of that value. Having the Converter directly return the property I want wouldn't work, as I need property changes to be tracked.
What I'm trying to achieve would be something like this:
// NOTE: FOLLOWING IS NOT SUPPORTED BY WPF
// A 'Binding' cannot be set on the 'Source' property of type 'Binding'.
// A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
Text={Binding TextField Source={Binding SomeValue, Converter={StaticResource GetObjectFromValueConverter}}}`
Ideally, this would all be wrapped up in a simple markup extension.
Text={l:GetTextField SomeValue}
Problem is, I haven't been able to find any way to do this other than bind the Tag of the element to the converter, and then bind the target field to the property as follows:
Tag={Binding SomeValue, Converter={StaticResource GetObjectFromValueConverter}}
Text={Binding Tag.TextField, RelativeSource={RelativeSource Self}}
This is obviously cumbersome, limited (you only get one Tag field) and feels abusive. How else can I go about achieving what I want whilst monitoring for changes of TextField though?
You can bind the DataContext of the TextBox, instead of the Tag. That will make your other bindings much simpler:
DataContext="{Binding SomeValue, Converter={StaticResource GetObjectFromValueConverter}}"
Text="{Binding TextField}"
This assumes that you do not have any other bindings on the TextBox that require the inherited DataContext. For example, in the bindings below Text2 would be broken:
DataContext="{Binding SomeValue, Converter={StaticResource GetObjectFromValueConverter}}"
Text2="{Binding SomeOtherValue, Converter={StaticResource GetObjectFromValueConverter}}"
Text="{Binding TextField}"
Also, if you have a more complex control other than TextBox, the DataContext for any controls below it in the logical/visual tree will also be affected.

For a user control, how to set the binding of a item template item to a user property?

I have a simple user control, which is essentially just an AutoCompleteBox with some custom logic.
For a specific instance (a collection of Persons), I want it to look like this:
<sdk:AutoCompleteBox Name="myACB" ItemsSource="{Binding People}" FilterMode="StartsWith" MinimumPrefixLength="2" ValueMemberBinding={Binding LastName}>
<sdk:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}" />
</DataTemplate>
</sdk:AutoCompleteBox.ItemTemplate>
</sdk:AutoCompleteBox>
However, I want to make the data source generic and therefore the display values will be different (ValueMemberBinding and the template TextBlock text). That is why I am making a custom control so I can specify the differences with properties.
I have no problem setting the source with a user control property, but I am having difficulty with the display binding properties. Right now I have:
public static DependencyProperty DisplayMemberProperty = DependencyProperty.Register("DisplayMember", typeof(string), typeof(myAutoComplete), null);
public string DisplayMember
{
get
{ return myACB.ValueMemberPath; }
set
{
myACB.ValueMemberPath = value; // this works fine
// but how can set the text binding for the templated textblock?
}
}
I want the DisplayMember property to be the property name to display for whatever kind of custom collection (persons, cars, etc) I have bound to the AutoCompleteBox.
I don't think I can modify the datatemplate programmatically. Is there a way I can do this with binding (relative source)?
I am not sure if this works, but I think you could bind the text directly to the ValueMemberBinding property and use a converter to get the text out of it...
<TextBlock Text="{TemplateBinding DisplayMember}" />
Thank you for the suggestions.
I was unable to get a solution that I preferred, but my workaround is to just pass in a datatemplate resource as a property and that gets assigned to the autocompletebox itemtemplate.
Define a template:
<DataTemplate x:Key="myCustomDT">
<!-- whatever you want here -->
</DataTemplate>
Create the user control property for it:
public static DependencyProperty DisplayTemplateProperty = DependencyProperty.Register("DisplayTemplate", typeof(DataTemplate), typeof(myAutoComplete), null);
public DataTemplate DisplayTemplate {
get { return myACB.ItemTemplate; }
set { myACB.ItemTemplate = value; }
}
Now:
<local:myAutoComplete DisplayTemplate="{StaticResource myCustomDT}" />
Not the best method, but it will work for now.

Categories