WPF Dependency property changed but not effectuated in binding - c#

I ran into an issue which I posted about before here. I am still struggling with this issue, so I attempted to break it down in a smaller code setup.
The problem:
I have a binding of dependency property to view model, which does not update the viewmodel with it's changed value #construction time.
The binding seems correct, because changing the value in XAML after the application started (relying on xaml hot reload) does update the view model with changes.
I can reproduce the problem with the following setup:
MainWindow:
<Grid>
<local:UserControl1
SomeText="My changed text"
DataContext="{Binding UserControlViewModel}"/>
</Grid>
MainViewModel:
public class MainViewModel
{
public UserControlViewModel UserControlViewModel { get; set; }
public MainViewModel()
{
UserControlViewModel = new UserControlViewModel();
}
}
UserControl:
<UserControl.Resources>
<Style TargetType="local:UserControl1">
<Setter Property="SomeText" Value="{Binding MyText, Mode=OneWayToSource}"></Setter>
</Style>
</UserControl.Resources>
<Grid>
<TextBlock Text="{Binding MyText}"></TextBlock>
</Grid>
UserControl code behind:
public static readonly DependencyProperty SomeTextProperty = DependencyProperty.Register(
nameof(SomeText),
typeof(string),
typeof(UserControl1),
new PropertyMetadata("default text", PropertyChangedCallback));
public string SomeText { get; set; }
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// first and only update: 'default text' => 'My changed text'
}
public UserControl1()
{
InitializeComponent();
}
UserControl view model:
public class UserControlViewModel
{
// Setter is called with 'default text'
// AFTER the property changed callback is triggered with updated text
public string MyText { get; set; }
}
When I run the app, the text 'default text' is displayed, while I expected 'My changed text'.
When I then change the SomeText property in XAML, then again I see the changed callback fire, and consequently I see the view model setter get updated. This time with the changed value. Thus, the binding seems to work fine, but during startup it fails to update the view model with the (already known) changed value.
Can anybody explain what is causing this issue? Is there a way around this problem?
Update
I just found out, that when I change the XAML (using hot reload) the update sequence is:
first the viewmodel's setter is set
then the OnPropertyChanged callback fires.
the result is that the changed value is displayed on the UI
That's the opposite of what happens at construction time. Then the order is :
The OnPropertyChanged callback fires
The view model's setter is set.
The result is that the default value is displayed on the UI (as described in the original issue)
This is actually really weird. Because when the Property Changed callback fires (during start up) I can cast the DependencyObject back to my UserControl and check its data context. The datacontext is null at the time.
My previous experiment with hot reload proves that eventually the binding works perfect.
Thus step 1 is to set the text 'my changed text' to the dependency property.
Step 2 is to connect the view model as datacontext to MyUserControl
Step 3 is that the bindings are evaulated and in this case, the binding (onewaytosource) gets it's initial sync, but with the old value.
To me, this looks like a bug in WPF.

You are using the wrong binding mode for your use case.
When you specify OneWayToSource, you are allowing the data to flow only from your textbox to the property in your ViewModel, as the source is the MyText property.
Try removing the Mode=OneWayToSource, or use TwoWay if you want the text to be updated both from View and ViewModel. (IIRC TwoWay is the default mode for TextBox control).
Also, is your ViewModel implementing the INotifyPropertyChanged Interface to support the bindings?
A small summary that explains the different modes is in this SO answer

Related

Dependency Property on a usercontrol that is bound to another VM

I've run into the following problem:
I'm currently creating an on screen keyboard that is a usercontrol that has its own viewmodel.
<UserControl.DataContext>
<Binding Source="{StaticResource Locator}" Path="AlphaNumericKeyboard" />
</UserControl.DataContext>
I'm attempting to add a dependency property called KeyboardAlphaMode that can be toggled by other view models that are using this usercontrol
public static readonly DependencyProperty KeyboardAlphaModeProperty = DependencyProperty.Register("KeyboardAlphaMode",
typeof(UIKeyboardAlphaMode), typeof(AlphaNumericKeyboardView),
new FrameworkPropertyMetadata(UIKeyboardAlphaMode.LowerCase, new PropertyChangedCallback(KeyboardAlphaModeCallBack)));
private static void KeyboardAlphaModeCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e){ ... }
But, when I attempt to bind to this property from another view, the callback was never fired ..
<k:AlphaNumericKeyboardView x:Name="alphaNumericKeyboard" KeyboardAlphaMode="{Binding KeyboardAlphaMode, UpdateSourceTrigger=PropertyChanged}">
</k:AlphaNumericKeyboardView>
What am I missing here? a setter? trigger?
Or this is just a thought, can a usercontrol that has dependency be bound to a viewmodel? or does it have to be bound to itself?
Edit - 10/10/2014 # 1:31pm
After rethinking the entire solution i came up with the following scenario for my problem.
I binded the Dependency Property to the view's viewmodel and let the viewmodels interact with each other instead having other viewmodel talking to this specific view ...
Here's the code for that ..
Binding alphaModeBinding = new Binding("KeyboardAlphaMode")
{
Mode = BindingMode.TwoWay,
TargetNullValue = UIKeyboardAlphaMode.LowerCase,
FallbackValue = UIKeyboardAlphaMode.LowerCase
};
this.SetBinding(KeyboardAlphaModeProperty, alphaModeBinding);
I also made the dependency property protected so no one else can access it.
Unless there is a better way to track property changes, i'm sticking with this for now.
Again, not sure this is the best solution but it gets the job done.
Try Mode=TwoWay on the binding.

How to bind a command to a button

I am new to wpf and this fancy binding stuff, followed these tutorial and got this XAML:
<Button
x:Name="btn"
Content="refresh"
Command="{Binding RefreshCmd}" />
and this code:
public someClass ()
{
InitializeComponent();
CreateRefreshCmd();
btn.DataContext=this; // without this line it will not work !!
}
public ICommand RefreshCmd
{
get;
internal set;
}
private bool CanExecuteRefreshCmd ()
{
return true;
}
private void CreateRefreshCmd ()
{
RefreshCmd=new RelayCommand(e => RefreshExec(), c => this.CanExecuteRefreshCmd());
}
public void RefreshExec ()
{
// do something fancy here !
}
but without the last line in constructor it will not work.
In the tutorial this line does not exist.
How can i avoid this?
EDIT:
I clicked the databinding with visual studio and got this:
Command="{Binding RefreshCmd, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:spielerei}}}"
is this really necessary?
For binding to work, you need to set a data context for bindings to target, so yes, it is necessary. In the Command binding you posted in your edit, the binding is instructed to look for the RefreshCmd property on an ancestor of the Button control of type my:spielerei, which I assume is the containing window type. This is why the explicit setting of DataContext doesn't appear in the tutorial.
Bindings and commands can be used in code-behind, but are much more commonly used with view-models in the MVVM pattern. This involves setting the DataContext of your class to a view-model, which contains the properties and commands you want to bind to. To change your code to follow MVVM, we need a view-model:
public class SomeClassViewModel
{
public SomeClassViewModel()
{
this.RefreshCmd = new RelayCommand(e => RefreshExec(), c => this.CanExecuteRefreshCmd());
}
public ICommand RefreshCmd { get; internal set; }
private bool CanExecuteRefreshCmd()
{
return true;
}
public void RefreshExec()
{
// do something fancy here !
}
}
Then, in the code-behind, create the view-model, and assign it as the data context of the object:
public class SomeClass
{
public SomeClass()
{
InitializeComponent();
this.DataContext = new SomeClassViewModel();
}
}
Notice that all of the code from the SomeClass code-behind file has moved to the view-model - it is now testable, and your XAML controls can communicate with the view-model by binding to properties and executing commands.
Binding will work correctly if there is an object it can bind to. This object is read from DataContext property. If this property is not set there is nothing to bind to. It is why the following line is needed:
btn.DataContext=this;
The tutorial mentioned by you does it in a little bit different way i.e. it sets DataContext in XAML. Please examine MainWindow.xaml file from this tutorial. It contains the following code at the beginning which populates DataContext property:
<Window x:Class="MvvmCommand.MainWindow" DataContext="{Binding Main, Source={StaticResource Locator}}">
When you use a Binding in WPF, by default it sets the binding to the named property on the DataContext of the object that has the property that is bound. So in your example, the DataContext of the button.
This property is inherited down through the tree, so if not set on the Button it will look up the tree all the way to the window that holds the control.
MSDN on binding
Without all your XAML to look through I do have to guess, but I am guessing you haven't set the datacontext of the window that hosts the button. By setting it in the constructor explicitly to this you are setting the source of the binding to the object that has the property, hence why it works.
The normal way to do this is to set the data context to a class that contains the command. The usual design pattern for this is MVVM. The idea of binding is to have separation - it is not like events where you handle them in the code behind, instead it allows you to create a view model or similar class that exposes the commands and bind this to the view. This allows you to do things like unit test the functionality via the view model without having to unit test the view, share view models to multiple views etc.
data context is required to be set so that binding framework can resolve the values
you may have various method of setting the same
first method you've used
another method is to set via xaml
<Window x:Class="Project.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
Idea here is to set the data context to self.
In short, It's not necessary. Do not set datacontext to button, set data context (viemodel) for your page (view) in XAML. Of course your command must be exposed via that viewmodel.
For another question I put up simple example showing command binding and cross viewmodel communication, check it out here https://github.com/mikkoviitala/cross-viewmodel-communication

How to validate a dependency property on a UserControl?

My view model implements IDataErrorInfo and contains a Message property that is validated.
I have created a UserControl with a Text DependencyProperty that is bound to Message. There are several controls on my UserControl that are bound to Text (which therefore show Message).
How can I show validation errors on the controls in my UserControl that are not bound to Message directly?
After quite some time, I have managed to figure out a solution that I thought I should share in case others find it useful:
Basically I have added a PropertyChangedCallback on my Text DependencyProperty. In this call-back I get the binding between Text and the property on the view model and check it for validation errors. If a ValidationError is found, I go through all the controls in my UserControl that are bound to Text, and give their binding the same error using Validation.MarkInvalid.
EDIT:
Copying the validation errors like this works fine if I put the code below in a button click event handler. If however the code is in the PropertyChangedCallback for Text then nothing happens. Does anyone have a solution?
// Get the binding from the Text property to the view model.
BindingExpression textBindingExpression = BindingOperations.GetBindingExpression(this,
MyUserControl.TextProperty);
// If there is a validation error, then give it to the control bindings.
if (textBindingExpression != null && textBindingExpression.ValidationError != null) {
Validation.MarkInvalid(this.MyTextBox.GetBindingExpression(TextBox.TextProperty),
textBindingExpression.ValidationError);
Validation.MarkInvalid(this.MyTextBlock.GetBindingExpression(TextBlock.TextProperty),
textBindingExpression.ValidationError);
}
Here is the solution I came up with which allows a UserControl with Dependency Properties to "wrap" the validation from the View Model it is bound to.
Firstly I followed the pattern in this post to create the desired DataContext hierarchy.
XAML:
<!-- Some boilerplate attributes snipped -->
<UserControl x:Class="App.Views.UserControls.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:App.Views.UserControls"
Validation.ErrorTemplate="{x:Null}">
<Grid x:Name="LayoutRoot"
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MyUserControl}}">
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</UserControl>
This way, the DataContext of the control is the view model inherited from the parent, which is where the validation is done. This is then overridden on the control's root child element to be the control itself, which allows binding to the Dependency Properties in code-behind. Also note that the control's ErrorTemplate has been nulled out - this is to prevent the default red box appearing.
The inherited view model can now be accessed from the control's code behind quite simply:
private INotifyDataErrorInfo ViewModelErrors => DataContext as INotifyDataErrorInfo;
Now implement INotifyDataErrorInfo in the user control and wrap the view model:
public bool HasErrors => ViewModelErrors.HasErrors;
public IEnumerable GetErrors(string propertyName)
{
return ViewModelErrors.GetErrors(propertyName);
}
The tricky part comes when you need to know which model property your control dependency property is bound to. This would be easier if you could look up registered dependency properties by name and interrogate the binding, but I couldn't find a way to do that without reflection. Therefore I used the PropertyChangedCallback of the dependency property to manually build a list of mappings. The parameters to the callback contain all the required information.
// Maps User Control properties to their View Model properties.
private readonly Dictionary<string, string> _propertyMappings = new Dictionary<string, string>();
// This should work for any property.
private static void OnDependencyPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var userControl = (MyUserControl)d;
var dependencyPropertyName = e.Property.Name;
// Create this mapping one time only.
if (!userControl._propertyMappings.ContainsKey(dependencyPropertyName))
{
// Get the binding from the property to the view model.
var binding = BindingOperations.GetBindingExpression(d, e.Property);
if (binding != null)
{
// Create a mapping of user control property to view model property.
// This will let us look up the error from the view model.
var boundPropertyName = binding.ResolvedSourcePropertyName;
userControl._propertyMappings[dependencyPropertyName] = boundPropertyName;
}
}
}
Then incorporate this in to GetErrors:
public IEnumerable GetErrors(string propertyName)
{
if (ViewModelErrors != null && _propertyMappings.ContainsKey(propertyName))
{
return ViewModelErrors.GetErrors(_propertyMappings[propertyName]);
}
else
{
return Enumerable.Empty<string>();
}
}
That should be enough. Validation is done in the model and the results pulled down to the user control. No need to duplicate.

Custom UserControl "IsEnabled" data binding not working

I have a kinda awful problem with my WPF application right now...
I have a custom UserControl used to edit details of a component. It should start by being not enabled, and become enabled as soon as the user chose a component to edit.
The problem is: the IsEnabled property does not even change.
Here is my code:
<my:UcComponentEditor Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEnabled="{Binding EditorEnabled}"
DataContext="{Binding VmComponent}" />
EditorEnabled is a property in my ViewModel (VmComponent), and is by default false, becomes true when the user chose a component or created one
Just for the record, in my ViewModel:
private Boolean _editorEnabled = false;
public Boolean EditorEnabled
{
get { return _editorEnabled; }
set
{
_editorEnabled = value;
OnPropertyChanged("EditorEnabled");
}
}
When I try to launch my app, the UserControl is starting... enabled.
I added breakpoints everywhere, the EditorEnabled is false from the beginning.
I also did a horribly stupid thing to try to figure out what's happening: I created a converter (so useful -- converting a boolean to boolean -- eh), put a breakpoint on it, and... The code is never reached.
<my:UcComponentEditor Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEnabled="{Binding EditorEnabled, Converter={StaticResource BoolConverter}}"
DataContext="{Binding VmComponent}" />
That probably means that the property isEnabled is never set, since the converter is never reached.
Do you see any kind of problem there? I started working in WPF about one week ago and therefore I may have missed something essential...
Thank you very much for your time :-)
You should add a DependencyProperty for the binding to work properly. See here for more information.
Code-behind:
public static readonly DependencyProperty EditorEnabledDependencyProperty = DependencyProperty.Register("EditorEnabled", typeof(bool), typeof(UcComponentEditor), new PropertyMetadata(false));
public bool EditorEnabled
{
get { return (bool)base.GetValue(UcComponentEditor.EditorEnabledDependencyProperty); }
set { base.SetValue(UcComponentEditor.EditorEnabledDependencyProperty, value); }
}
The issue I think is that there is a binding on the DataContext property of the user control. Which means the EditorEnabled property should be a property in the VmComponent object. At least that's what my problem was.
To get around it, I specified a proper source to the binding of IsEnabled. Once I did that the control started working as expected.
Hope that helps.
Encapsulating your control in a DockPanel (for example) will remove the need for a DependencyProperty.
You can then simply do your binding with the dockpanel instead of the custom control. Setting the variable bound to IsEnabled on the Dockpanel will automatically enable or disable the items contained in the Dockpanel.

XAML inline data binding doesn't work; code behind binding works

Greetings folks!
I'm running into a problem with WPF databinding that I hope you can help out with. I'm new to WPF but an expereienced developer (VB 3.0-6.0, C#).
Here's the scenario:
I have a C# project called MasterPartsData which contains a number of classes which reprsent different types of parts (capacitor, diode, etc). They inherit from a base class called clsPart.
I have another C# WPF project which contains WPF UserControls (as well as a MainWindow) to visually represent the values stored in an individual MasterPartsData (MPD) object. I've created a private field in the usercontrol to hold the object with a getter and setter.
If I create a binding explicitly in the setter for the populated object:
_capacitor = value;
Binding binding = new Binding();
binding.Source = _capacitor;
binding.Path = new PropertyPath("C0uf");
this.txtC0uf.SetBinding(TextBox.TextProperty, binding);
(with _capacitor being the private object variable and C0uf being the property name)
the value correctly displays.
However I don't wish to have to explicitly create each binding in the code behind. My preference is to create the bindings inline in XAML, perhaps with a DataContext pointing to the object.
Unfortunately every different permutation I've tried fails to work; the text box doesn't show data.
I have a couple of suspicions:
1) The binding is correct, but the text box needs to be refreshed.
2) The binding is confused between the private variable and the properties.
3) Maybe the fact that the class is defined in a different project is causing issues.
4) I'm going mad and should check myself into an asylum before someone gets hurt. :)
Any help you can provide would be most appreciated. I'm more than happy to add more information, but didn't want to clutter the question with pages and pages of source.
With respect to your suspicions:
1) I think the default binding behavior of a TextBox is TwoWay, with a LostFocus update trigger, meaning that your UI focus will have to change to another control before the binding will update, if changes are made in the UI.
If changes are made in the code you need to raise the NotifyPropertyChanged event in order for the binding system to see it.
2) This is probably not the case, but it leaves the impression that you're trying to set bindings on your UserControl properties, which is not the way data binding was designed to be used in this particular kind of use case. What you want is to bind data from non-UI classes to dependency properties on your UserControls.
3) This will never matter, as long as your UI project has a reference to your classes.
4) This is a common reaction people have when beginning to use XAML and WPF. It's like instead of being handed a box of Legos, you just got handed an injection molding machine with insufficient instructions, isn't it?
Overall, this is a situation where you might need to examine your design; elements of the "Model-View-ViewModel" pattern will come in handy. If you're unfamiliar with this, it's a development pattern in which you introduce a "ViewModel" class, perhaps you can call it MasterPartsVM which contains an implementation of INotifyPropertyChanged.
The DataContext of your UserControl would be set to this MasterPartsVM class.
A brief code example, using some generic names. Given a ViewModel class with a small backing class that looks like this:
class PartViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public PartClass Data { get; set; }
public String SomeVMProperty
{
get { return Data.SomeProperty; }
set
{
if (Data.SomeProperty != value)
Data.SomeProperty = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("SomeVMProperty"));
}
}
}
class PartClass
{
public string SomeProperty { get; set; }
}
The XAML of a basic UserControl would look like this:
<UserControl x:Class="WpfApplication1.PartUserControl"
... >
<Grid>
<TextBox Text="{Binding SomeVMProperty}" Margin="68,77,104,176" />
</Grid>
</UserControl>
To connect your data class to this UserControl, you set the UserControl's DataContext property. If you do this in code, it's a matter of having a reference to your user control and the ViewModel, and then setting the property:
MyUserControlInstance.DataContext = new PartViewModel(); // or some existing PartViewModel
That combination of code should work to produce a textbox whose Text property changes every time the SomeVMProperty property is changed.
In a basic binding scenario, if your class looks like this
public class MasterPartsData
{
private string _c0uf;
public string C0uf
{
get { return _c0uf;}
set { _c0uf = value;}
}
public MasterPartsData()
{
C0uf = "Hello World!";
}
}
your XAML would look like this
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" >
<Window.DataContext>
<local:MasterPartsData />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Path=C0uf}" />
</Grid>
</Window>
Note, there are many different approaches to setting the DataContext, you don't necessarily just have to do it in the XAML
Also, typically your MasterDataParts class would implement INotifyPropertyChanged

Categories