WPF bindable dependency property is not working - c#

I bind as follows:
views:SciChartUserControl Name="SciChartUserControl" Quotes="{Binding QuoteCollection}"></views:SciChartUserControl>
I know for sure that QuoteCollection updates because a grid also binds to it and I see it updated.I want to be notified in the code-behind of my SciChartUserControl view but QuotesPropertyChanged is never invoked. This is driving me crazy, I have tried different ways for hours...something obvious I am overlooking?
public partial class SciChartUserControl : UserControl
{
private SciChartControlViewModel _viewModel;
public SciChartUserControl()
{
//Set ViewModel Datacontext
_viewModel = new SciChartControlViewModel();
DataContext = _viewModel;
InitializeComponent();
}
public static DependencyProperty QuotesProperty = DependencyProperty.Register("Quotes", typeof(List<Quote>), typeof(SciChartUserControl), new PropertyMetadata(QuotesPropertyChanged));
public List<Quote> Quotes
{
get
{
return (List<Quote>)GetValue(QuotesProperty);
}
set
{
SetValue(QuotesProperty, value);
}
}
private static void QuotesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
var quotes = (List<Quote>) e.NewValue;
}
}
EDIT: I added part of the view that hosts the SciChartUserControl.
<dxdo:LayoutPanel Caption="Time Series Visualization">
<views:SciChartUserControl Name="SciChartUserControl" Quotes="{Binding QuoteCollection}"></views:SciChartUserControl>
</dxdo:LayoutPanel>
<dxdo:LayoutPanel Caption="Time Series Data">
<dxg:GridControl Name="SampleDataGridControl" ItemsSource="{Binding QuoteCollection}" AutoGenerateColumns="AddNew" EnableSmartColumnsGeneration="True" AutoGeneratedColumns="SampleDataGridControl_OnAutoGeneratedColumns">
<dxg:GridControl.View>
<dxg:TableView AllowEditing="False" AutoWidth="True" BestFitArea="All" AllowBestFit="True" ShowGroupPanel="True" ShowSearchPanelMode="Always"/>
</dxg:GridControl.View>
</dxg:GridControl>
</dxdo:LayoutPanel>

Try using another constructor for the PropertyMetadata class:
public static DependencyProperty QuotesProperty = DependencyProperty.Register("Quotes",
typeof(List<Quote>), typeof(SciChartUserControl),
new PropertyMetadata(someDefaultvalue, QuotesPropertyChanged));
It could be that the single parameter constructor that takes a PropertyChangedCallback object that you are using is getting mixed up with the one that takes a single object parameter.

try this...
in the dependency property declaration change PropertyMetadata to the following..
new PropertyMetadata(null, new PropertyChangedCallback(QuotesPropertyChanged))

I believe this is because you have set your DataContext in your code behind, I ran into the same issue when setting it in XAML? It seems as though a DependencyProperty is being bound relative to the DataContext of the UserControl. UserControl's DependencyProperty is null when UserControl has a DataContext
<views:SciChartUserControl Name="SciChartUserControl"
Quotes="{Binding DataContext.QuoteCollection, RelativeSource={RelativeSource AncestorType={x:Type dxdo:LayoutPanel}}}" />

Related

How to use a user control dependency property when setting its data context?

I've made a user control which contains a command, to be called in response to a certain event. This command is a dependency property. I want to use it in the main window like this:
<local:myUserControl Command="{Binding someCommand}"/>
The "myCommand" is the dependency property I created for this user control. And I bind it to a command of the view model of the main window ("someCommand").
The problem is that I am setting the datacontext of my usercontrol (I have a view model for it), and it seems to reset the "Command" to null… Here is the code-behind of my view model:
public partial class myUserControl : UserControl, ICommandSource
{
public myUserControl()
{
this.DataContext = new myViewModel();
InitializeComponent();
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(myUserControl), new PropertyMetadata(null));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(myUserControl), new PropertyMetadata(0));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(myUserControl), new PropertyMetadata(null));
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Command.Execute(this.CommandParameter);
}
}
The code of my user control could be the Following:
<UserControl x:Class="myApp.myUserControl"
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"
xmlns:local="clr-namespace:myApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock MouseUp="TextBlock_MouseUp">
</TextBlock>
</Grid>
</UserControl>
(I know that this element seems a bit silly (or useless), but I have simplified it to test what didn't worked and also in order to ask a rather simple question).
I have discovered that, if I comment the "this.DataContext = new myViewModel();" line, the binding to the command works perfectly. And when I uncomment this line and put a breakpoint in the "TextBlock_MouseUp", the "Command" property is equal to null...
Would there be a way to resolve this problem? I have some complicated code in my view model (so I'm quite forced to keep this line "this.DataContext = new myViewModel();"), and I am not sure I could find another solution than having a "Command" dependency property in my user control…
To be sure I give a maximum of informations, I have the following code in the view model of my main window:
public ICommand someCommand { get; set; }
//Constructor
public MainWindowViewModel()
{
this.someCommand = new RelayCommand((obj) => { return true; },
(obj) =>
{
//I put a breakpoint here
int dummy = 0;
});
}
(The RelayCommand class is a standard RelayCommand class, with a "Predicate" CanExecute and an "Action Execute).
I hope this question is not a duplicate… I have found several similar question, but they did not seem to answer mine...
I'm really sorry for this question which was in fact a bit silly. I hadn't understand very well what happens during a binding. I thought that this code line in the MainWindow…
<local:myUserControl Command="{Binding someCommand}"/>
…would have made an attempt to bind the UserControl's "Command" property to the "someCommand" of the datacontext of the MainWindow. In fact, as #elgonzo pointed out, the binding looks up in the UserControl's datacontext for the "someCommand" property (and not in the MainWindow's datacontext!!). Therefore, setting the UserControl's datacontext with this line…
this.DataContext = new myViewModel();
...was preventing the binding to be correctly done (since it looks for the "someCommand" property of the UserControl's datacontext, which is now "myViewModel", which does not contain "someCommand"...).
To fix this, I had to change the binding like this:
<local:myUserControl Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.someCommand}"/>
I've found this solution here: https://stackoverflow.com/a/1127964/11609068.
Maybe it is not the best way to do it (the "Path= DataContext. someCommand" make me think this, it doesn't seem very elegant), but it works. Another way to do it is to name the MainWindow (x:Name="someName"), so that the binding is a bit simpler:
<local:myUserControl Command="{Binding ElementName=someName, Path=DataContext.someCommand}"/>
Again, sorry and many thanks to #elgonzo.

Bind XAML element to UserControl using ElementName

I have the following scenario and hierarchy of XAML elements in my page:
<Page> ....
<StackPanel> ...
<Grid> ....
<StackPanel>
<uc:MyUserControl
ReferencedButton={Binding ElementName=RightButton} />
<Button x:Name="RightButton" Click="{x:Bind ViewModel.OpenFlyout}" Content="Clickme" />
</StackPanel>
......
Then the code behind from 'MyUserControl'
public UIElement ReferencedButton
{
get { return (UIElement)GetValue(ReferencedButtonProperty); }
set { SetValue(ReferencedButtonProperty, value); }
}
public static readonly DependencyProperty ReferencedButtonProperty =
DependencyProperty.Register(nameof(ReferencedButton), typeof(UIElement), typeof(MyUserControl), null);
So far so good, however I was expecting that in my code behind, the 'ReferencedButton' property would be filled with a reference to the 'RightButton' button. However it always returns null.
I even tried:
{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, ElementName=RightButton}
I know it is possible to bind the element, because I got the example from a DevExpress component, but still without any success.
I am following the suggestions/rules from the following docs:
Binding ElementName
XAML Namescopes
p.s: I know that I can pass the reference to the button in my code behind however I would like to do this through XAML itself.
It turns out that I needed to use a PropertyChangedCallback to make it work. So the solution is as below:
public static readonly DependencyProperty ReferencedButtonProperty=
DependencyProperty.Register(nameof(ReferencedButton),
typeof(UIElement),
typeof(MyUserControl),
new PropertyMetadata(default(UIElement),
new PropertyChangedCallback(PlacementCallBack)));
and in the code behind of my control I can access and set the value by implementing the PlacementCallBack like this:
public static void PlacementCallBack(object sender, DependencyPropertyChangedEventArgs e)
{
var myuserControl = sender as MyUserControl;
myuserControl.ReferencedButton = e.NewValue as UIElement;
}
The object DependencyPropertyChangedEventArgs contains two propeties NewValue and OldValue, they hold the old and new values of the previous object set.

XAML binding not working on dependency property?

I am trying (and failing) to do data binding on a dependency property in xaml. It works just fine when I use code behind, but not in xaml.
The user control is simply a TextBlock that bind to the dependency property:
<UserControl x:Class="WpfTest.MyControl" [...]>
<TextBlock Text="{Binding Test}" />
</UserControl>
And the dependency property is a simple string:
public static readonly DependencyProperty TestProperty
= DependencyProperty.Register("Test", typeof(string), typeof(MyControl), new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
I have a regular property with the usual implementation of INotifyPropertyChanged in the main window.
private string _myText = "default";
public string MyText
{
get { return _myText; }
set { _myText = value; NotifyPropertyChanged(); }
}
So far so good. If I bind this property to a TextBlock on the main window everything works just fine. The text update properly if the MyText changes and all is well in the world.
<TextBlock Text="{Binding MyText}" />
However, if I do the same thing on my user control, nothing happens.
<local:MyControl x:Name="TheControl" Test="{Binding MyText}" />
And now the fun part is that if I do the very same binding in code behind it works!
TheControl.SetBinding(MyControl.TestProperty, new Binding
{
Source = DataContext,
Path = new PropertyPath("MyText"),
Mode = BindingMode.TwoWay
});
Why is it not working in xaml?
The dependency property declaration must look like this:
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register(
nameof(Test),
typeof(string),
typeof(MyControl),
new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
The binding in the UserControl's XAML must set the control instance as the source object, e.g. by setting the Bindings's RelativeSource property:
<UserControl x:Class="WpfTest.MyControl" ...>
<TextBlock Text="{Binding Test,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>
Also very important, never set the DataContext of a UserControl in its constructor. I'm sure there is something like
DataContext = this;
Remove it, as it effectively prevents inheriting a DataContext from the UserConrol's parent.
By setting Source = DataContext in the Binding in code behind you are explicitly setting a binding source, while in
<local:MyControl Test="{Binding MyText}" />
the binding source implicitly is the current DataContext. However, that DataContext has been set by the assignment in the UserControl's constructor to the UserControl itself, and is not the inherited DataContext (i.e. the view model instance) from the window.

Make dependency object properties bindable as a static resource?

How to make an array of dependency object properties bindable for later binding as a static resource?
The code I have now, it seems that my DependencyObject bypasses the dependency property system...
I have the following class:
public class ValueMarker : DependencyObject
{
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register("Brush", typeof(Brush), typeof(ValueMarker), new FrameworkPropertyMetadata(Brushes.Aqua));
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(ValueMarker), new FrameworkPropertyMetadata(0d));
public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register("Offset", typeof(double), typeof(ValueMarker), new FrameworkPropertyMetadata(0d));
public Brush Brush
{
get { return (Brush)GetValue(BrushProperty); }
set { SetValue(BrushProperty, value); }
}
public double Offset
{
get { return (double)GetValue(OffsetProperty); }
set { SetValue(OffsetProperty, value); }
}
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
}
In the XAML, I create a resource array of these with some bindings like so:
<x:Array Type="my:ValueMarker" x:Key="plainMarks">
<my:ValueMarker Brush="Red" Offset="-5" Value="{Binding Path=...}" />
<my:ValueMarker Brush="Orange" Offset="-5" Value="{Binding Path=...}"/>
<my:ValueMarker Brush="Orange" Offset="-5" Value="{Binding Path=...}"/>
<my:ValueMarker Brush="Red" Offset="-5" Value="{Binding Path=...}" />
</x:Array>
While debugging the bindings, I've noticed that should I remove the setter for the DP, the XAML would display an error saying the property is missing. It was my understanding that XAML uses DP system to assign value thus enabling binding. In this case, if the XAML expect a 'normal' property, binding is impossible. Anyone can enlighten me on how can I make it work?
The reason you cannot bind your ValueMarkers here is because:
1.They are not in the VisualTree of your window/usercontrol.
2.They are not object of Type that can inherit DataContext even if they are not part of Visual Tree.
So in order to make your ValueMarkers bind to the properties in the View DataContext, first of all you will have to derive them from Freezable class like below:
public class ValueMarker : Freezable
{
//All your Dependency Properties comes here//
protected override Freezable CreateInstanceCore()
{
return new ValueMarker();
}
}
After doing this you can simply bind your object like below:
<my:ValueMarker x:Key="vm1" Brush="Orange" Offset="-5" Value="{Binding Path=Text1}"/>
Here Text1 is property in Windows/usercontrols DataContext
Then you can use this resource as:
<TextBox Text="{Binding Value, Source={StaticResource vm1}, StringFormat=F2}"/>
Similarly you can create resource for other ValueMarkers to use them in binding.
You will not be able to bind by creating the x:Array as simply x:Array not lies in visualtree and does not inherit DataContext hence its elements also have no access to it.
If you still want to use the collection whose element should support binding, then you will need to create your own collection class that should inherit Freezable and exposes DependancyProperty to capture the DataContext and set it on child elements also.

Silverlight UserControl Binding

I have a simple User Control
Xaml:
<UserControl x:Class="GraemeGorman_Controls.Navigation.NavigationItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border x:Name="borderLayoutRoot">
<TextBlock x:Name="textBlockCaption" Text="{Binding Caption}" />
</Border>
</UserControl>
Cs:
namespace GraemeGorman_Controls.Navigation
{
public partial class NavigationItem : UserControl
{
public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register(
"Caption",
typeof (string),
typeof (NavigationItem),
new PropertyMetadata(new PropertyChangedCallback(OnCaptionChanged)));
public string Caption
{
get {return (string)GetValue(CaptionProperty);}
set {SetValue(CaptionProperty, value);}
}
public NavigationItem()
{
InitializeComponent();
}
private static void OnCaptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//null
}
}
}
What my issue is, when I create an instance of the control the caption never shows - now i have tested the property in the OnCaptionChanged function using e.NewValue and it is the correct value. What is wrong with my binding?
If I write in the code behind caption property for set
textBlockCaption.Text = value;
It appears fine...
Any help appreciated
Thanks
Graeme
From the code-behind it looks like you are missing one line of code.
Try to add DataContext = this; in your constructor. This has worked for me in the past.
How are you creating the instance of the NavigationItem control?
you'll need to do something like:
<Page ...
xmlns:gg="clr-namespace:GraemeGorman_Controls.Navigation">
<gg:NavigationItem Caption="FooBar" />
or even
<gg:NavigationItem Caption="{Binding Path=TheCaption}" />
where TheCaption is a property of your Page's DataContext (eg your ViewModel)
Hope This Helps :)

Categories