Is it possible to add my own custom property to any control?
I want to do something similar to e.g. x:Name="aname".
So I could write in xaml:
<TextBox local:MyCustomProperty="myValue" />
Maybe someone can provide a small sample or a link showing how to do this.
In google I only find markup extensions, which doesn't seem to be what I am looking for.
You can use an Attached Property.
public class MyCustomProperty
{
#region Color dependency property
public static readonly DependencyProperty ColorProperty;
public static Color GetColor(DependencyObject obj)
{
return (Color)obj.GetValue(ColorProperty);
}
public static void SetColor(DependencyObject obj, Color value)
{
obj.SetValue(ColorProperty, value);
}
#endregion
static MyCustomProperty()
{
//register attached dependency property
var metadata = new FrameworkPropertyMetadata(Colors.Transparent);
ColorProperty = DependencyProperty.RegisterAttached("Color",
typeof(Color),
typeof(MyCustomProperty), metadata);
}
}
And in XAML
<DockPanel custom:MyCustomProperty.Color="{StaticResource MyColor}" >
Related
I have previously used the following code I found somewhere to implement a BindingProxy in WPF.
public class BindingProxy : Freezable
{
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return this.GetValue(DataProperty); }
set { this.SetValue(DataProperty, value); }
}
}
I have tried to create a similar class for my Xamarin application which looks like this.
public class BindingProxy : BindableObject
{
// Using a DependencyProperty as the backing store for Data.
// This enables animation, styling, binding, etc...
public static readonly BindableProperty DataProperty = BindableProperty.Create(nameof(Data), typeof(object), typeof(BindingProxy), null);
public object Data
{
get { return this.GetValue(DataProperty); }
set { this.SetValue(DataProperty, value); }
}
}
This is the closest I could come up with to the Freezable class in Xamarin, unfortunately however when I declare my xaml as so
<ContentPage.Resources>
<binding:BindingProxy x:Key="BindingProxy" Data="{Binding BindingContext}" />
</ContentPage.Resources>
the Data property is never set to the BindingContext (or any other value) and returns the default value (null).
Can anyone provide any insight to what I might be doing wrong?
According to the answers here resources are not provided with the BindingContext and therefore do not support databinding.
Depending on your use case there might be another option to get the specific BindingContext, so you maybe want to explain that a little.
I am using SharpVector's SvgViewBox to show static resource images like this:
<svgc:SvgViewbox Source="/Resources/label.svg"/>
which works fine. However, I wish to control what image is shown through a binding to a view model.
The problem I'm experiencing is that the Source property of SvgViewbox is not bindable.
How can I get around this limitation without violating MVVM (e.g., passing the control into the view model and modifying it there)?
What you are looking for is called attached properties. MSDN offers a topic on it with the title "Custom Attached Properties"
In your case it may look as simple as this
namespace MyProject.Extensions
{
public class SvgViewboxAttachedProperties : DependencyObject
{
public static string GetSource(DependencyObject obj)
{
return (string) obj.GetValue(SourceProperty);
}
public static void SetSource(DependencyObject obj, string value)
{
obj.SetValue(SourceProperty, value);
}
private static void OnSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var svgControl = obj as SvgViewbox;
if (svgControl != null)
{
var path = (string)e.NewValue;
svgControl.Source = string.IsNullOrWhiteSpace(path) ? default(Uri) : new Uri(path);
}
}
public static readonly DependencyProperty SourceProperty =
DependencyProperty.RegisterAttached("Source",
typeof (string), typeof (SvgViewboxAttachedProperties),
// default value: null
new PropertyMetadata(null, OnSourceChanged));
}
}
XAML to use it
<SvgViewbox Margin="0 200"
local:SvgViewboxAttachedProperties.Source="{Binding Path=ImagePath}" />
Note that local is the namespace prefix and it should point to your assembly/namespace where that class is located at, i.e. xmlns:local="clr-namespace:MyProject.Extensions;assembly=MyProject".
Then only use your attached property (local:Source) and never the Source property.
The new attached property local:Source is of type System.Uri. To update the image first assign null then the filename/filepath again.
I am working on a DependencyProperty for my Avalon dock controller. Here is some sample code which i have currently working on.
Requirement is: Create all dependency properties in one single class and access the property in View. Something like this.
<Button isPaneVisible="true"> or <Button isPaneVisible="{Staticresource path=name, Mode=twoway">
Could you please help me to reslove this issue?
namespace Avatar.UI.ViewModel
{
internal class DependencyPropertyClass : DependencyObject
{
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.RegisterAttached("IsPaneVisible", typeof(bool), typeof(DependencyPropertyClass),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsPaneVisible_PropertyChanged));
private static void IsPaneVisible_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
/// <summary>
/// Sets the IsPaneVisible for an element.
/// </summary>
public bool IsPaneVisible
{
get { return (bool)GetValue(IsPaneVisibleProperty); }
set
{
SetValue(IsPaneVisibleProperty, value);
}
}
}
}
<UserControl x:Class="Avatar.UI.View.ContentView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:avalonDock="http://avalondock.codeplex.com"
xmlns:local="clr-namespace:Avatar.UI.ViewModel"
d:DesignHeight="300" d:DesignWidth="300">
<Button IsPaneVisible="true"></Button
</UserControl>
Defining an attached dependency property also requires the definition of static get and set accessor methods. See Custom Attached Properties for more information. Note also that your class does not necessarily need to be derived from DependencyObject as long as it only defines attached properties. But it is always a good idea to define such properties in a public class.
public class DependencyPropertyClass
{
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.RegisterAttached("IsPaneVisible", typeof(bool), typeof(DependencyPropertyClass),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsPaneVisible_PropertyChanged));
private static void IsPaneVisible_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static bool GetIsPaneVisible(DependencyObject obj)
{
return (bool)obj.GetValue(IsPaneVisibleProperty);
}
public static void SetIsPaneVisible(DependencyObject obj, bool value)
{
obj.SetValue(IsPaneVisibleProperty, value);
}
}
And as Cyborgx37 has pointed out, you would use an attached property in XAML like this:
<Button local:DependencyPropertyClass.IsPaneVisible="True" />
I could be wrong, but I think you are looking for this:
<Button local:DependencyPropertyClass.IsPaneVisible="true"></Button>
You have to specify the namespace, since IsPaneVisible is not part of the "http://schemas.microsoft.com/winfx/2006/xaml/presentation" namespace.
See: Attached Properties Overview
EDIT
It's been a while since I've done this, so things are slowly coming back to me as I scan your code. For an attached property, you cannot use an instance property to get/set the property. You must create static Get<PropertyName> and Set<PropertyName> functions:
public static void SetIsPaneVisible(DependenyObject target, Boolean value)
{
target.SetValue(IsPaneVisibleProperty, value);
}
public static bool GetIsPaneVisible(DependenyObject target)
{
return (bool)target.GetValue(IsPaneVisibleProperty);
}
Seriously... please read the linked article. It's all explained there.
The dependency property should derive the base calss for which you are going to create dependency property. for example, if you are going to create dependency property for button, then derive the base class button to your class.
This is how I have resolved my issue.
I'm trying to create a GUI (WPF) Library where each (custom) control basically wraps an internal (third party) control. Then, I'm manually exposing each property (not all of them, but almost). In XAML the resulting control is pretty straightforward:
<my:CustomButton Content="ClickMe" />
And the code behind is quite simple as well:
public class CustomButton : Control
{
private MyThirdPartyButton _button = null;
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
public CustomButton()
{
_button = new MyThirdPartyButton();
this.AddVisualChild(_button);
}
protected override int VisualChildrenCount
{
get
{ return _button == null ? 0 : 1; }
}
protected override Visual GetVisualChild(int index)
{
if (_button == null)
{
throw new ArgumentOutOfRangeException();
}
return _button;
}
#region Property: Content
public Object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(Object),
typeof(CustomButton),
new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeContent))
);
private static void ChangeContent(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
(source as CustomButton).UpdateContent(e.NewValue);
}
private void UpdateContent(Object sel)
{
_button.Content = sel;
}
#endregion
}
The problem comes after we expose MyThirdPartyButton as a property (in case we don't expose something, we would like to give the programmer the means to use it directly). By simply creating the property, like this:
public MyThirdPartyButton InternalControl
{
get { return _button; }
set
{
if (_button != value)
{
this.RemoveVisualChild(_button);
_button = value;
this.AddVisualChild(_button);
}
}
}
The resulting XAML would be this:
<my:CustomButton>
<my:CustomButton.InternalControl>
<thirdparty:MyThirdPartyButton Content="ClickMe" />
</my:CustomButton.InternalControl>
And what I'm looking for, is something like this:
<my:CustomButton>
<my:CustomButton.InternalControl Content="ClickMe" />
But (with the code I have) its impossible to add attributes to InternalControl...
Any ideas/suggestions?
Thanks a lot,
--
Robert
WPF's animation system has the ability to set sub-properties of objects, but the XAML parser does not.
Two workarounds:
In the InternalControl property setter, take the value passed in and iterate through its DependencyProperties copying them to your actual InternalControl.
Use a build event to programmatically create attached properties for all internal control properties.
I'll explain each of these in turn.
Setting properties using the property setter
This solution will not result in the simplified syntax you desire, but it is simple to implement and will probably solve the main problem with is, how to merge values set on your container control with values set on the internal control.
For this solution you continue to use the XAML you didn't like:
<my:CustomButton Something="Abc">
<my:CustomButton.InternalControl>
<thirdparty:MyThirdPartyButton Content="ClickMe" />
</my:CustomButton.InternalControl>
but you don't actually end up replacing your InternalControl.
To do this, your InternalControl's setter would be:
public InternalControl InternalControl
{
get { return _internalControl; }
set
{
var enumerator = value.GetLocalValueEnumerator();
while(enumerator.MoveNext())
{
var entry = enumerator.Current as LocalValueEntry;
_internalControl.SetValue(entry.Property, entry.Value);
}
}
}
You may need some additional logic to exclude DPs not publically visible or that are set by default. This can actually be handled easily by creating a dummy object in the static constructor and making a list of DPs that have local values by default.
Using a build event to create attached properties
This solution allows you to write very pretty XAML:
<my:CustomButton Something="Abc"
my:ThirdPartyButtonProperty.Content="ClickMe" />
The implementation is to automatically create the ThirdPartyButtonProperty class in a build event. The build event will use CodeDOM to construct attached properties for each property declared in ThirdPartyButton that isn't already mirrored in CustomButton. In each case, the PropertyChangedCallback for the attached property will copy the value into the corresponding property of InternalControl:
public class ThirdPartyButtonProperty
{
public static object GetContent(...
public static void SetContent(...
public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached("Content", typeof(object), typeof(ThirdPartyButtonProperty), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
((CustomButton)obj).InternalControl.Content = (object)e.NewValue;
}
});
}
This part of the implementation is straightforward: The tricky part is creating the MSBuild task, referencing it from your .csproj, and sequencing it so that it runs after the precompile of my:CustomButton so it can see what additional properties it needs to add.
I’m creating a UserControl for a rich TreeView (one that has context menus for renaming nodes, adding child nodes, etc.). I want to be able to use this control to manage or navigate any hierarchical data structures I will create. I currently have it working for any data structure that implements the following interface (the interface need not actually be implemented, however, only the presence of these members is required):
interface ITreeItem
{
string Header { get; set; }
IEnumerable Children { get; }
}
Then in my UserControl, I use templates to bind my tree to the data structure, like so:
<TextBlock x:Name="HeaderTextBlock" Text="{Binding Path=Header}" />
What I would like to do is define the name of each of these members in my RichTreeView, allowing it to adapt to a range of different data structures, like so:
class MyItem
{
string Name { get; set; }
ObservableCollection<MyItem> Items;
}
<uc:RichTreeView ItemSource={Binding Source={StaticResource MyItemsProvider}}
HeaderProperty="Name" ChildrenProperty="Items" />
Is there any way to expose the Path of a binding inside a UserControl as a public property of that UserControl? Is there some other way to go about solving this problem?
Perhaps this might help:
Create a new Binding when you set the HeaderProperty property on the Header dependency property:
Header property is your normal everyday DependencyProperty:
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(ownerclass));
and the property of your HeaderProperty works as follows:
public static readonly DependencyProperty HeaderPropertyProperty =
DependencyProperty.Register("HeaderProperty", typeof(string), typeof(ownerclass), new PropertyMetadata(OnHeaderPropertyChanged));
public string HeaderProperty
{
get { return (string)GetValue(HeaderPropertyProperty); }
set { SetValue(HeaderPropertyProperty, value); }
}
public static void OnHeaderPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (args.NewValue != null)
{
ownerclass c = (ownerclass) obj;
Binding b = new Binding();
b.Path = new PropertyPath(args.NewValue.ToString());
c.SetBinding(ownerclass.HeaderProperty, b);
}
}
HeaderProperty is your normal everyday DependencyProperty, with a method that is invoked as soon as the HeaderProperty changes. So when it changes , it creates a binding on the Header which will bind to the path you set in the HeaderProperty. :)