Tag Property in WPF DataGrid Column - wpf

I need to save an string inside a Datagrid Column which differs from the Header.
This is needed because I generate a Datagrid dynamically and want to translate the Column Headers while generating them. Then I bind the whole XAML to a ContentControl.
No problem till here... But I want to reorder and resize the columns, so I need to lookup them afterwoods. For this I need the original (not translated) ColumnHeader.
In my opinion a Tag property of the column would solve this problem, but there is no :(

In WPF, you have virtually unlimited "Tag" properties by using Attached Properties. An attached property can be set on any DependencyObject. A good example of such an attached property is Grid.Row. Since you can define them, you also have the possibility of naming them something more meaningful than Tag.
Sample code for defining an attached property:
public static class SomeClass {
public static readonly DependencyProperty TagProperty = DependencyProperty.RegisterAttached(
"Tag",
typeof(object),
typeof(SomeClass),
new FrameworkPropertyMetadata(null));
public static object GetTag(DependencyObject dependencyObject) {
return dependencyObject.GetValue(TagProperty);
}
public static void SetTag(DependencyObject dependencyObject, object value) {
dependencyObject.SetValue(TagProperty, value);
}
}
Usage :
<DataGridColumn SomeClass.Tag="abc" />

Related

Why should I use an attached property instead of a regular dependency property?

I just discovered than I can do the following:
var button = new Button();
button.SetValue(TextBlock.TextProperty, "text");
var text = (string)button.GetValue(TextBlock.TextProperty); // text is "text"
While the above example is a bit unrealistic, it does show that I can attach a regular dependency property onto another object. It doesn't have to be a an attached property (TextBlock.TextProperty is not registerd with DependencyProperty.RegisterAttached().
This bares the questions why are there attached properties in the first place? The only difference I can see for now ist that I can't attach regular dependency properties in XAML. But that's about it. Are there any other differences?
Update:
To make it more clear, the below code works and looks pretty close to an attached property from the end users perspective:
public static class AttachedPropertyDeclarer
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(Button),
new PropertyMetadata(default(string),OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// do something when text changed
}
}
...
button.SetValue(AttachedPropertyDeclarer.TextProperty, "text");
var text = (string)button.GetValue(AttachedPropertyDeclarer.TextProperty);
Compare this to the attached property way:
public static class AttachedPropertyDeclarer
{
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(AttachedPropertyDeclarer),
new PropertyMetadata(default(string),OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// do something when text changed
}
}
The only effective differnce to an attached property here is that I have to declare the owner of type Button whereas in a attached property it would usually be AttachedPropertyDeclarer. But this only needs to be done if I need a changed event handler (i.e. OnTextChanged).
Regarding your example, you have not as you say, attached a regular dependency property onto another object. All your code has achieved is to store a string value in a Dictionary along with a reference to your object. That does not make it an Attached Property - importantly, you cannot access that string value from the Button directly, as there is no Text property on a Button.
What your code does is actually very similar to this:
Dictionary<object, object> values2 = new Dictionary<object, object>();
var button = new Button();
values2.Add(button, "text");
string text = values2[button].ToString();
Now to answer your question:
The main reason to declare an Attached Property is in order to add a property to a type that you didn't declare, thereby extending its functionality.
A great example of this would be to add a SelectedItems property to the ItemsControl or ListBox class. In doing so, we extend the current, or default functionality of the class. Another good example would be declaring an Attached Property that automatically brings added items into view (again in an ItemsControl or ListBox class).
UPDATE >>>
According to your comments, you seem to be refusing to accept the differences that I have outlined... you said:
There is literally no difference from the end users perspective except that I can't use it in XAML.
Firstly, do you not think that this is a huge difference?.. you won't be able to use it for data binding for a start. Furthermore, you keep saying that you can attach a property to a type that you haven't declared using a DependencyProperty, but you are 100% incorrect. You can reference an Attached Property directly in both code and XAML, while you can't reference what you are calling your attached property directly in either XAML or code.
All you are doing is storing a value in a Dictionary and you certainly don't need the overhead of a DependencyProperty to do that. There really is no comparison between doing that and declaring an Attached Property. From the Attached Properties Overview page on MSDN:
You might create an attached property when there is a reason to have a property setting mechanism available for classes other than the defining class.
Note the following part: a property setting mechanism
Adding values into a Dictionary is not a property setting mechanism. So again, you lose the ability to use your pretend Attached Property in Styles, Animations, Triggers, etc.
To clarify this situation for once and for all, you can develop a simple test project. Implement the IList SelectedItems Attached Property for a ListBox that I mentioned (you can find online tutorials for this) and then do the same using your pretend Attached Property (if it is even possible). The difference in the simplicity of development bewteen the two will clearly show you why you should use an Attached Property instead of a regular DependencyProperty.
If you look closely at dependency property identifier, all DP's are registered with class DependencyProperty and we pass the Owner class type and property name at time of registration.
Sample:
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register(
"IsSpinning", typeof(Boolean), typeof(OwnerClass));
At time of registration it creates some unique hash code combining property name and owner class type to represent each DP uniquely.
So, when you set value for that DP on some object like in your case on Button, code flow is like this:
First it will get the unique value generated at time of registration of property and add the key value pair in private dictionary named _effectiveValues declared in class Dependency Object with Key set to unique hashcode at time of registration and value being the value set by user.
Note - No written documentation for this on MSDN but verified this by peeking into source code using reflector.
So, when you set the value from code behind it will work like I mentioned above because it does not validate before adding value in the dictionary if it belongs to that type or not and fetching value will get you the value from dictionary.
Not sure but might be constraint is there in XAML only where WPF guys enforced the type check. Sadly there is no written documentation for this on MSDN.
Attached properties are discovered, when you want to have control over an existing control, but dont want to extend it. A pretty good example is, there is no way to bind BlackOutDates property in XAML for WPF DatePicker. In that case you can use an Attached Property to attach a custom functionality to map the BlackOutDates. This suits good in MVVM, since attached properties provided way for binding in XAML.
public class BlackOutDatesAdapter
{
public static List<DateTime> GetBlackOutDates(DependencyObject obj)
{
return (List<DateTime>)obj.GetValue(BlackOutDatesProperty);
}
public static void SetBlackOutDates(DependencyObject obj, List<DateTime> value)
{
obj.SetValue(BlackOutDatesProperty, value);
}
// Using a DependencyProperty as the backing store for BlackOutDates. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BlackOutDatesProperty =
DependencyProperty.RegisterAttached("BlackOutDates", typeof(List<DateTime>), typeof(BlackOutDatesAdapter), new PropertyMetadata(null, OnBlackOutDatesChanged));
private static void OnBlackOutDatesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as DatePicker;
var list = (List<DateTime>)e.NewValue;
foreach(var date in list)
{
control.BlackoutDates.Add(new CalendarDateRange(date));
}
}
}
Binding in XAML will look like this,
<DatePicker VerticalAlignment="Center"
Width="200"
local:BlackOutDatesAdapter.BlackOutDates="{Binding BlackOutDates}"
DisplayDate="{Binding DisplayDate}" />
In the callback of property, you can do your own mapping of adding the dates to DatePicker. For more information, please read this post.

How to Databind TextBox to Property in CodeBehind

I am using Expression Blend.
Let's say I got:
Public string FirstName{get;set;}
Edit: thanks for the answers, but I'm afraid people didn't understand my question. I do know how to Bind Data in Code or in XAML.
My question is if there is a way to do all that with the Expression Blend Interface without writing it directly. Only with mouse movements.
You would actually want to put the property on a View Model, and use XAML binding, but that is another story.
As you describe your example, you would first need to implement the "FirstName" property as a Dependency Property and not a simple get/set. Here is a great code-snippet from Shawn Wildermuth to save lots of typing (there is a single typo in the snippet you need to fix - "($type$)args.NewValue;"... NewValue has the wrong case in the snippet).
You can bind in XAML to a simple get/set property, but it is a one-way/one-time binding and will not update with changes.
In code, the binding requires two things to be set.
Set the DataContext of the control (or the page) and
Set a data binding on the control.
For the example you mention you could use code like the following (assumes a TextBox control called myTextBox in the Xaml):
using System.Windows;
using System.Windows.Controls;
namespace BindingCodeTest
{
public partial class BindingCode : UserControl
{
public string FirstName
{
get { return (string)GetValue(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
// Using a DependencyProperty as the backing store for FirstName.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty FirstNameProperty =
DependencyProperty.Register("FirstName",
typeof(string),
typeof(BindingCode),
new PropertyMetadata(string.Empty,
new PropertyChangedCallback(OnFirstNameChanged)));
static void OnFirstNameChanged(object sender, DependencyPropertyChangedEventArgs args)
{
// Get reference to self
BindingCode source = (BindingCode)sender;
// Add Handling Code
string newValue = (string)args.NewValue;
}
public BindingCode()
{
InitializeComponent();
myTextBox.DataContext = this;
myTextBox.SetBinding(TextBox.TextProperty, new System.Windows.Data.Binding("FirstName"));
FirstName = "First name"; // Sample change
}
}
}
In Blend 4, on the 'Data' tab > New sample Data.. > name data source as you like, f.e. 'MySampleDataSource'. Then your 'MySampleDataSource' will have a '+' button (the same Data tab on the right) with 3 options. Choose 'Add simple property' and name it 'FirstName'. Then drag that property on your TextBox or TextBlock.
The result is like this:
<TextBlock x:Name="firstName" Text="{Binding FirstName}"/>

Using dependency properties in wpf

I'm not quite sure if I've got the right grasp on this or not, what I've read seems to agree with what I'm trying to do, however It doesn't seem to be working.
If I add an additional owner to a dependency property of a class, whenever the orig class dp changes, the change should get propagated to the additional owner, correct?
What I have is a custom control, which I want to set a property on, and then on certain objects that are within the custom control data template inherit this property value.
public class Class1: DependencyObject{
public static readonly DependencyProperty LongDayHeadersProperty;
public bool LongDayHeaders {
get { return (bool)GetValue(LongDayHeadersProperty); }
set { SetValue(LongDayHeadersProperty, value); }
}
static Class1(){
LongDayHeadersProperty = DependencyProperty.Register("LongDayHeaders", typeof(bool), typeof(Class1),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
}
}
public class Class2: DependecyObject{
public static readonly DependencyProperty LongDayHeadersProperty;
public bool LongDayHeaders{
get{ return(bool)GetValue(LongDayHeadersProperty); }
set{ SetValue(LongDayHeadersProperty, value); }
}
static Class2(){
LongDayHeadersProperty = Class1.LongDayHeadersProperty.AddOwner(typeof(Class2));
}
}
But if I assign a DependencyPropertyDescriptor to both properties, it only fires for the Class1 and Class2 doesn't change.
Have I missed something in my understanding?
UPDATE
After some testing, I'm not even sure if my child control is considered a child control within the logical or visual tree. I think it is, but the lack of success leads me to believe otherwise.
There a many class2's which exist in an observable collection of class1. This, to me, makes them childs of class1? But even if I use RegisterAttach on class2, and set the property in class1, it doesn't seem to have any effect?
As MSDN states, the Inherits flag only works when you use RegisterAttached to create the property. You can still use the property syntax for the property.
Update
For clarity, here is how I would define the properties:
public class Class1 : FrameworkElement
{
public static readonly DependencyProperty LongDayHeadersProperty =
DependencyProperty.RegisterAttached("LongDayHeaders",
typeof(bool),
typeof(Class1),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
public bool LongDayHeaders
{
get { return (bool)GetValue(LongDayHeadersProperty); }
set { SetValue(LongDayHeadersProperty, value); }
}
}
public class Class2: FrameworkElement
{
public static readonly DependencyProperty LongDayHeadersProperty =
Class1.LongDayHeadersProperty.AddOwner(typeof(Class2));
public bool LongDayHeaders
{
get{ return(bool)GetValue(LongDayHeadersProperty); }
set{ SetValue(LongDayHeadersProperty, value); }
}
}
If you want your children to be logical children of your control, you need to call the AddLogicalChild. Also, you should expose them through the LogicalChildren property. I must also point out that both classes must derive from FrameworkElement or FrameworkContentElement, as the logical tree is only defined for these elements.
Since you are using an ObservableCollection, you would handle the collection changed events and Add/Remove the children depending on the change. Also, the LogicalChildren property can just return your collection's enumerator.
You are confusing DependencyProperties with Attached (Dependency) Properties.
A DP is for when a class wants bindable, stylable etc properties on itself. Just like .NET properties, they are scoped within their classes. You can register for a property changed event on individual objects, but not globally. TextBox.Text is an example of this. Note that Label.Text is unrelated to TextBox.Text.
An AP is for when a class wants to decorate another object with additional properties. The class that declares the AP is able to listen for property changed events on ALL instances of other objects that have this AP set. Canvas.Left is an example of this. Note that you always have to qualify this setter: <Label Text="Hi" Canvas.Left="50"/>

How to Add Custom Silverlight XAML Attributes?

Is it possible to introduce 'custom' attributes into different UI Elements in XAML ? Also to read them later like we add attributes for server controls in ASP.NET ?
I intend to read specific attributes and operate on them together.
It sounds like you're trying to find Attached Properties.
An attached property lets you add in a property, definable in Xaml, which can be "attached" to any UIelement. You then retrieve them in code like any other Dependency Property.
Here is the approach I tend to take with this.
Create a new class file called Meta:-
namespace SilverlightApplication1
{
public static class Meta
{
#region SomeValue
public static string GetSomeValue(DependencyObject obj)
{
return (string)obj.GetValue(SomeValueProperty);
}
public static void SetSomeValue(DependencyObject obj, string value)
{
obj.SetValue(SomeValueProperty, value);
}
public static readonly DependencyProperty SomeValueProperty =
DependencyProperty.RegisterAttached("SomeValue", typeof(string), typeof(Meta),
new PropertyMetadata(null));
#end region
#region SomeOtherValue
// Boilerplate code from above.
#end region
}
}
A value can now be attached in XAML like this:-
<TextBox x:Name="txt" local:Meta.SomeValue="Hello, World!" />
At some point in code this value can be retrieved with:-
string value = Meta.GetSomeValue(txt);
Note you don't have to stick with String as the type of the property you can pretty much use any type you like with the limitation that if you can to attach it in XAML the type must be compatible with the way XAML constructs objects (for example requires a default constructor).
The way I've accomplished that is by creating a new class that inherits the base control.
For example, I have a class called WebTextBox that inherits TextBox. And inside WebTextBox are some custom properties and events. By doing this you're inheriting all the behaviors of the TextBox control. But you can get creative here if you choose, even modifying the behavior by overriding events and such.
Anyway, after you create the class you'll then have to add the namespace for the project to the XAML. Something like this:
xmlns:me="clr-namespace:YourNamespace;assembly=YourAssembly"
And then you can add a WebTextBox (or whatever you call it) like this:
<me:WebTextBox CustomAttribute="cool stuff" />

WPF: How to accept both string and FrameworkElement in dependency property (like wpf Label does)?

I am creating a custom WPF control that should have several content slots.
I'd like the user to be able to use either string, or a FrameworkElement as a value of property, for example:
<!-- MyHeading is a string -->
<MyControl MyHeading="Hello World" />
<MyControl>
<!-- MyHeading is a FrameworkElement -->
<MyControl.MyHeading>
<Expander Header="Hello">
World
</Expander>
</MyControl.MyHeading>
</MyControl>
I know that WPF ContentControl does this (accepts both strings and other elements), and I know that it has something to do with TypeConverter attribute (partially explained here), but I tried to look at ContentControl, Label, TextBlock and other controls in Reflector, and didn't find any TypeConverter atrribute there, and googling didn't help.
I first tried to implemet it like this, but it obviously doesn't know about how to convert string to FrameworkElement, and throws exception during control's initialization:
public FrameworkElement Heading
{
get { return (FrameworkElement)GetValue(HeadingProperty); }
set { SetValue(HeadingProperty, value); }
}
// Using a DependencyProperty as the backing store for Heading. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeadingProperty =
DependencyProperty.Register("Heading", typeof(object), typeof(DialogControl), new UIPropertyMetadata(new FrameworkElement()));
Then I tried to hack it like this:
public object Heading
{
get { return (object)GetValue(HeadingProperty); }
set
{
if (value is string)
{
var tb = new TextBlock();
tb.Text = (string) value;
tb.FontSize = 20;
SetValue(HeadingProperty, tb);
}
else if (value is FrameworkElement)
{
SetValue(HeadingProperty, value);
} else
throw new ArgumentOutOfRangeException("Heading can take only string or FrameworkElement.");
}
}
// Using a DependencyProperty as the backing store for Heading. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeadingProperty =
DependencyProperty.Register("Heading", typeof(object), typeof(DialogControl), new UIPropertyMetadata(null));
but it is pretty ugly and still doesn't instantiate :(.
Anyone knows how to do it? Thanks for your time!
The DependencyProperty should be of type Object. The magic happens in the when you bind the property as the Content for a ContentPresenter. You should also look into the ContentSource property if you want to handle Templating and StringFormatting properly.
As Bryan said just use object as your type. When WPF encounters a non-frameworkelement it will (assuming there is no DataTemplate to apply) call the object's ToString() method and use the text as the content. So not only can you use string, but also DateTime, Enum's, whatever.
Also, you should consider deriving from HeaderedContentControl if your control has both a header and main content. Then you don't need to implement either of those two content properties and you'll get all the bells and whistles for free such as data templating.
As the others have said, set the type to be object. To do type checking, use the validation callback on the dependency property. Then do checks for valid types.
public object Header
{
get { return (object)GetValue(HeadingProperty); }
set { SetValue(HeadingProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register(
"Heading",
typeof(object),
typeof(DialogControl),
new UIPropertyMetadata(null),
new ValidateValueCallback(Heading_Validation)
);
private static bool Heading_Validation(object source)
{
return source is string||
source is FrameworkElement ||
source == null;
}
This will check to see, before assignment, if the passed object is of type String, FrameworkElement or null.
Enjoy!

Resources