Setter of bindable property never fires - c#

I created a custom renderer for a Label. Now I wanted to manipulate with its focus state, so I created bindable property and event. It works fine when I change the bindable property from custom renderer like so: Element.IsFocused = true;
But when I change it from the view model, it affects XAML view, but for some reasons doesn't call the setter for this property. Here is the code:
In custom class:
public new static readonly BindableProperty IsFocusedProperty =
BindableProperty.Create(nameof(IsFocused), typeof(bool), typeof(FloatingEntry), false);
public new bool IsFocused
{
get { return (bool)GetValue(IsFocusedProperty); }
set
{
SetValue(IsFocusedProperty, value);
if (value) Focus();
}
}
In XAML:
IsFocused="{Binding PasswordEntryIsFocused}"
In View Model:
private bool _passwordEntryIsFocused;
public bool PasswordEntryIsFocused
{
get { return _passwordEntryIsFocused; }
set
{
SetProperty(ref _passwordEntryIsFocused, value);
}
}
In View Model in some method: PasswordEntryIsFocused = true;
It's not about new keyword, I tried without it.
And binding works, because I tried to bind it with a visual property, like IsVisible and it was working like it should, but setter is always called only from a custom renderer.
I think I may miss something in a context of bindable property work.

But when I change it from the view model, it affects XAML view, but for some reasons doesn't call the setter for this property.
Yes, that is a common mistake with WPF. The XAML generated code does not call the setter, instead it changes the bound dependency property immediately. You can't break on this event, unless you attach the PropertyChangedCallback event.

Related

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}" />

Better way to implement a behavior

I'm relatively new to WPF and Behaviors.
I have this behavior, I need to execute DoSomething() every time I set IsRedundant in the ViewModel.
Each time I need to trigger DoSomething, I would need to change the value of the property and this is confusing (if ture => set it to false, If false => set it to true). IsRedundant only used to raise the property changed event and for nothing else.
Is there a better way of achieving this ?
Any ideas ?
wpf
<i:Interaction.Behaviors>
<local:UIElementBehavior Redundant="{Binding IsRedundant, Mode=TwoWay}"/ >
</i:Interaction.Behaviors>
C#
class UIElementBehavior : Behavior<UIElement>
{
public static readonly DependencyProperty RedundantProperty = DependencyProperty.Register(
"Redundant",
typeof(bool),
typeof(UIElementBehavior),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, DoSomething));
public bool Redundant
{
get { return (bool)GetValue(RedundantProperty); }
set { SetValue(RedundantProperty, value); }
}
private static void DoSomething(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Do something on the AssociatedObject
}
}
Each time I need to trigger DoSomething, I would need to change the value of the property and this is confusing (if true => set it to false, If false => set it to true)
The problem is that you are using binding. Binding required target to be dependency property. And those are special, their setters aren't called, so you have to use callback to get informed when their value is changed via binding.
Moreover there is internally a check if value is different, for performance reasons callback is not called if value is the same, so you must change it as you do already.
An alternative solution is to simply add event in the view model:
public class ViewModel: INotifyPropertyChanged
{
public EventHandler SomethingHappens;
// call this to tell something to listener if any (can be view or another view model)
public OnSomethingHappens() => SomethingHappens?.Invoke(this, EventArgs.Empty);
...
}
Now you can subscribe/unsubscribe in the view to/from this event and do something in the event handler. If you are purist, then refactor code from the view into reusable behavior.
Is it shorter? Nope. Is it more clear? Yes, compared to using bool and such "wonderful" code:
IsRedundant = false;
IsRedundant = true; // lol
I was using bool properties like you do to inform the view in the past.
Then I used events.
Now I use combination of both. Every view model already implements INotifyPropertyChanged so why not use it?
Think about IsRedundant as a state. It can be used not only to trigger some method, but also used by the view to run animations via data triggers, control visibility of dynamic layout, etc. So you need a normal bool property in view model.
The view then can subscribe to/unsubscribe from PropertyChanged and simply have to check:
if(e.PropertyName == nameof(ViewModel.IsRedudant)) { ... }

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.

Dependency Property Default Value

I am new with WPF and dependency properties and my question might be totally newbie...
I have the following dependency property:
public static readonly DependencyProperty IsEditableProperty =
DependencyProperty.Register("IsEditable", typeof(bool), typeof(EditContactUserControl),
new FrameworkPropertyMetadata(false, OnIsEditablePropertyChanged));
public bool IsEditable
{
get { return (bool)GetValue(IsEditableProperty); }
set { SetValue(IsEditableProperty, value); }
}
private static void OnIsEditablePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
EditContactUserControl control = source as EditContactUserControl;
bool isEditable = (bool)e.NewValue;
if (isEditable)
control.stackPanelButtons.Visibility = Visibility.Visible;
else
control.stackPanelButtons.Visibility = Visibility.Collapsed;
}
The problem is that I want to have the code in the OnIsEditablePropertyChanged to be executed also for the default value of my property, which doesn't happen.
What am I doing wrong, or how should I do this in your opiniion?
Thank you in advance.
Instead of changing the visibility in code, you should Bind the Visibility property in XAML and use a boolean to Visibility Converter.
If you do this, it doesn't matter if the property is initialized or not.
The OnPropertyChanged callback won't be called on startup: The "default" value is in fact never really "set". Default: The value of the property when it isn't set.
If you want to execute some code at control startup, put it in the ApplyTemplate method override (in the case of a TemplatedControl) or at the end of your constructor (in the case of a UserControl)
Avoid duplicating this code in the constructor and in the property changed callback: Put it in a common method called by both ie:
void OnIsEditableChangedImpl(bool newValue)
{
....
}
I think a much better approach would be to set up stackPanelButtons.Visibility = Visibility.Collapsed in your XAML as default too, in which case you don't need to run all this code on startup!

Binding Visibility in XAML to a Visibility property

I've seen on the internet quite a few examples of binding a boolean to the Visibility property of a control in XAML. Most of the good examples use a BooleanToVisibiliy converter.
I'd like to just set the Visible property on the control to bind to a System.Windows.Visibility property in the code-behind, but it doesn't seem to want to work.
This is my XAML:
<Grid x:Name="actions" Visibility="{Binding Path=ActionsVisible, UpdateSourceTrigger=PropertyChanged}" />
This is the code for the property:
private Visibility _actionsVisible;
public Visibility ActionsVisible
{
get
{
return _actionsVisible;
}
set
{
_actionsVisible = value;
}
}
In the constructor of the Window, I also have this call:
base.DataContext = this;
When I update either ActionsVisible or this.actions.Visibility, the state doesn't transfer. Any ideas to what might be going wrong?
Change your property to be a DependencyProperty. This will handle the updating for you.
public Visibility ActionsVisible
{
get { return (Visibility)GetValue(ActionsVisibleProperty); }
set { SetValue(ActionsVisibleProperty, value); }
}
// Using a DependencyProperty as the backing store for ActionsVisible. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ActionsVisibleProperty =
DependencyProperty.Register("ActionsVisible", typeof(Visibility), typeof(FooForm));
I think the problem is that WPF can't know that your ActionsVisible property has changed since you've not notified the fact.
Your class will need to implement INotifyPropertyChanged, then in your set method for ActionsVisible you'll need to fire the PropertyChanged event with ActionsVisible as the property name that has changed.
Hope this helps...
Write: NotifyPropertyChanged("ActionsVisible")

Categories

Resources