Is there a way to pass a parameter to a command through a keybinding done in code? - wpf

I am making a custom control I need to add some default keybindings, microsoft has already done with copy and paste in a textbox. However one of the keybindings needs to pass a parameter to the command which it is bound to. It is simple to do this in xaml, is there any way to do this in code?
this.InputBindings.Add(new KeyBinding(ChangeToRepositoryCommand, new KeyGesture(Key.F1)));

I found the answer:
InputBindings.Add(new KeyBinding(ChangeToRepositoryCommand, new KeyGesture(Key.F1)) { CommandParameter = 0 });
I apologize if my question was unclear.

The copy and paste commands are handled by the text box so parameters are not strictly passed, but i know what you are getting at.
I do this using a hack - and an attached property, like so
public class AttachableParameter : DependencyObject {
public static Object GetParameter(DependencyObject obj) {
return (Object)obj.GetValue(ParameterProperty);
}
public static void SetParameter(DependencyObject obj, Object value) {
obj.SetValue(ParameterProperty, value);
}
// Using a DependencyProperty as the backing store for Parameter. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ParameterProperty =
DependencyProperty.RegisterAttached("Parameter", typeof(Object), typeof(AttachableParameter), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
}
then in the xaml
<ListBox local:AttachableParameter.Parameter="{Binding RelativeSource={RelativeSource Self}, Path=SelectedItems}" />
which makes the parameter the selected items
then when the command fires on the window i use this to see if the command parameter is there (i call this from the can execute and the Executed)
private Object GetCommandParameter() {
Object parameter = null;
UIElement element = FocusManager.GetFocusedElement(this) as UIElement;
if (element != null) {
parameter = AttachableParameter.GetParameter(element as DependencyObject);
}
return parameter;
}
It is a hack, but i have not found another way to get the command parameter for a binding that is fired from a key binding. (I would love to know a better way)

Related

Bind to current item in ItemsControl (WP7.1 / 8.0 / Silverlight)

Windows Phone 7.1 project (WP 8.0 SDK), I want to pass current item in ItemTemplate to a user control.
XAML:
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ShipControl Ship="{Binding}" x:Name="ShipControl"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
Code behind ShipControl:
public object Ship
{
get
{
return GetValue(ShipProperty);
}
set
{
SetValue(ShipProperty, value);
}
}
//Used by xaml binding
public static readonly DependencyProperty ShipProperty = DependencyProperty.Register("Ship", typeof(Ship), typeof(Ship), new PropertyMetadata(null, new PropertyChangedCallback(OnShipChanged)));
private static void OnShipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//TODO: Set break point here
return;
}
However, when debugging Ship an object of value DataBinding is passed as value, not a Ship (therefore return type is object instead of Ship). That eventually causes an exception on SetValue.
Other bindings on Ship-properties do work, so I really have no idea. According to this question, above should work:
WPF Pass current item from list to usercontrol
See here for sample project which throws exception on data binding, because passed object is Binding instead of data object. http://dl.dropbox.com/u/33603251/TestBindingApp.zip
You need to put a x:Name="MyControl" in your control, and then your binding will look like Ship="{Binding ElementName=MyList, Path=CurrentItem}" instead of just {Binding} (which does not mean much AFAIK). Your control needs to expose the CurrentItem property.
If you do not want to explicity name your control, you can try to play with Relative Source but I did not try myself so cannot help you on this one.
Your Dependency Property is badly formed so the XAML parser does not treat it as such.
You need to change your instance property type to Ship, and DependencyProperty owner type to ShipControl. Then the Binding will work (assuming that you are binding to a list of Ships).
public Ship Ship
{
get { return (Ship)GetValue(ShipProperty); }
set { SetValue(ShipProperty, value); }
}
public static readonly DependencyProperty ShipProperty =
DependencyProperty.Register("Ship", typeof(Ship), typeof(ShipControl), new PropertyMetadata(null, new PropertyChangedCallback(OnShipChanged)));
private static void OnShipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//TODO: Set break point here
return;
}

Why does binding fail when binding a child element to another element when the parent succeeds?

Say I have two classes that can reference a third UI object (in this example a button).
In addition, the parent class can contain an element of the child class.
If they both are bound to the same control, the same way, the child will fail but the parent succeed.
Is this a bug in WPF?
The parent :
class MyFrameworkElement : FrameworkElement
{
// A depenedency property that will contain a child element sub-element
private static readonly DependencyProperty ChildElementProperty =
DependencyProperty.Register("ChildElement",
typeof(MyChildElement),
typeof(MyFrameworkElement),
new PropertyMetadata());
[Category("ChildProperties")]
public MyChildElement ChildElement
{
set { SetValue(ChildElementProperty, value); }
get { return (MyChildElement)GetValue(ChildElementProperty); }
}
// Now, a reference to some other control, in this case we will bind a button to it!
public UIElement ButtonReferenceInParent
{
get { return (UIElement)GetValue(ButtonReferenceInParentProperty); }
set { SetValue(ButtonReferenceInParentProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonReferenceInParent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonReferenceInParentProperty =
DependencyProperty.Register("ButtonReferenceInParent", typeof(UIElement), typeof(MyFrameworkElement), new UIPropertyMetadata(null));
And then the child :
public class MyChildElement : FrameworkElement
{
public UIElement ButtonReferenceInChild
{
get { return (UIElement)GetValue(ButtonReferenceInChildProperty); }
set { SetValue(ButtonReferenceInChildProperty, value); }
}
public static readonly DependencyProperty ButtonReferenceInChildProperty =
DependencyProperty.Register("ButtonReferenceInChild", typeof(UIElement), typeof(MyChildElement), new UIPropertyMetadata(null));
}
OK -
Now say I Add them to my XAML like this :
<Grid>
<my:MyFrameworkElement x:Name="ParentName" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ButtonReferenceInParent="{Binding ElementName=buttonisme}">
<my:MyFrameworkElement.ChildElement>
<my:MyChildElement x:Name="ChildName" ButtonReferenceInChild="{Binding ElementName=buttonisme}"/>
</my:MyFrameworkElement.ChildElement>
</my:MyFrameworkElement>
<Button x:Name="buttonisme" Click="buttonisme_Click" />
</Grid>
Why does the binding work on the parent but then fail on the child, when I am using the EXACT same notation?
Here is my test code...
Console.WriteLine("Parent button reference is {0}", ParentName.ButtonReferenceInParent);
if (ChildName.ButtonReferenceInChild == null)
{
Console.WriteLine("Child button reference is null!");
}
else
{
Console.WriteLine("Child button is {0}", ChildName.ButtonReferenceInChild);
}
And here is the test result...
Parent button reference is System.Windows.Controls.Button
Child button reference is null!
The short answer to a long question is that Microsoft doesn't expect you to derive from FrameworkElement without doing a little plumbing.
Just doing derivation, breaks the logical tree which is used when doing binding by element name.
You probably also have to plum up the visual tree, and overload the arrange/measure parts of framework element. (We don't do that here as we aren't visual in the example.)
In this specific case we need to add any children of your object to the logical tree or break the ability to bind child elements.
A good example of someone who solved this is here
Information on overriding the logical tree is here
Anyways, the code needed to fix this SIMPLE example only relied on the logical tree (as the child object isn't really visual.)
Adding this function and changing the dependency property makes the binding work.
private static readonly DependencyProperty ChildElementProperty =
DependencyProperty.Register("ChildElement",
typeof(MyChildElement),
typeof(MyFrameworkElement),
new PropertyMetadata(OnChildElementChanged));
private static void OnChildElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyFrameworkElement control = d as MyFrameworkElement;
if (e.OldValue != null)
{
control.RemoveLogicalChild(e.OldValue);
}
control.AddLogicalChild(e.NewValue);
}
First, when you setup the xaml like this:
<my:MyFrameworkElement x:Name="ParentName" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ButtonReferenceInParent="{Binding ElementName=buttonisme}"/>
<my:MyChildElement x:Name="ChildName" ButtonReferenceInChild="{Binding ElementName=buttonisme}"/>
It works. I did this because I suspect a visual tree upwards traversal search for the Element Name you use in the binding.
I am still figuring out how the binding can be succesfull in your nested scenario. But maybe this may give you some hint...

IMarkupExtension with DependencyProperties

I'm trying to create a custom markup extension using IMarkupExtension<T> that has some DependencyProperties for binding. However, I am struggling to resolve the problem of the markup extension being resolved at XAML parse time, and the bindings only later. I don't seem to ever get something through the bindings: they're always null and never call their change callback.
The docs mention something about returning the instance of the markup extension (under "Returning the Current Markup Extensions Instance"), but that seems to make stuff explode because it's the wrong type for the target. This SL5 MultiBinding seems to return a proxy binding to an internal source object, but I can't manage to get that working: my bindings still don't ever set.
I can't seem to find any solid information how how to actually implement markup extensions with DependencyProperties (even though it seemed like something a lot of people were excited about with SL5...). Can anyone offer any guidance or tutorials?
Specifically, what I'm trying to do is create a markup extension that can dynamically construct a path to do a binding to a list, like so:
{my:ListLookup ListPath='List' Index={Binding Index}}
I'm wanting it to basically output a Binding that would look like {Binding List[Index]}, where Index is dynamic. The purpose of doing this over, say, a MultiBinding on the list and index, is so that we are binding directly to the object and get change notifications. (If there's a better way of doing this...)
I've fiddled with this a lot more and I've found the solution. It's based on the implementation of the SL5 MultiBinding that I linked to in the question.
The trick is that a Binding on a MarkupExtension will never be evaluated because it doesn't have a DataContext or something, but if you take the BindingExpression from it and throw it into a proxy Attached Property (attached to the target object) then you can get the Binding to resolve.
Below is a simple MarkupExtension that demonstrates this. All it's doing is taking a single Binding and outputting its value (obeying changes appropriately), but it shows how it holds together. This can be extended to solve the dictionary issue I was talking about, along with this problem in general.
public class SimpleBindingMarkupExtension : DependencyObject, IMarkupExtension<object>, INotifyPropertyChanged
{
public object Binding
{
get { return (object)GetValue(BindingProperty); }
set { SetValue(BindingProperty, value); }
}
public static readonly DependencyProperty BindingProperty =
DependencyProperty.Register(
"Binding",
typeof(object),
typeof(SimpleBindingMarkupExtension),
new PropertyMetadata(null));
public static readonly DependencyProperty ProxyAttachedBindingProperty =
DependencyProperty.RegisterAttached(
"ProxyAttachedBinding",
typeof(object),
typeof(SimpleBindingMarkupExtension),
new PropertyMetadata(null, OnProxyAttachedBindingChanged));
public static readonly DependencyProperty AttachedMarkupExtensionProperty =
DependencyProperty.RegisterAttached(
"AttachedMarkupExtension",
typeof(SimpleBindingMarkupExtension),
typeof(SimpleBindingMarkupExtension),
new PropertyMetadata(null));
private object _bindingSource;
public object BindingSource
{
get { return _bindingSource; }
set
{
_bindingSource = value;
OnPropertyChanged("BindingSource");
}
}
private static void OnProxyAttachedBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Pull the MarkupExtension from the attached property
var markupExtension = (SimpleBindingMarkupExtension) d.GetValue(AttachedMarkupExtensionProperty);
markupExtension.ProxyAttachedBindingChanged(e.NewValue);
}
private void ProxyAttachedBindingChanged(object value)
{
BindingSource = value;
}
public object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = (IProvideValueTarget) serviceProvider.GetService(typeof (IProvideValueTarget));
DependencyObject targetObject = target.TargetObject as DependencyObject;
if (targetObject == null)
return null;
// Attach this MarkupExtension to the object so we can find it again from attached property change callbacks
targetObject.SetValue(AttachedMarkupExtensionProperty, this);
// Put binding onto proxy attached property, so it actually evaluates
var localValue = ReadLocalValue(BindingProperty);
var bindingExpression = localValue as BindingExpression;
if (bindingExpression == null)
{
return localValue;
}
Binding originalBinding = bindingExpression.ParentBinding;
BindingOperations.SetBinding(targetObject, ProxyAttachedBindingProperty, originalBinding);
// Give the target a proxy Binding that binds to a property on the MarkupExtension
Binding binding = new Binding
{
Path = new PropertyPath("BindingSource"),
Source = this
};
return binding.ProvideValue(serviceProvider);
}
#region INotifyPropertyChanged
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Usage:
<TextBlock Text="{local:SimpleBindingMarkupExtension Binding={Binding Text}}"/>
As mentioned, this example will produce the same result as just saying Text="{Binding Text}", but shows the solution.

Attached Property and Binding

I'm creating an attached behavior in order to set a regular property of a class:
public class LookupHelper
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached("ItemsSource", typeof(object), typeof(LookupHelper), new UIPropertyMetadata(null, OnItemsSourceChanged));
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as MyControl;
if(control == null)
return;
control.ItemsSource = (IEnumerable)e.NewValue;
}
public static object GetItemsSource(GridColumn column)
{
return column.GetValue(ItemsSourceProperty);
}
public static void SetItemsSource(GridColumn column, object value)
{
column.SetValue(ItemsSourceProperty, value);
}
}
Here, ItemsSource property on MyControl is a regular property, so I can not bind it in Xaml, hence this attached behavior.
Now, when I use this attached property using string or objects it works and breakpoint I set is hit, but when I set it with Binding markup, it never runs. Why isn't this working?
<MyControl ctrl:LookupHelper.ItemsSource="DataSource"/>; //It works
<MyControl ctrl:LookupHelper.ItemsSource="{Binding Path=MyDataSource}"/>; //Does not work
What I need to do is to set the ItemsSource property to the value specified by the Binding.
In your Get and Set methods, you're defining the receiving object as GridColumn where it should be DependencyObject.
You might also want to change the type of your DP from object to IEnumerable since your casting to that in your change handler.
Can you please post the markup you are using? Also, If the actual property exists on an object and makes sense there then I think you should be using a regular dependency property on that object instead of an attached property on a helper class.
Edit
From MSDN:
The signature for the GetPropertyName accessor must be:
public static object GetPropertyName(object target)
and the signature for the SetPropertyName accessor must be:
public static void SetPropertyName(object target, object value)
In your case, is GridColumn the correct target type?

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