GetBindingExpression returns null in the Loaded event - c#

I'm trying to call the GetBindingExpression method in the Loaded event, but it always returns null.
Is this expected behavior, or am I doing something wrong? If it is expected, after what event do binding expressions become available?
I just create custom control
public partial class LookUp : ComboBox
public static readonly DependencyProperty LookUpItemsSourceProperty =
DependencyProperty.Register("LookUpItemsSource"
, typeof(IEnumerable)
, typeof(LookUp)
, new PropertyMetadata(OnItemsSourcePropertyChanged));
public IEnumerable LookUpItemsSource
{
get
{
return this.GetValue(LookUpItemsSourceProperty) as IEnumerable;
}
set
{
this.SetValue(LookUpItemsSourceProperty, value);
}
}
And use this control in xaml
<Controls:LookUp Name="cb1" LookUpItemsSource="{x:Static Helper:DataManager.CycleLookUpData}"
Now i want to get binding expression when control initialized that method return null:
cb1.GetBindingExpression(LookUp.LookUpItemsSourceProperty)

x:static will set the value of key, it is not binding expression. You will have to use,
{Binding CycleLookUpData, source={x:static Helper:DataManager}}

If you don't use {Binding ... in XAML you can't use GetBindingExpression() method. In your case you set value instead of binding. You need to use cb1.GetValue(LookUp.LookUpItemsSourceProperty) instead.

Related

How to declare a DependencyProperty that could also be set without binding

I know that I can declare a new DependencyProperty as such:
public String PropertyPath
{
get { return (String)GetValue(PropertyPathProperty); }
set { SetValue(PropertyPathProperty, value); }
}
public static readonly DependencyProperty PropertyPathProperty =
DependencyProperty.Register(nameof(PropertyPath), typeof(String),
typeof(NotEmptyStringTextBox),
new FrameworkPropertyMetadata(PropertyPath_PropertyChanged));
protected static void PropertyPath_PropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var ctl = d as NotEmptyStringTextBox;
var binding = new Binding(ctl.PropertyPath)
{
ValidationRules = { new NotEmptyStringRule() },
// Optional. With this, the bound property will be updated and validation
// will be applied on every keystroke.
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
ctl.StringBox.SetBinding(TextBox.TextProperty, binding);
}
But then the UserControl can only recieve a string with the name of the property to bind, and bind to it.
What I would like is to be able to have the same kind of comportment as "classical" properties, which you can either bind to, or give a static value.
My usage would be a boolean that modifies the display state of a UserControl, either statically with a fixed value or dynamically with a binding, all depending on the use case.
Maybe the way I made my dependency Property in the first place is incorrect, but here is how I can use it:
<inputboxes:NotEmptyStringTextBox
Grid.Column="1"
PropertyPath="Name"/>
This will bind the "Name" property from the DataContext, but I can't use it with a raw string, as it will make a BindingExpression error: "property not found"
EDIT:
I now have tried the following:
public bool Test
{
get { return (bool)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register(nameof(Test), typeof(bool),
typeof(DamageTemplateListEditableUserControl));
I declared this new property, but I still cannot bind anything to it, only raw values are accepted
You shouldn't create a new binding in the callback. In fact, you don't need any callback at all.
Rename the dependency property to something better like "Text" and just bind the Text property of StringBox to the current value of your dependency property like this:
<TextBox x:Name="StringBox"
Text="{Binding Text, RelativeSource={RelativeSource AncestorType=local:NotEmptyStringTextBox},
UpdateSourceTrigger=PropertyChanged}" />
You can then set or bind the dependency property as usual.
If you really want a "PropertyPath" property, it shouldn't be a dependency property that you can bind something to but rather a simple CLR property that you can set to a string that represents a name of a property to bind to.
This is for example how the DisplayMemberPath property of an ItemsControl is implemented.

ObservableCollection is Null when I try to add TextBlocks through XAML to a custom control

In my WPF Project, i am trying to create an ObservalbeCollection dependency property inside a custom control. I am not sure if i am doing it the right way but i am using the below code :
public static readonly DependencyProperty MenuOptionsDependency = DependencyProperty.Register("MenuOptions", typeof(ObservableCollection<TextBlock>), typeof(DropDownButton));
public ObservableCollection<TextBlock> MenuOptions
{
get
{
return (ObservableCollection<TextBlock>)GetValue(MenuOptionsDependency);
}
set
{
SetValue(MenuOptionsDependency, value);
}
}
The problem arises when i try to add TextBlocks through XAML to this control as follows :
<local:CustomControl1 x:Name="cmm">
<local:CustomControl1.MenuOptions>
<TextBlock/>
</local:CustomControl1.MenuOptions>
</local:CustomControl1>
This throws a design time error saying :
Collection 'CustomControl1'.'MenuOptions' is null
After going through the accepted answer on this SO post, i understand ObservableCollection, being a reference type, will be null as it will be default value. I read through the answer but i am still unclear/unsure on how to implement the solution in my situation.
I need to be able to add objects to the collection through XAML(and also through C#). I would really appreciate if someone points out where i am missing/what i am doing wrong.
You must never set the default value of a collection type dependency property to something else than null. When you assign a non-null default value by (static) property metadata, all instances of your control will use the same collection object.
Instead, set a default value in the control's constructor by SetCurrentValue:
public DropDownButton()
{
SetCurrentValue(MenuOptionsDependency, new ObservableCollection<TextBlock>());
}
Besides that, there is a strict naming convention, according to which the dependency property identifier field must be named as the property with a Property suffix. So your MenuOptionsDependency should actually be named MenuOptionsProperty.
It's also unclear whether the property type really needs to be ObservableCollection. You don't seem to register a CollectionChanged event handler anywhere, which indicates that your control is not supposed to react on such changes.
Consider a property declaration like this:
public DropDownButton()
{
SetCurrentValue(MenuOptionsProperty, new List<TextBlock>());
}
public static readonly DependencyProperty MenuOptionsProperty =
DependencyProperty.Register(
nameof(MenuOptions), typeof(IEnumerable<TextBlock>), typeof(DropDownButton));
public IEnumerable<TextBlock> MenuOptions
{
get { return (IEnumerable<TextBlock>)GetValue(MenuOptionsProperty); }
set { SetValue(MenuOptionsProperty, value); }
}

Dotvvm run-time binding to 'object' type

i have this class
public class Property{
public string Name {get;set;}
public object Value {get;set;}
}
i want to create list of the above class List<Property> and dynamically add Mark Up Controls Code only
, so as their website they have an example HERE and what i did to that example is adding a public property of type Property to the TextBoxWithLabel class and changed the setter of the above example for binding as follows:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set {
SetValue(TextProperty, value);
Property.Value = value;
}
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
when i run the app and type something in the input field, the Value property of Type Property still null and here is where i'm stuck
i tried also to debug setter and it turns out it does not reach there so there is problem with run-time binding, which is 'as their example' this line of code
textBox.SetBinding(TextBox.TextProperty, GetValueBinding(TextProperty));
any help will appreciated :)
EDIT:
for more clarification,i have page called MainAppPage
and Markup Control with code behind called ContentControl
simply , MainAppPage passes List<Property> to ContentControl using this in MainAppPage
<controls:ContentControl Instance="{value: ClassObject}"/> then ContentControl start iterating through List<Property> and creating InputField's that derive from HtmlGenericControl
InputField's rendering like a charm in ContentControl
only thing is not working is binding , so again, how to bind Property.Value to InputField.Text so any changes happens in UI from user reflects on Property.Value after the InputField gets unfocused like any other MVVM pattern ?
DotVVM does not assign to the property usning the setter, is sets the underlying property store in DotvvmBindableObject instead. It's very simmilar what WPF does with their DependencyProperty, it's needed to represent the data bindings. You can actually completely omit the C# property declaration, declaring the field TextProperty and calling the DotvvmProperty.Register is enough to declare a property for dotvvm.
Other "problem" is that the controls do not store any data, everything has to be persisted in the view model. You can only use the control properties to data-bind a view model property. I think we are running here into a XY problem, I can only tell why your code does not work, but I have no idea what are actually trying to do...
Anyway, if you just want to "bind" your control to a view model property, have a look at https://www.dotvvm.com/docs/tutorials/control-development-markup-controls-with-code/2.0. You can declare the property like that:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
Use it in the markup of you control
#baseType FullName.Of.YourControl
{{value: _control.Text}}
And use the control in your page (or other control)
<cc:YourControl Text="{value: _this.Property.Value}" />

Custom DependencyProperty set from DataTemplate

I'm working with a custom control that has several user-defined dependency properties. I'm running into the same issue described in this question.
My control is setting the default value of a custom dependency property in its constructor. When I use the control in a DataTemplate, the value set in the constructor is always used, even if I try to set it in XAML.
The answer to the linked question explains that the value set in the C# code has a higher priority, and a better approach would be to specify the default value in the dependency property's metadata.
In my case, I can't specify a default because the dependency property doesn't have a single default value that applies in all cases. The default values depend on another property, so I must look them up when the control is created and not when the property is registered.
Here's some code to help illustrate my problem:
public partial class MyControl : UserControl
{
public static readonly DependencyProperty MyProperty =
DependencyProperty.Register(
"MyProperty",
typeof(int),
typeof(MyControl),
new FrameworkPropertyMetadata(
int.MinValue,
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback("OnMyPropertyChanged")));
public MyControl() : base()
{
InitializeComponent();
this.MyProperty = GetDefaultPropertyValue();
}
public int MyProperty
{
get { return (int)GetValue(MyProperty); }
set { SetValue(MyProperty, value); }
}
private int GetDefaultPropertyValue()
{
// look up the appropriate default based on some other criteria
return 42;
// (in reality, the default value for "MyProperty"
// depends on the value of a "Mode" custom DependencyProperty.
// this is just hard coded for testing)
}
}
The XAML usage looks something like this:
<!-- View displays 4 (desired) -->
<local:MyControl MyProperty="4" />
<!-- View displays default of 42 (desired) -->
<local:MyControl />
<!-- View displays default of 42 (wanted 4) -->
<DataTemplate x:Key="MyTemplate">
<local:MyControl MyProperty="4"/>
</DataTemplate>
To summarize:
The desired behavior is that the value from XAML is used first. If the value is not specified in the XAML, then I would like to fallback to the default value set in the control's constructor.
If I just include the control directly in a view, I get the expected behavior. If the control is used inside a DataTemplate, then I always get the default set in the constructor (even when the data template explicitly sets another value).
Is there any other way to specify the default value when the control is used in a template? The only option I can think of is to break the control up into several separate but similar controls, each of which uses a default value that is registered with the dependency property (which removes the need to have the default set based on the a Mode property).
Setting the default value in OnApplyTemplate while adding a small check should solve this:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Only set the default value if no value is set.
if (MyProperty == (int)MyPropertyProperty.DefaultMetadata.DefaultValue)
{
this.MyProperty = GetDefaultPropertyValue();
}
}
Please note that although this will work, it's not ideal since setting the property's value by code will essentially clear any data bindings for this property. For example, the following binding will no longer work once you call MyProperty = 42 in code:
<local:MyControl MyProperty="{Binding SomeProperty}" />
It should be possible to set the value while maintaining any bindings by using SetCurrentValue(MyPropertyProperty, GetDefaultPropertyValue()); to modify the property instead of MyProperty = GetDefaultPropertyValue(), but I'm not sure I like that too much either.
A better solution
What I would do is introduce a new read-only property in addition to the existing one, which will act as a calculated property. For example:
private static readonly DependencyPropertyKey MyCalculatedPropertyPropertyKey =
DependencyProperty.RegisterReadOnly("MyCalculatedProperty", typeof(int), typeof(MyControl),
new PropertyMetadata(int.MinValue));
public static readonly DependencyProperty MyCalculatedPropertyProperty = MyCalculatedPropertyPropertyKey.DependencyProperty;
public int MyCalculatedProperty
{
get { return (int)GetValue(MyCalculatedPropertyProperty); }
private set { SetValue(MyCalculatedPropertyPropertyKey, value); }
}
private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyControl)d).MyCalculatedProperty = (int)e.NewValue;
}
public MyControl()
: base()
{
InitializeComponent();
MyCalculatedProperty = GetDefaultPropertyValue();
}

ListView.View is null in dependency property

I would like to disable column reording in a control we have created that is derived from ListView. This control is called a SortableListView. I thought a dependency property would be the best way to implement this, but the ((SortableListVIew)source).View is returning null. Here is the code:
public class SortableListView : ListView
{
// ...lots of other properties here
public static readonly DependencyProperty AllowsColumnReorderProperty =
DependencyProperty.Register(
"AllowsColumnReorder",
typeof(bool),
typeof(SortableListView),
new UIPropertyMetadata(true, AllowsColumnReorderPropertyChanged));
public bool AllowsColumnReorder
{
get
{
return (bool)this.GetValue(AllowsColumnReorderProperty);
}
set
{
this.SetValue(AllowsColumnReorderProperty, value);
}
}
private static void AllowsColumnReorderPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
ViewBase vb = ((SortableListView)source).View;
if (vb != null)
{
((GridView)vb).AllowsColumnReorder = (bool)e.NewValue;
}
}
And the XAML:
<TableControls:SortableListView x:Name="QueueViewTable" Margin="0,0,0,0"
Style="{StaticResource ListViewStyle}"
ItemsSource="{Binding Path=QueueList}"
ItemContainerStyle="{StaticResource alternatingListViewItemStyle}"
AlternationCount="2"
SelectionMode="Single"
SortEnabled="False"
AllowsColumnReorder="false">
The trouble is that vb is always null so the method fails to set AllowsColumnReoder. I am quite sure that the cast is valid because the code originally looked like this in OnInitialized:
((GridView)this.View).AllowsColumnReorder = false;
...but I need to set the AllowsColumnReorder on a particular instance of the view so this code is no good.
Can anyone tell me what I'm doing wrong? Or is there a better way to set this property?
The View property of ListView is itself a dependency property that could change. It appears to not be set yet when you're setting your property?
You may have to override the View property in your sortable list view, so you can add a property change listener, and then apply your sort property when the view itself gets set?
in wpf, you can override a dependency property declared in a parent class, shown here: http://msdn.microsoft.com/en-us/library/ms754209.aspx
you'd override the metadata for the View property, and in the PropertyMetadata param you set there, you can pass a function like you are above for AllowsColumnReorderPropertyChanged
in that handler, you'd check to see if the new view is a gridview, and then set your property.
that way, the either order of AllowsColumnReorder or View getting set will properly set your property.

Categories

Resources