Dependency Property on a usercontrol that is bound to another VM - c#

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.

Related

WPF MVVM Exception when Registering UserControl in DataContext

Im new to MVVM and try to follow all the guidelines I find to respect it. I would like to have a Busy-Animation on one of my usercontrols. I want to include it on the control like this.
The Usercontrol it is nested in is shown on the MainWindow using a DataTemplate for a ViewModel, for example like so:
<Window.Resources>
<DataTemplate DataType="{x:Type AppViews:AppConfigViewModel}">
<local:AppConfigView />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentPageViewModel}" />
</Grid>
When running this, the Application is shown and I also see the view for the AppConfigViewModel which is bind correctly since underlying values are displayed correctly in the view.
Now I tried to register the Busy-Animation in the ViewModel (to control it from there) by doing this in the Constructor of the BusyAnimation:
(DataContext as PageViewModel).BusyAnim = this;
For some reason the DataContext is always null and the result of this line is an exception. What am I doing wrong here?
What I tried to did there is against the idea of MVVM.
I tried downcasting an object that is meant to be general.
A better aproach for the task I tried to achieve is implementing dependency properties in the busy animation component. Those are meant to be bound to from the viewmodel of the mainly displayed view. that way the busy animation can be shown when some property in the viewmodel changes. That could be for example a bool with the name "working".
this is the dependency property code in my busy animation:
public static readonly DependencyProperty ShowBusyProperty = DependencyProperty.Register("ShowBusy", typeof(Boolean), typeof(FortschrittView), new PropertyMetadata(false, OnShowBusyPropertyChanged));
public Boolean ShowBusy
{
get { return (Boolean)this.GetValue(ShowBusyProperty); }
set { this.SetValue(ShowBusyProperty, value); }
}
private static void OnShowBusyPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
FortschrittView myUserControl = dependencyObject as FortschrittView;
myUserControl.OnPropertyChanged("ShowBusy");
myUserControl.OnShowBusyPropertyChanged(e);
}
private void OnShowBusyPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if(ShowBusy)
{
Start();
}
else
{
Stop();
}
}
Yes its a lot of code, but I feel wpf wants it that way. Remember above code is in the busy-animation user control and triggers Start() Stop() functions which control storyboards.
Below xaml is in the control that uses the busyanimation, binding it to a viewmodel that the busy-animation should indicate background-work for:
<local:BusyAnimation ShowBusy="{Binding Model.IsBusy}"/>
That ShowBusy Property there is the Dependency Property implemented above. Of course IsBusy from the model should follow the observable pattern for everything to work.
/ps: I throughoutly documented the mistakes i did and how i solved them. Can I get rid of the negative points I got somehow for creating this question?

WPF Binding not updating with UpdateSourceTrigger=PropertyChanged

I have a TabControl in which I set the DataContext to an instance of the this class, It's basicly a wrapper for DependencyProperties of a static class with the same properties.
In my Markup I set the DataContext like this
<TabControl DataContext="{Binding ElementName=self, Path=Settings}">
and binding to the property within the TabControl like this
<TextBox Text="{Binding Path=Url, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
However, this does not lead to any updates of the source when the content of the TextBox is changed. I can change the content of the TextBox, let it loose focus etc. it does just not update the source.
Url is a dependency property and when set from XAML, wrapper property setter won't be called.
From MSDN:
The current WPF implementation of its XAML processor is inherently
dependency property aware. The WPF XAML processor uses property system
methods for dependency properties when loading binary XAML and
processing attributes that are dependency properties. This effectively
bypasses the property wrappers. When you implement custom dependency
properties, you must account for this behavior and should avoid
placing any other code in your property wrapper other than the
property system methods GetValue and SetValue.
In case you want to do something on its property changed you should provide PropertyChangedCallback and write code there.
You can refer to the sample here in case PropertyChangedCallback is new to you. Something like:
public static readonly DependencyProperty UrlProperty =
DependencyProperty.Register(
"Url",
typeof(string),
typeof(SettingsWrapper),
new PropertyMetadata(OnUrlChanged)
)
);
private static void OnUrlChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
SettingsWrapper instance = (SettingsWrapper)d;
instance.Settings.Url = e.NewValue.ToString();
}
You said in a (now deleted) comment that your Window has x:Name="self", however the Window class does not have a property called Settings.
If this is an attached property, you need to reference it by the attached property by the full name, and wrap it in parenthesis.
For example,
<TabControl DataContext="{Binding ElementName=self, Path=(local:MyClass.Settings)}">
See WPF Attached Property Data Binding for more info.

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.

Loading XAML at runtime using the MVVM pattern in WPF

This is a question that extends from the originally posted here:
Link to loading-xaml through runtime
I'm working on a WPF MVVM application that loads XAML content dynamically from an external source, very similar as the answer in the post above.
Here is what I got so far:
My View declares an instance of the ViewModel as a resource and creates an instance of that ViewModel
In my ViewModel constructor I'm loading a XamlString property coming from an external source (file or db..)
In my view I have a button that user clicks after ViewModel finishes loading and in the click-event code-behind I'm deserializing the dynamically loaded XAML and add it to my grid.
My question is, how can I eliminate code-behind and automate the logic so the View can render the new xaml section dynamically right after the ViewModel is done getting the XAML content and initializing the string property?
Should I use some kind of Messaging Bus so the ViewModel notifies once the property has been set so the View can add the new content?
What troubles me is the fact that ViewModels do have a reference to Views and should not be in charge of generating UI elements.
Thanks in advance!
Edit:
Just to clarify: in my particular case I am not trying to bind a Business Object or Collection (Model) to a UI element (e.g. Grid) which obviously could be accomplished through templates and binding. My ViewModel is retrieving a whole XAML Form from an external source and setting it as a string property available to the View. My question is: Who should be in charge of deserializing this XAML string property into a UI element and add it programmatically to the my grid once my Xaml string property in the VM is set?
This sounds to me more of like a View responsibility, not ViewModel. But the pattern as i understand it enforces to replace any code-behind logic with V-VM bindings.
I have a working solution now and I'd like to share it. Unfortunately I did not get rid of code-behind completely but it works as I expect it to. Here is how it works(simplified):
I have my simplified ViewModel:
public class MyViewModel : ViewModelBase
{
//This property implements INPC and triggers notification on Set
public string XamlViewData {get;set;}
public ViewModel()
{
GetXamlFormData();
}
//Gets the XAML Form from an external source (e.g. Database, File System)
public void GetXamlFormData()
{
//Set the Xaml String property
XamlViewData = //Logic to get XAML string from external source
}
}
Now my View:
<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
<Grid x:Name="content" Grid.Row="1" Margin="2"/>
<!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
<TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
</StackPanel>
</Grid>
Basically I created a hidden TextBlock bound to my XAML String property in the ViewModel and I hooked its Loaded event to an event handler in the code behind of the View:
private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
//First get the ViewModel from DataContext
MyViewModel vm = content.DataContext as MyViewModel;
FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
//Add the XAML portion to the Grid content to render the XAML form dynamically!
content.Children.Add(rootObject);
}
This may not be the most elegant but gets the job done. Like some people say, in MVVM there are some cases like this where little code-behind code is needed. It doesn't hurt and also part of this solution still uses the V-VM Binding principles when using the VM to retrieve and populate the XamlString property and exposing it to the View. If we would like to Unit Test the XAML parsing and loading functionality we could delegate it to a separate class.
I hope someone finds this useful!
I'm having trouble understanding what you're saying, so my answer will be based on my interpretation. You should consider posting a sample (simplified) of what you're trying to do.
1) I think you're misunderstanding what MVVM does. MVVM is mostly a binding-based pattern. Your view model should be exposing properties containing business objects and your view should just be binding to those properties. If I am misunderstanding you, and that's what you are doing, then your problem is that your view needs to be aware of when the properties get updated (after you deserialize your xaml, etc). There are two ways to do this: INotifyPropertyChanged interface on your viewmodel, or make your view model inherit from DependencyObject, and make the properties dependency properties. I won't go into details here, because this is a large subject that you should research on Google before making a decision.
2) Generally speaking, you shouldn't use click events inside your view if you're using MVVM. Instead, create properties on the view model of type ICommand (and create ICommand implementations to match, or use an implementation of DelegateCommand (google it) which will allow you to use delegates to implement the interface. The idea is, your view binds to the property and executes the handler directly inside the viewmodel.
3) If you want to push information from the viewmodel to the view, then you should create an event on the viewmodel and subscribe to it in the view, but this is a last resort, only to be used in cases like displaying a new window, etc. Generally, you should be using binding.
4) To be more specific about what you're doing, you should be binding your Grid's ItemsSource property to some property on the view model. Note, the property on the view model should be of type ObservableCollection<T> if you want to be able to add items and get instant updates.
Hope this helps.

Categories