Below is my ParamModel class which is inherited from DependencyObject
public class ParamsModel : DependencyObject
{
public object MyProperty
{
get { return (object)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(object), typeof(ParamsModel), new PropertyMetadata(null));
public ParamsModel()
{
}
}
I have referred this class in my XAML like below
<TextBox Text="{Binding DataContext.MyName,ElementName=pageRoot}" />
<ListBox ItemsSource="{Binding MyItems}"
Width="500"
Height="500">
<local:ParamsModel MyProperty="{Binding DataContext.MyName,ElementName=pageRoot}" />
</ListBox>
I have put breakpoint at MyProperty setter which is not hitting at runtime, but the same class Constructor is hitting. Could anyone please help me on this
It is because the binding mechanism don't call the CLR property of dependency property. It calls GetValue/SetValue directly.
I have been trying to bind to a DependencyObject view model in a Store App and it just wouldn't work unless I put the PropertyMetaData parameter to null instead of new PropertyMetadata(null).
Related
I have a static DependencProperty IsControlVisibleProperty in MyControl.xaml.cs. And its value is changed inside that same class. And I want to listen to this property in another control Visibility property whenever IsControlVisibleProperty value is changed.
MyControl.xaml.cs :
public partial class MyControl : UserControl
{
public static DependencyProperty IsControlVisibleProperty = DependencyProperty.Register(nameof(IsControlVisible), typeof(bool), typeof(MyControl));
public bool IsControlVisible
{
get{ return (bool)GetValue(IsControlVisibleProperty); }
set { SetValue(IsControlVisibleProperty, value); }
}
// In a function I am updating the dependency property
private void UpdateProp(bool isVisible)
{
this.SetValue(UserControl1.IsControlVisible, isVisible);
}
Now I want to use IsControlVisibleProperty value in another xaml file
SampleControl.xaml:
<UserControl x:Class="SampleControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="http://schemas.microsoft.com/netfx/2007/xaml/presentation"
Height="300" Width="300">
<UserControl.Resources>
<converter:BooleanToVisibilityConverter x:Key="boolToVisConverter"/>
</UserControl.Resources>
<Grid>
<local:MyControl/>
<TextBlock Name="ErrorMessage" Text="Failed to run" Visibility="{Binding ElementName="ErrorMessage" , Path=(local:MyControl.IsControlVisible), Converter={StaticResource boolToVisConverter}, Mode=TwoWay}"/>
</Grid>
So my TextBlock (ErrorMessage) is not binding the IsVisibleProperty from MyControl.xaml.cs. I want IsVisibleProperty to be always binded with TextBlock(whereever the property changes it should alse change its visibility ) not just one time on contruction . Unfortunately I am unable to achieve this . Is there any other way to do so ?
The declaration of a DependencyProperty is always static but your IsControlVisible property is not static. In fact, you can't declare a static dependency property because the GetValue and SetValue method are not static.
What you should is to define an attached dependency property in a static class:
public static class MyProperties
{
public static readonly DependencyProperty IsControlVisibleProperty = DependencyProperty.RegisterAttached(
"IsControlVisible",
typeof(bool),
typeof(MyProperties));
public static void SetIsControlVisible(UIElement element, Boolean value)
{
element.SetValue(IsControlVisibleProperty, value);
}
public static bool GetIsControlVisible(UIElement element)
{
return (bool)element.GetValue(IsControlVisibleProperty);
}
}
You can then set this property on any UIElement (or whatever the type is in your get and set accessors) using the accessors like this:
MyProperties.SetIsControlVisible(this, true); //this = the UserControl
You bind to the attached property of the parent UserControl like this:
<TextBlock Name="ErrorMessage" Text="Failed to run"
Visibility="{Binding Path=(local:MyProperties.IsControlVisible),
Converter={StaticResource boolToVisConverter},
RelativeSource={RelativeSource AncestorType=UserControl}}" />
I defined a custom loading spinner UserControl in a WPF UserContol library.
It has one dependency property:
public string SpinnerSourcePath { get => _spinner.Source.ToString(); set => _spinner.Source = (ImageSource)new ImageSourceConverter().ConvertFromString(value); }
public static readonly DependencyProperty SpinnerSourcePathProperty =
DependencyProperty.Register(nameof(SpinnerSourcePath), typeof(string), typeof(Spinner));
where _spinner is the Image.
(I tried it directly with ImageSource class but no dice)
The xaml looks like this:
<Image x:Name="_spinner" RenderTransformOrigin="0.5 0.5">
<SomeStyleToMakeItRotate.../>
</Image>
and I use it by defining it like:
<c:Spinner SpinnerSourcePath="/Test;component/_Resources/loading.png"/>
(The project name is Test, the Spinner control resides in a different project), nothing is displayed.
However, if I add the Source property directly in the Spinner definition:
<Image x:Name="_spinner" Source="/Test;component/_Resources/loading.png" RenderTransformOrigin="0.5 0.5">
<SomeStyleToMakeItRotate.../>
</Image>
it shows correctly...
This leads me to believe that the dependency property is wrong, but how ?
E1:
After trying to do the same steps on a different control it stopped working again.
This time I have a DP:
public static readonly DependencyProperty ValidationFunctionProperty =
DependencyProperty.Register(nameof(ValidationFunction), typeof(Func<string, bool>), typeof(ValidatedTextBox), new PropertyMetadata(OnAssignValidation));
public Func<string, bool> ValidationFunction {
get => (Func<string, bool>)GetValue(ValidationFunctionProperty);
set => SetValue(ValidationFunctionProperty, value);
}
private static void OnAssignValidation(DependencyObject d, DependencyPropertyChangedEventArgs e) {
Debugger.Break();
}
Control usage:
<c:ValidatedTextBox x:Name="valid"
Text="Test"
ValidationFunction="{Binding Validation, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource test}}"/>
The converter is just a Debugger.Break() and return original
And finally the RelativeSource control is my MainWindow
public MainWindow() {
InitializeComponent();
}
public Func<string,bool> Validation => (s) => true;
(There is a problem with the Text DP as well, but I think I can solve that one on my own)
E2
Ok Pro problem was the RelativePath pointing to UserControl but it was placed in a Window
Your dependency property declaration is wrong, because the get/set methods of the CLR property wrapper must call the GetValue and SetValue methods of the DependencyObject base class (and nothing else).
Besides that, the property should also use ImageSource as its type:
public static readonly DependencyProperty SpinnerSourceProperty =
DependencyProperty.Register(
nameof(SpinnerSource), typeof(ImageSource), typeof(Spinner));
public ImageSource SpinnerSource
{
get { return (ImageSource)GetValue(SpinnerSourceProperty); }
set { SetValue(SpinnerSourceProperty, value); }
}
The Image element in the UserControl's XAML would use the property like this:
<Image Source="{Binding SpinnerSource,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
Got a user control defined as follows:
public partial class EveBlueprintsGrid : UserControl
{
public static DependencyProperty BlueprintListProperty = DependencyProperty.Register(
"EveBlueprints",
typeof(EveBlueprintList),
typeof(EveBlueprintsGrid));
public EveBlueprintList EveBlueprints
{
get { return (EveBlueprintList)GetValue(BlueprintListProperty); }
set { SetValue(BlueprintListProperty, value); }
}
with XAML:
<DataGrid x:Name="BlueprintsDataGrid" Grid.Row="0" IsReadOnly="True" ItemsSource="{Binding Path=EveBlueprints.Blueprints, ElementName=uc}" AutoGenerateColumns="False">
With the calling XAML:
<Blueprints:EveBlueprintsGrid Grid.Column="1" EveBlueprints="{Binding Blueprints}"/>
I am getting the "A Binding can only be set on a DependencyProperty of a DependencyObject." When I look at the calling XAML.
EveBlueprints contains property Blueprints which is an ObservableCollection.
What's interesting is I have an almost exact copy of this on another object which isn't giving me the errors I'm seeing.
What am I missing here?
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}}}" />
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.