DependencyProperty refreshing only one time - wpf

I have a custom control with DependencyProperty
public Point EndPoint
{
get { return (Point)GetValue(EndPointProperty); }
set { SetValue(EndPointProperty, value); }
}
public static readonly DependencyProperty EndPointProperty =
DependencyProperty.Register("EndPoint", typeof(Point), typeof(Speedometer), new FrameworkPropertyMetadata(new Point(100,100),FrameworkPropertyMetadataOptions.AffectsRender));
and somewhere in DefiningGeometry logic I'm calling
EndPoint = new Point(xEnd, yEnd);
In xaml, where I use this, I have another control and I'm trying to bind Canvas.Left to EndPoint.X of my custom control
<Rectangle Fill="White" Height="50" Width="50" x:Name="Grip" Canvas.Left="{Binding ElementName=control, Path=EndPoint.X}" Canvas.Top="{Binding ElementName=control, Path=EndPoint.Y}">
Update seems to be fired only once, because result depends of DependencyProperty default value.
How to make it updating all time when value of property is changed?

Ok, I got it.
First, I implemented INotifyPropertyChanged in my custom control, then after set Property, I'm calling OnPropertyChanged(nameof(EndPoint)); I thought DependencyProperty can notify property changes two way.

Related

WPF/XAML: Shortcut to a user control

I have created a UserControl named ContactPerson. It contains a persons name, phone number etc.
This usercontrol does not have a headline (ie. a label such as "_Contact Person") because I use this usercontrol in different situations.
However in one situation, I do have such a label, which means my code look somewhat like this:
<Label Content="_Contact Person"
Target="{Binding ElementName=_contactView}" />
<View1:contactView x:Name="_contactView"
DataContext="{Binding SupplierContact}"/>
I want - to set keyboard focus to the name-textbox inside the ContactPersonUserControl but it seems to be a difficult task (it is private after alle).
I do not want to move the label inside the usercontrol, which I guess would in fact be the most simple solution. It seems to me that XAML should provide a solution to this scenario.
How to do this in a simple and elegant way?
Thx
(I have a few of these controls, so I'll need to use the same solution several times).
I am not sure whether i understand your scenario correctly, But if you want to set focus to ui control from view model, then you may need to create an attached property.
Attached Property:
public static class ControlFocusExtension
{
public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(ControlFocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
public static bool GetIsFocused(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
private static void OnIsFocusedPropertyChanged(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)pDependencyObject;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
}
User control XAML:
Now in your View (in XAML) you can bind this property to your ViewModel:
<TextBox local:FocusExtension.IsFocused="{Binding IsNameFocused}" />
You can change the value of IsNameFocused from your view model if needed.
Alternatively if you don't want to bind this then you can use it as -
<TextBox local:FocusExtension.IsFocused="True" />
Set focusvisualstyle property null for those you dont want to set focus like this
example <Label FocusVisualStyle="{x:Null}">
and for textbox focus <TextBox Focusable="True"/>

Is it possible to get what value a DependencyProperty would be at each of the precedence levels, not just the final value?

We are trying to get what the value of a DependencyProperty would be if it weren't locally set in the XAML. For instance, take this XAML...
<StackPanel Orientation="Vertical"
TextElement.FontSize="24">
<TextBlock Text="What size am I?"
FontSize="{Binding FontSize, RelativeSource={RelativeSource Self}, Converter={StaticResource PercentageConverter}, ConverterParameter=0.75}" />
</StackPanel>
This of course doesn't work. What I've done instead is this...
<TextBlock Text="What size am I?"
FontSize="{Binding (TextElement.FontSize), RelativeSource={RelativeSource AncestorType=FrameworkElement}, Converter={StaticResource PercentageConverter}, ConverterParameter=0.5}" />
...which checks the parent's value for the inherited FontSize property but that only works because FontSize does support inheritance. I'm looking for a solution for any DependencyProperty, not just ones that can be inherited.
Update
I changed the title of the question to be a little more generic as to what would be helpful to us. Specifically we'd love to be able to say 'For this specific instance of a DependencyObject, what would the value of a DependencyProperty be for each of the precedence levels?' When we query a DP, we of course get what the value after all precedence is applied. We'd love to say 'What would it be one or two levels higher?', etc.
A generic and robust solution should take into account the full rule set for DependencyProperty value precedence. I don't think that it is safe to say that if a value is not set locally, then its value would be the default value - the value instead could then be provided by a style, a trigger, etc.
I would suggest taking a look at the class DependencyPropertyHelper. This class contains a method called GetValueSource which when given a DependencyObject and a DependencyProperty will return a BaseValueSource result that tells you where the value for the property is coming from (style, style trigger, inherited, local, etc).
That being said, I think it would not be too hard to write an attached behaviour which you could use in XAML to return the "non-local" value for a specified DependencyProperty. This behaviour would check if the value source is local, and if it is clone the element, add it to the logical tree, and clear the local value. It would then read the value from the specified DependencyProperty on the clone, and set a read-only attached DependencyProperty to this value. This way you are letting the WPF property engine do the work for you.
Hope this helps!
Edit:
Here is a proof of concept for the attached behaviour. The behaviour has 2 properties:
PropertyToInspectValue - The DependencyProperty who's value you
wish to inspect.
InspectedValue - The computed non-local value for the specified DependencyProperty.
In XAML you bind to these 2 properties (setting one, reading the other):
<StackPanel>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="30" />
</Style>
<Style x:Key="NamedStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="35" />
</Style>
</StackPanel.Resources>
<!-- Without local value - uses default style (30)-->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
<!-- Without local value - uses named style (35)-->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Style="{StaticResource NamedStyle}"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
</StackPanel>
<StackPanel TextElement.FontSize="25">
<!-- Without local value - uses inherited value (25) -->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
</StackPanel>
<!-- Without local value - uses default font size (11) -->
<TextBlock l:DependencyPropertyValueHelper.PropertyToInspectValue="TextElement.FontSize"
Text="{Binding Path=(l:DependencyPropertyValueHelper.InspectedValue), RelativeSource={RelativeSource Self}}"
FontSize="15" />
</StackPanel>
The example above will correctly show the values: 30, 35, 25, and 11. A couple of limitations in the code below is that the DependencyObject which we are inspecting must be a child of a Panel, and that currently only the Style property is copied over, but really all properties should be cloned since triggers in the style could use any property to change the value a DependencyProperty. Food for thought I guess ...
Attached Behaviour:
public static class DependencyPropertyValueHelper
{
private static bool _isUpdating;
#region InspectedValue Read-only Attached Dependency Property
public static object GetInspectedValue(DependencyObject d)
{
return d.GetValue(InspectedValueProperty);
}
private static readonly DependencyPropertyKey InspectedValuePropertyKey =
DependencyProperty.RegisterAttachedReadOnly("InspectedValue", typeof (object),
typeof (DependencyPropertyValueHelper),
new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty InspectedValueProperty = InspectedValuePropertyKey.DependencyProperty;
#endregion
#region PropertyToInspect Attached Dependency Property
public static void SetPropertyToInspectValue(DependencyObject d, DependencyProperty dependencyProperty)
{
d.SetValue(PropertyToInspectValueProperty, dependencyProperty);
}
public static DependencyProperty GetPropertyToInspectValue(DependencyObject d)
{
return (DependencyProperty)d.GetValue(PropertyToInspectValueProperty);
}
public static readonly DependencyProperty PropertyToInspectValueProperty =
DependencyProperty.RegisterAttached("PropertyToInspectValue", typeof (DependencyProperty),
typeof (DependencyPropertyValueHelper),
new FrameworkPropertyMetadata(OnPropertyToInspectValuePropertyChanged));
#endregion
#region Private ValueChanged Attached Dependency Property
public static readonly DependencyProperty ValueChangedProperty =
DependencyProperty.RegisterAttached("ValueChanged", typeof(object),
typeof(DependencyPropertyValueHelper),
new FrameworkPropertyMetadata(OnValuePropertyChanged));
#endregion
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!_isUpdating)
DetermineNonLocalValue(d, GetPropertyToInspectValue(d));
}
private static void OnPropertyToInspectValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dependencyProperty = (DependencyProperty) e.NewValue;
DetermineNonLocalValue(d, dependencyProperty);
var binding = new Binding
{
RelativeSource = new RelativeSource(RelativeSourceMode.Self),
Path = new PropertyPath(dependencyProperty.Name),
};
BindingOperations.SetBinding(d, ValueChangedProperty, binding);
}
private static void DetermineNonLocalValue(DependencyObject d, DependencyProperty dependencyProperty)
{
var element = d as FrameworkElement;
if (element == null)
return;
var valueSource = DependencyPropertyHelper.GetValueSource(element, dependencyProperty);
if (valueSource.BaseValueSource == BaseValueSource.Local)
{
_isUpdating = true;
var clonedDependencyObject = Activator.CreateInstance(element.GetType()) as FrameworkElement;
var parent = VisualTreeHelper.GetParent(element) as Panel;
// Currently only works if parent is a panel
if (parent != null)
{
// Copy any property which could impact the DP's value ...
// Probably check if this is a control and copy the ControlTemplate too ...
if (element.Style != null)
clonedDependencyObject.Style = element.Style;
parent.Children.Add(clonedDependencyObject);
// Let WPF provide us with the non-local value
element.SetValue(InspectedValuePropertyKey, clonedDependencyObject.GetValue(dependencyProperty));
parent.Children.Remove(clonedDependencyObject);
}
_isUpdating = false;
}
else
{
element.SetValue(InspectedValuePropertyKey, d.GetValue(dependencyProperty));
}
}
}
Edit 2
To elaborate on this approach. There is no chain of values exposed by WPF which we can inspect (or at least that I know of). The code above attempts to discover what the value would be if it was not set locally (from whatever value source: inherited, styles, triggers, etc). When it discovers this value it then populates the InspectedValue property with the result, which then can be read from.
A first attempt at this would be to:
Clear the local value for the DP, store it away somewhere (e.g. using DependencyObject.ClearValue)
Query the DP for its new value which we will assume is what the value would have been if it was not set locally. Record this value.
Restore the original value from step 1.
This approach fails - in that during step 2 your property will not pick up values from default styles (for example) since the style has already been applied, and simply removing a local value from a random DependencyPropery does not trigger a re-application.
Another approach would be in step 2 above - remove the element from the logical tree, and then add it back (since when elements are added to the tree, WPF goes through all possible value sources to determine the value of each DP). This too fails for the same reason - you are not guaranteed that styles will be re-applied.
The attached behaviour above tries a third approach - clone an element but make sure the the property in question is not set on the clone, and then add it to the logical tree. At this point WPF will then process the element as it would have with the original element and apply a value to the DP (inherited, triggers, style, etc). We can then read its value, and store it away. As demonstrated, this approach works for common use-cases, but is not perfect since if the non-local value was coming from a Trigger using a read-only property like IsMouseOver it would not pick it up (and a deep copy of the original object state would not fix this).
you should use the overloaded method of GetPropertyMetaData that suit best your need (that should be the one using types), and then you can retrieve the default value with... the DefaultValue property.

WPF custom control databinding

I'm new to the development of custom controls in WPF, but I tried to develop a single one to use in a application that I'm developing. This control is an autocomplete textbox. In this control, I have a DependencyProprety that has a list of possible entries so a person can choose from while entering the text
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof (IList<object>),typeof (AutoCompleteTextBox),new PropertyMetadata(null));
public IList<object> ItemsSource
{
get { return (IList<object>) GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
RaiseOnPropertyChanged("ItemsSource");
}
}
I use this control in a usercontrol and associate this control to a property in the viewmodel
<CustomControls:AutoCompleteTextBox Height="23" Width="200"
VerticalAlignment="Center" Text="{Binding Path=ArticleName, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Path=Articles,
Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
</CustomControls:AutoCompleteTextBox>
I have a viewmodel that I assign on the usercontrol load to the datacontext of the usercontrol load
protected virtual void Window_Loaded(object sender, RoutedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
this.DataContext = viewModel;
SetLabels();
}
}
This viewmodel has the property Articles with values but the ItemsSource property of the control is null when I try to search in the list after the user enter some text.
Is there any special step that I missed when I create the control so use the mvvm pattern.
I hope that the explain the problem in a understandable way. Any help/hints would be welcome.
There are two issues here:
First, you're dependency property is defining the "default" value for this property to be null. You can change that by changing the metadata to specify a new collection:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof (IList<object>),typeof (AutoCompleteTextBox),
new PropertyMetadata(new List<object>));
Secondly, when using dependency properties, the setter can't contain any logic. You should keep your property set as:
public IList<object> ItemsSource
{
get { return (IList<object>) GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
This is because the setter doesn't actually get called by the binding system - only when you use code. However, since the class is a DependencyObject and this is a DP, you don't need to raise property changed events.

WPF - Dependency Properties Error

I'm working on a WPF project, and my intention is to make two specific RadioButtons alter properties of another specified Component. But for now, i'm just trying to store a String inside the RadioButton.
For that, I've created a behavior class:
public class AdjustBehavior : Behavior<RadioButton>
{
With this property:
public static DependencyProperty AdjustLabelContentProperty =
DependencyProperty.RegisterAttached("LabelContent", typeof(String), typeof(AdjustBehavior),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits));
And these getters and setters:
public static String GetLabelContent(RadioButton tb)
{
return (String)tb.GetValue(AdjustLabelContentProperty);
}
public static void SetLabelContent(RadioButton tb, String value)
{
tb.SetValue(AdjustLabelContentProperty, value);
}
On the XAML side, I did this:
<RadioButton Content="Banana" Height="16" HorizontalAlignment="Left" Margin="30,216,0,0" Name="radioButton1" VerticalAlignment="Top" GroupName="a" IsThreeState="False" IsChecked="True" Checked="radioButton1_Checked" >
<int:Interaction.Behaviors>
<i:AdjustBehavior LabelContent="Apple" />
</int:Interaction.Behaviors>
</RadioButton>
Where int: is the namespace to Interaction.Behaviors and i: is the namespace to the AdjustBehavior class. But whenever I start my application, LabelContent is set to null. Why?
I didn't post the rest of my Behavior class because I think it won't matter, but I'll do if necessary.
Thanks in Advance.
Clark
You should use DependencyProperty.Register, not RegisterAttached. This isn't being used as an attached property, but rather a standard dependency property.
Attached property requires target to be attached to. In your case that target is radio button,
so you should use
<RadioButton i:AdjustBehavior.LabelContent="Apple" ... />
If you need to just create property of AdjustBehavior, use normal dependency property, not attached.
LabelContent should be either an attached property on RadioButton or dependency property on AdjustBehavior .

Setting XAML property value to user control

I have a user control in WPF which i want the text of one of it's labels to be read from the XAML where it is used. Hence..
My User Control:
<UserControl x:Class="muc">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold">
<Label.Content>
<Binding ElementName="TestName" Path="." />
</Label.Content>
</Label>
</UserControl>
Then using it:
<mycontorls:muc TestName="This is a test" />
But it doesn't works ...
How can i read the properties ?
I tried the first two answers and what I got worked in code but not on XAML (also doesn't let you see changes in the design view when using the control).
To get a property working like any other native one, here is the full process:
(The sample adds a dependency property of type Nullable to show in the control as text or a default if null)
In the code file:
1.a Define a dependency property:
public static readonly DependencyProperty MyNumberProperty = DependencyProperty.Register("MyNumber", typeof(Nullable<int>), typeof(MyUserControl), new PropertyMetadata(null, new PropertyChangedCallback(OnMyNumberChanged)));
1.b Implement the OnMyNumberChanged Callback:
private static void OnMyNumberChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args){
// When the color changes, set the icon color PlayButton
MyUserControl muc = (MyUserControl)obj;
Nullable<int> value = (Nullable<int>)args.NewValue;
if (value != null)
{
muc.MyNumberTextBlock.Text = value.ToString();
}
else
{
muc.MyNumberTextBlock.Text = "N/A";
}
}
1.c implement the MyNumber property (not dependency) to use the dependency property for easy in code use:
public Nullable<int> MyNumber{
get
{
return (Nullable<int>)GetValue(MyNumberProperty);
}
set
{
SetValue(MyNumberProperty, value);
OnTargetPowerChanged(this, new DependencyPropertyChangedEventArgs(TargetPowerProperty, value, value)); // Old value irrelevant.
}
}
In the XAML file bind the TextBlock control's text to the property (not dependency) to get the default value of the dependency property in case it is not set by the user of the control (assuming you called your root element of the user control "RootElement"):
This code:
< TextBlock Name="MyNumberTextBlock" Text="{Binding MyNumber, ElementName=RootElement}"/>
If you give the root UserControl element a name, then you can refer to it using ElementName:
<UserControl x:Class="muc"
Name="rootElement">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold">
<Label.Content>
<Binding ElementName="rootElement" Path="TestName" />
</Label.Content>
</Label>
</UserControl>
You can also use the markup extension syntax to make it a little shorter:
<UserControl x:Class="muc"
Name="rootElement">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold"
Content="{Binding TestName, ElementName=rootElement}"/>
</UserControl>
Also remember that your control will be created before its properties are set. You will either need to implement INotifyPropertyChanged or have TestName be a dependency property so that the binding is re-evaluated after the property is set.
I've only done this with Silverlight, but i wouldnt be surprised if it works in the exact same way!
// <summary>
// Xaml exposed TextExposedInXaml property.
// </summary>
public static readonly DependencyProperty TestNameProperty = DependencyProperty.Register("TestName", typeof(string), typeof(NameOfMyUserControl), new PropertyMetadata(string.empty));
// <summary>
// Gets or sets the control's text
// </summary>
public string TextExposedInXaml
{
get
{
return (string)GetValue(TestNameProperty );
}
set
{
SetValue(TestNameProperty , value);
// set the value of the control's text here...!
}
}
{Binding ElementName=x} binds to an element with name x in the element tree, there is nothing here that deals with property TestName. If you want a property on your user control, then you have to define the property in the class corresponding to that user control (in your case it would be muc), and use {Binding RelativeSource={RelativeSource FindAncestor, ...}} to reference it on your user control (see here for details), or give it a name so you can use ElementName.

Resources