I have a UserControl that I am trying to hook up event handlers from a parent control to. I want to do as little in code as I can and am running into some problems. Here is my setup:
in NewUserControl code behind I have:
public RoutedEventHandler PrintClickHandler { get; set; }
public DependencyProperty PrintClickHandlerProperty =
DependencyProperty.Register("PrintClickHandler", typeof(RoutedEventHandler),
typeof(NewUserControl), new PropertyMetadata(null));
In MyParentControl I have:
public RoutedEventHandler PrintClickHandler
{
get { return btnPrintCall_Click; }
}
private void btnPrintCall_Click(object sender, RoutedEventArgs e)
{
// some code
}
And finally in the MyParentControls xaml I have the binding for NewUserControl.PrintClickHandler:
<NewUserControl PrintClickHandler="{Binding Path=PrintClickHandler,
RelativeSource={RelativeSource AncestorType=local:MyParentControl, AncestorLevel=1}}" />
Now in the debugger I can implement all the getters and setters the classic way to hit breakpoints when they are called. I see the getter for MyParentControl.PrintClickHandler in the being hit, but the setter for NewUserControl.PrintClickHandler is never hit. I also have no errors or warnings related to this binding in the output.
I've never tried doing events like this, but it does look like your dependency property may by setup incorrectly, try:
public RoutedEventHandler PrintClickHandler
{
get { return (RoutedEventHandler)GetValue(PrintClickHandlerProperty); }
set { SetValue(PrintClickHandlerProperty, value); }
}
public static DependencyProperty PrintClickHandlerProperty = DependencyProperty.Register(
"PrintClickHandler",
typeof(RoutedEventHandler),
typeof(NewUserControl),
new PropertyMetadata(null));
This question is erroneous. As I am dealing with an event, using a dependency property is inappropriate. I can simple add a public Event property in my code behind which can be set in the xaml to an appropriate event handler method.
Related
I have a UserControl called ActionsTreeView I built using MVVM practices where I have an IPluginsProvider interface that populates the data in my UserControl. I want to be able to provide an object implementating this IContentProvider interface as a parameter to initialize my UserControl's ViewModel.
Here is my approach so far, which isn't working. I am wondering if I'm going down the right path? I declare a DependencyProperty in my user control which is visible to my mainWindow where I want to instantiate this UserControl. This code just attempts to pass the PluginsProvider object to my UserControl which needs it to build its ViewModel.
My PluginProvider DependencyProperty setter in my UserControl never gets hit because my My PropertyChanged handler is always null in MainWindow.xaml.cs I think I have the code right, but not sure I'm going down the right road and what I'm missing to make this connection?
ActionsTreeView.xaml.cs
public partial class ActionsTreeView: UserControl
{
public static readonly DependencyProperty PluginProviderProperty = DependencyProperty.Register("PluginProvider", typeof(Models.IPluginsProvider), typeof(ActionsTreeView), new FrameworkPropertyMetadata(null, OnPluginProviderChanged));
private ViewModels.ActionsTreeViewModel vm;
public ActionsTreeView()
{
//Wire-up our ViewModel with the data provider and bind it to DataContext for our user control
//This is a Mock-up until I figure out a way to get the real provider here
Models.IPluginProvider pluginSource = new Models.MockPluginProvider();
vm = new ViewModels.ActionsTreeViewModel(pluginSource );
this.DataContext = vm;
InitializeComponent();
}
private static void OnPluginProviderChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((ActionsTreeView)source).PluginProvider = (Models.IPluginsProvider)e.NewValue;
}
public Models.IPluginsProvider PluginProvider
{
get
{
return (Models.IPluginsProvider)GetValue(PluginProviderProperty);
}
set
{
SetValue(PluginProviderProperty, value);
vm.SetPluginSource(PluginProvider);
}
}...
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
this.ActionProvider = new Models.PluginsProvider(Library.Action.AvailableActions);
}
private Models.IPluginsProvider _actionProvider;
public Models.IPluginsProvider ActionProvider
{
get { return _actionProvider; }
set
{
_actionProvider = value;
OnPropertyChanged("ActionProvider");
}
}
protected void OnPropertyChanged(string property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) //HANDLER IS ALWAYS NULL
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
Using my UserControl in MainWindow.xaml
<Grid>
<UserControls:ActionsTreeView PluginProvider="{Binding ActionProvider}" />
</Grid>
I don't think you can pass a parameter in the ctor in xaml.
If you create control in code behind you can pass the parameter in the ctor(Param param)
Not sure if this fits in the MVVM model but I use it a lot in regular code behind
Use a frame in the XAML for a place to put the UserControl
Seems like you are missing the binding source
<Grid>
<UserControls:ActionsTreeView PluginProvider="{Binding ActionProvider, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />
</Grid>
since your property ActionProvider is declared in MainWindow so during binding you are required to refer the same source unless you've set it as data context of the window
alternative to above you can also do the below if there is no other data context used in the MainWindow then you can use the original binding you have PluginProvider="{Binding ActionProvider}"
public MainWindow()
{
InitializeComponent();
this.ActionProvider = new Models.PluginsProvider(Library.Action.AvailableActions);
DataContext = this;
}
I've set the DataContext to this which will effectively resolve the value of ActionProvider in binding from the instance this
Extra
you may also choose to remove INotifyPropertyChanged from MainWindow as it is already DependencyObject and capable of property notification and declare a DependencyProperty for ActionProvider
eg
public Models.IPluginsProvider ActionProvider
{
get { return (Models.IPluginsProvider)GetValue(ActionProviderProperty); }
set { SetValue(ActionProviderProperty, value); }
}
// Using a DependencyProperty as the backing store for ActionProvider. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ActionProviderProperty =
DependencyProperty.Register("ActionProvider", typeof(Models.IPluginsProvider), typeof(MainWindow), new PropertyMetadata(null));
so you don't need to worry for the notification change manually, you might be required to use this if the above solution does not work for you otherwise it is good to have.
I've created a custom control with, amongst others, the following:
public partial class MyButton : UserControl
{
public bool Enabled
{
get { return (bool)GetValue(EnabledProperty); }
set {
SetValue(EnabledProperty, value);
SomeOtherStuff();
}
}
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(bool), typeof(MyButton), new PropertyMetadata(true));
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(EnabledProperty, value);
}
public static bool GetEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(EnabledProperty);
}
}
In my XAML, I (try to) use binding to set the Enabled property:
<MyButton x:Name="myButtom1" Enabled="{Binding CanEnableButton}"/>
I know the bind between my control and the underlying data model is valid and working as I can bind 'IsEnabled' (a native property of the underlying UserControl) and it works as expected. However, my Enabled property is never set via the above binding. I've put breakpoints on my property set/get and they never get hit at all.
I can only imaging I've missed something relating to binding in my custom control. Can anyone see what?
I've tried implementing INotifyPropertyChanged on my control (and calling the PropertyChanged event from my Enabled setter) ... but that didn't fix it.
[ BTW: In case you are wondering "Why?": I can't intercept changes to the IsEnabled state of the base control, so I decided to implement and use my own version of a Enable/disable property (which I called Enabled) - one where I could plug my own code into the property setter ]
First of all drop the SetEnabled and GetEnabled pair, these only make sense for an attached property which is not what you are doing.
Now your main problem is that you are under the false assumption that the get/set members of your propery get called during binding, they don't.
What you need is to pass a call back method in the property meta data, it's here that you intercept changes and take other actions like so:-
public bool IsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.Register(
"IsEnabled",
typeof(bool),
typeof(MyButton),
new PropertyMetadata(true, OnIsEnabledPropertyChanged));
private static void OnIsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyButton source = d as MyButton;
source.SomeOtherStuff();
}
private void SomeOtherStuff()
{
// Your other stuff here
}
With this in place regardless of how the propery is changed the SomeOtherStuff procedure will execute.
I'd suggest using the IsEnabledChanged event which is part of every Control/UserControl.
That would allow you to hook up to the event and do whatever actions you want to take.
public MainPage()
{
InitializeComponent();
this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(MainPage_IsEnabledChanged);
}
void MainPage_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Do SomeStuff
}
Two way binding does not work on my custom control with the following internals:
public partial class ColorInputControl
{
public ColorInputControl()
{
InitializeComponent();
colorPicker.AddHandler(ColorPicker.SelectedColorChangedEvent,
new RoutedPropertyChangedEventHandler<Color>( SelectedColorChanged));;
colorPicker.AddHandler(ColorPicker.CancelEvent,
new RoutedPropertyChangedEventHandler<Color>(OnCancel));
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register
("SelectedColor", typeof(Color), typeof(ColorInputControl),
new PropertyMetadata(Colors.Transparent, null));
public Color SelectedColor
{
get
{
return (Color)GetValue(SelectedColorProperty);
//return colorPicker.SelectedColor;
}
set
{
SetValue(SelectedColorProperty, value);
colorPicker.SelectedColor = value;
}
}
private void SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs<Color> e)
{
SetValue(SelectedColorProperty, colorPicker.SelectedColor);
}
}
SelectedColor is being bound to a property that fires INotifyPropertyChanged event control when it changes. However, I cannot get two-way binding to work. Changes from the UI are pesisted to the data source. However, changes originating from the data source are not reflected on the UI.
What did I miss? TIA.
Never do any work (updating the color picker) in the SelectColor helpers. Those are convinence wrappers and are not guarranted to be called. (As you can see in your two way binding.) Add a PropertyChangedCallback to your SelectedColorProperty metadata. Do your work in there.
i want to do add custom event handlers to default framework elements using DependencyProperties.
Something like the following:
<Border custom:MyProps.HandleMyEvent="someHandler">...</Border>
Here is the code behind for the control that contains the Border element:
public class MyPage : Page{
public void someHandler(object sender, EventArgs e){
//do something
}
}
Here is rough sample of how i imagine the class that defines the property:
public class MyProps{
public event EventHandler MyInternalHandler;
public static readonly DependencyProperty HandleMyEventProperty = ...
public void SetHandleMyEvent(object sender, EventHandler e){
MyInternalHandler += e;
}
}
The problem is that I don't know/didn't find any hints how to combine DependencyProperties with events/delegates and EventHandlers.
Do you have a clue?
I'm going to assume this has nothing to do with WPF, this is a silverlight question.
First of all you can't simply add an Event to an existing control. After all you are adding attached Properties whereas events are handled differently, they're not properties.
You need to create a new type which has this event then create an attached property of this type.
Here is a basic type that simply has an event:-
public class MyEventer
{
public event EventHandler MyEvent;
// What would call this??
protected void OnMyEvent(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
Now we create an attached property which has MyEventer as its property, I prefer to place these in a separate static class.
public static class MyProps
{
public static MyEventer GetEventer(DependencyObject obj)
{
return (MyEventer)obj.GetValue(EventerProperty );
}
public static void SetEventer(DependencyObject obj, MyEventer value)
{
obj.SetValue(EventerProperty , value);
}
public static readonly DependencyProperty EventerProperty =
DepencencyProperty.RegisterAttached("Eventer", typeof(MyEventer), typeof(MyProps), null)
}
}
Now you attach this to a control like this:-
<Border ...>
<custom:MyProps.Eventer>
<custom:MyEventer MyEvent="someHandler" />
</custom:MyProps.Eventer>
</Border>
If you compile the project before writing this xaml you'll note that Visual Studio will offer you the option for it to create the event handler in the code behind for you.
Of course this still leaves a significant question: How did you intend to cause the event to fire?
How do I expose the ActualWidth property of one of the components of my user control to users?
I have found plenty of examples of how to expose a normal property by creating a new dependency property and binding, but none on how to expose a read-only property like ActualWidth.
What you need is a ReadOnly dependency property. The first thing you need to do is to tap into the change notification of the ActualWidthProperty dependency on the control that you need to expose. You can do that by using the DependencyPropertyDescriptor like this:
// Need to tap into change notification of the FrameworkElement.ActualWidthProperty
Public MyUserControl()
{
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty
(FrameworkElement.ActualWidthProperty, typeof(FrameworkElement));
descriptor.AddValueChanged(this.MyElement, new EventHandler
OnActualWidthChanged);
}
// Dependency Property Declaration
private static DependencyPropertyKey ElementActualWidthPropertyKey =
DependencyProperty.RegisterReadOnly("ElementActualWidth", typeof(double),
new PropertyMetadata());
public static DependencyProperty ElementActualWidthProperty =
ElementActualWidthPropertyKey.DependencyProperty;
public double ElementActualWidth
{
get{return (double)GetValue(ElementActualWidthProperty); }
}
private void SetActualWidth(double value)
{
SetValue(ElementActualWidthPropertyKey, value);
}
// Dependency Property Callback
// Called when this.MyElement.ActualWidth is changed
private void OnActualWidthChanged(object sender, Eventargs e)
{
this.SetActualWidth(this.MyElement.ActualWidth);
}
ActualWidth is a public readonly property (coming from FrameworkElement) and is exposed by default. What is the case that you are trying to achieve?