How to Add Custom Silverlight XAML Attributes? - silverlight

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" />

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.

Silverlight: How to add a Dependency Property to multiple controls?

Is it possible to create a Dependency Property for multiple controls without resorting to subclass every one of it?
I thought about using Attached Properties but they nest only one level deep as far as I understand it.
I want something like this to be possible:
<!-- MyDataGrid implements the new Attached Properties SourceData and TargetData -->
<MyDataGrid>
<StackPanel>
<TextBox MyDataGrid.SourceData="{Binding Somewhere}" MyDataGrid.TargetData="{Binding Somewhere}" />
</StackPanel>
<CheckBox MyDataGrid.SourceData="{Binding Somewhere}" MyDataGrid.TargetData="{Binding Somewhere}" />
</MyDataGrid>
This won't work since the Attached Properties wouldn't be found in the TextBox since it's no direct descendent of MyDataGrid.
Background is that I try to automatically convert an old Xaml-like Gui-syntax into real Xaml and with the old system it was possible to set different sources and targets for changed data. Now I'm searching for a Xaml-solution that doesn't involve subclassing every control there is.
Thanks in advance.
are you sure you are using Attached property correctly?
public static readonly DependencyProperty SourceDataProperty = DependencyProperty.RegisterAttached(
"SourceData", typeof (string), typeof (MyDataGrid), new PropertyMetadata("test"));
public static void SetSourceData(DependencyObject obj, string sourceData)
{
obj.SetValue(SourceDataProperty, sourceData);
}
public static string GetSourceData(DependencyObject obj)
{
return (string) obj.GetValue(SourceDataProperty);
}
This worked for me.Though SetSourceData was not get called, but data was there.
To retrive data.
MyDataGrid.GetSourceData(tbox);
Where tbox is the instance of your TextBox.

How should I expose a collection on a UserControl?

I'm creating a UserControl which will be used in various scenarios. I need to expose a collection of strings from the UserControl and I'm not sure how to do it.
The two possible uses I see are:
a control on its containing control binds to the collection, e.g. a ListBox;
a property on the containing control's ViewModel needs to bind to the collection.
I can get the former to work with a public ObservableCollection<String> property on the UserControl but it won't work for the latter. (I get "Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Collections.ObjectModel.ObservableCollection`1[System.String]'.")
Is there a solution that will work for both?
EDIT
This is my stab at a UML diagram showing what I'm doing:
I think the problem is just that the binder can't understand generics. You should be able to get around that by inheriting from ObservableCollection<string> to make a non-generic class. You can use something like this:
class StringCollection : ObservableCollection<string> { }
Since you are using that property as a target of a binding, you must declare it as a DependencyProperty:
class ObjectSelectorView
{
public StringCollection ObjectNames
{
get { return (StringCollection)GetValue(ObjectNamesProperty); }
set { SetValue(ObjectNamesProperty, value); }
}
// Using a DependencyProperty as the backing store for ObjectNames.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty ObjectNamesProperty =
DependencyProperty.Register("ObjectNames", typeof(StringCollection),
typeof(ObjectSelectorView), null);
....
}
I would suggest that the error you are seeing is actually the result of your not implementing this property as dependency property. You should be using a dependency property here.
Don't expose the property on your control as a concrete ObservableCollection of anything. Instead expose the property as a simple non-generic IList.
In your control's constructor assign an initial empty instance of ObservableCollection<String> to this property. However the property should have a public setter and therefore your initial collection instance may be replaced by some other implementer of IList. Therefore you should limit your usage of this property internally to IList or gracefully degrade behaviour if the current instance does not have the other interfaces you might want.

Typed Data Templates in Silverlight

My understanding is that Silverlight does not support DataTemplates with a DataType attribute.
How then would you accomplish the following in SL (author is Josh Smith, full link below). In a nutshell, he's saying that if you bind a TabControl's tab pages to a collection of ViewModels, WPF will figure out how to display each one on the fly by looking for a DataTemplate that has the appropriate (matching) DataType set. Way cool, but I'm wondering how you would (could?) do this in Silverlight.
Applying a View to a ViewModel
MainWindowViewModel indirectly adds
and removes Workspace­ViewModel
objects to and from the main window's
Tab­Control. By relying on data
binding, the Content property of a
TabItem receives a
ViewModelBase-derived object to
display. ViewModelBase is not a UI
element, so it has no inherent support
for rendering itself. By default, in
WPF a non-visual object is rendered by
displaying the results of a call to
its ToString method in a TextBlock.
That clearly is not what you need,
unless your users have a burning
desire to see the type name of our
ViewModel classes!
You can easily tell WPF how to render
a ViewModel object by using typed
DataTemplates. A typed DataTemplate
does not have an x:Key value assigned
to it, but it does have its DataType
property set to an instance of the
Type class. If WPF tries to render one
of your ViewModel objects, it will
check to see if the resource system
has a typed DataTemplate in scope
whose DataType is the same as (or a
base class of) the type of your
ViewModel object. If it finds one, it
uses that template to render the
ViewModel object referenced by the tab
item's Content property.
The MainWindowResources.xaml file has
a Resource­Dictionary. That dictionary
is added to the main window's resource
hierarchy, which means that the
resources it contains are in the
window's resource scope. When a tab
item's content is set to a ViewModel
object, a typed DataTemplate from this
dictionary supplies a view (that is, a
user control) to render it, as shown
in Figure 10.in Figure 10.
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx in Figure 10.
Here is ONE way you can do it. I have used a technique like this in the past, and had great success with it.
Consider a very simple container that will create the view for you like this:
public class ViewMapper : ContentControl
{
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property.Name == "DataContext")
WhenDataContextChanges();
}
private void WhenDataContextChanges()
{
if (DataContext == null)
Content = null;
else
Content = ViewFactory.GetView(DataContext.GetType());
}
}
EDIT
So, you can use this control to do the mapping for you:
<Border DataContext="{Binding MyViewModel}">
<ViewMapper />
</Border>
END EDIT
Note that ViewMapper simply waits for the data context to change, looks up the appropriate view for the data type, and creates a new one. It relies on ViewFactory, which is a very simple static lookup that maps types to views:
public class ViewFactory
{
private static readonly Dictionary<string, Func<UIElement>> _registry = new Dictionary<string, Func<UIElement>>();
private static string Key(Type viewModelType)
{
return viewModelType.FullName;
}
public static void RegisterView(Type viewModelType, Func<UIElement> createView)
{
_registry.Add(Key(viewModelType), createView);
}
public static UIElement GetView(Type viewModelType)
{
var key = Key(viewModelType);
if (!_registry.ContainsKey(key))
return null;
return _registry[key]();
}
}
Then, you simply need to register the view mappings some place:
ViewFactory.RegisterView(typeof(SomeViewModel), () => new SomeView());
Note that ViewFactory could just as easily use Activator.CreateInstance instead of using the Func mechanism. Take that one step further, and you can use an IoC container... You could always decide to map via a string Name property on the ViewModel instead of a type... the possibilities are endless and powerful here.

Dependency property in app.xaml.cs

I am new to WPF and the below question may look silly for many, please pardon me.
How can I create a dependency property in app.xaml.cs?
Actually, I tried to created it. The below code,
public static DependencyProperty TempProperty =
DependencyProperty.Register("Temp", typeof(string), typeof(App));
public string Temp
{
get { return (string)GetValue(TempProperty); }
set { SetValue(TempProperty, value); }
}
throws the below compile time errors:
The name 'GetValue' does not exist in the current context
The name 'SetValue' does not exist in the current context
Can anybody help me in this?
Thank you!
DependencyProperties can only be created on DependencyObjects, and since Application (which your App class inherits from) doesn't implement it, you can't create a DependencyProperty directly on the App class.
I assume you want this property to support binding. If this is the case, you have two options:
Implement INotifyPropertyChanged in App.xaml.cs
Create a DependencyObject derived class with your properties on it, and expose it as a standard read-only property of your App. The properties can then be successfully bound by "dotting-down" to them.
i.e if your new property is called Properties, you can bind like so:
<TextBlock Text="{Binding Properties.Temp}" />
If the property needs to be the target of a Binding, then option #2 is your best bet.
You class that contains dependency properties must inherit from DependencyObject.

Resources