Setting XAML property value to user control - wpf

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.

Related

bind dependency property from custom class to UI

have tried for almost a hole week to find how to bind my dependency property that is in a custom class (Elements) to a simple text box,
Text inside textbox must change every time when i send particular data, this is happening for the first time only,after that, textbox want be updated ??
i have tried every single example but i could not reach my goal, here is my code :
for the class where the depency object created :
public class Elements : DependencyObject
{
public static DependencyProperty TextDataProperty = DependencyProperty.Register
(
"TextData",
typeof(string),
typeof(Elements),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender)
);
public string TextData
{
get
{
return (string)GetValue(TextDataProperty);
}
set
{
SetValue(TextDataProperty, value);
}
}
public void UpdateText(string sdata)
{
TextData = sdata;
}
}
i have refere to this class in xaml like so
xmlns:local="clr-namespace:WpfApplication1"
and to use it :
<Window.Resources>
<local:Elements x:Key="myElements" ></local:Elements>
</Window.Resources>
the text box that need to be updated is binded to the custom class like this :
<TextBox
Height="23"
HorizontalAlignment="Left"
Margin="12,61,0,0"
Name="textBox1"
VerticalAlignment="Top"
Width="237"
Text="{Binding TextData, Source={StaticResource myElements}}"
/>
what im doing wrong ?? can someone help please
From your comment: You are creating a new Elements object in
Elements _elements = new Elements();
_elements.UpdateText(textBox2.Text);
This is not the object that is used for the binding in
Text="{Binding TextData, Source={StaticResource myElements}}"
Change your code so that it accesses the object from the Resources:
var elements = (Elements)Resources["myElements"];
elements.UpdateText(textBox2.Text);
Then you should replace the TextBox by a TextBlock, because you only want to display text, but do not want the user to edit it.
You should also take a look at the MVVM design pattern and how it is used in WPF. There are plenty of online resources available.

Trigger on attached property of DataGridTextColumn

I am trying to define a custom attached property on DataGridTextColumn and writing a DataTrigger against it in my xaml file. Here is how the attached property (FilterDisplayStyle) is defined in my class.
//Dependency Property whether Column Filter is combobox or textbox or editable combobox.
public static FrameworkPropertyMetadata inheritsMetaData =
new FrameworkPropertyMetadata(FilterDisplayTypeEnum.TextBoxOnly, FrameworkPropertyMetadataOptions.Inherits);
public static DependencyProperty FilterDisplayTypeProperty = DependencyProperty.RegisterAttached("FilterDisplayType",
typeof(FilterDisplayTypeEnum), typeof(DataGridColumn), inheritsMetaData);
public static FilterDisplayTypeEnum GetFilterDisplayType(DependencyObject target) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter Element"); }
return (FilterDisplayTypeEnum)target.GetValue(FilterDisplayTypeProperty);
}
public static void SetFilterDisplayType(DependencyObject target, FilterDisplayTypeEnum value) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter Element"); }
target.SetValue(FilterDisplayTypeProperty, value);
}
The above attached property's type is FilterDisplayTypeEnum which is defined as below.
public enum FilterDisplayTypeEnum {
TextBoxOnly,
NonEditableComboBox,
EditableComboBox
}
Here is how I set this property in DataGridTextColumn
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" f:DataGridColumnExtensions.FilterDisplayType="NonEditableComboBox" />
....
</DataGrid.Columns>
Now I am trying to retrieve this property in using the following
<TextBox Text="{Binding Mode=OneWay, Path=FilterDisplayType, RelativeSource={RelativeSource AncestorType={x:Type DataGridTextColumn}}}"/>
But I don't get any text on my TextBox above.
Surprisingly, I have another attached property (this time attached to DataGrid instead) that works perfectly fine. The issue is only with DataGridTextColumn. Also, using WPF Inspector, I see there is no direct visual representation of DataGridTextColumn in the Visual Tree , so I was skeptical whether I could use FindAncestor way of binding on ancestor which is DataGridTextColumn. Can anyone help me out in this scenario. To Summarize, I can't access a custom attached property defined on DataGridTextColumn using FindAncestor type of Binding. Are there any alternatives to this?
regards,
Nirvan
Edit:
As per #Clemens suggestions, I changed the definition of the Attached Property to something like this. But I still can't access the attached property in my xaml.
Attached Property Definition:
public static DependencyProperty FilterDisplayTypeProperty = DependencyProperty.RegisterAttached("FilterDisplayType",
typeof(FilterDisplayTypeEnum), typeof(DataGridColumnExtensions), inheritsMetaData);
public static FilterDisplayTypeEnum GetFilterDisplayType(DataGridBoundColumn target) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter target"); }
return (FilterDisplayTypeEnum)target.GetValue(FilterDisplayTypeProperty);
}
public static void SetFilterDisplayType(DataGridBoundColumn target, FilterDisplayTypeEnum value) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter target"); }
target.SetValue(FilterDisplayTypeProperty, value);
}
I am still unable to access the property "FilterDisplayType" in my xaml code as given below
<TextBox Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridTextColumn}}, Path=FilterDisplayType}"/>
The owner type must be the type that declares the property, here DataGridColumnExtensions:
public static DependencyProperty FilterDisplayTypeProperty =
DependencyProperty.RegisterAttached("FilterDisplayType",
typeof(FilterDisplayTypeEnum),
typeof(DataGridColumnExtensions), // here
inheritsMetaData);
This seems to be a common misunderstanding with attached properties. See also here.
And also note that the syntax for binding to an attached property is (Class.Property), so you would need to bind like this:
<TextBox
Text="{Binding Path=(DataGridColumnExtensions.FilterDisplayType)}"/>
And just another note: i haven't quite understood why the property inherits. As far as i can see you intend to set it explicitly on DataGridTextColumn objects.

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.

How to change TextBlock Text from codebehind?

I have a custom control. There is a Stack Panel with Button and TextBlock in generic.xaml:
<StackPanel>
<TextBlock x:Name="StatusText" />
</StackPanel>
Then I have
public class MyClass : Control
{
// Constructor etc.
public static readonly DependencyProperty StatusTextProperty = DependencyProperty.Register("StatusText", typeof(TextBlock), typeof(MyClass), null);
public TextBlock StatusText
{
get { return (TextBlock)this.GetValue(StatusTextProperty); }
set { SetValue(StatusTextProperty, value); }
}
}
There is if with some logic in that happens after the button is clicked.
How do I change the Text property of TextBloc?
I thought that I can do something like this
StatusText.SetValue(TextBlock.TextProperty, "Some text here.");
But it always returns NullReferenceException (Object reference not set to an instance of an object.)
Should I use PropertyChangedCallback() on dependency property or what else do I need? I am missing something ;-)
You're taking the wrong approach - instead of trying to push the text into the text block from the control's class, you need the text block to pull the value from the control's class. The main steps you need to do are:
Change the type of the dependency property from TextBlock to string.
Bind the Text property of the TextBlock in your control template to the dependency property using a TemplateBinding binding expression. Something along the lines of:
<TextBlock Text="{TemplateBinding StatusText}" />
You can then simply set the text to be displayed to the property on your control.
Hope this helps...
Chris
You can type your question on google and find answer few times faster.

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 .

Resources