Defining command in base View model - c#

We would like define some button be visible in every page for a set of users. We defined button as control template for view and called out it in the pages that require them to be displayed.
<Button ImageSource="home_footer.png" BackgroundColor="Transparent" HorizontalOptions="FillAndExpand"
Command="{TemplateBinding BindingContext.HomeCommand}"/>
Here command is selected according to the binding context, i.e corresponding view model. Command need to written multiple times. I thought of including the command in base view model so that it needs to written only once. Is there any option for implementing this option.
Thanks in advance.

In your BaseViewModel add a command
public ICommand HomeCommand { get; set; }
In your base ContentPage add a bindable property that supplies you this command:
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly BindableProperty CommandProperty = BindableProperty.Create(
nameof(Command),
typeof(ICommand),
typeof(ClassName),
null
);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
nameof(CommandParameter),
typeof(object),
typeof(ClassName),
null
);
Supply a command to this property from the ContentPage
<base:CustomContentPage ....
Command="{Binding HomeCommand}">
Set it to your Button something like:
You can give it a better name to explain the behaviour,
Goodluck!
Feel free to get back if you have queries!

Related

Can't programmatically data bind 3 objects in WPF

I'm trying to data bind a Slider control to a custom view model, and then bind the same property from the view model to a custom type that inherits from DependencyObject. The binding mode between the view model and the Slider control is two way, but the mode between the view model and the custom DependencyObject type should only be one way (the type should not be able to change the view model value).
Here's the relevant bit from my view model:
public class ScanViewModel : DependencyObject
{
public static readonly DependencyProperty CurrentScanIndexProperty = DependencyProperty.Register("CurrentScanIndex", typeof(Int32), typeof(ScanViewModel), new PropertyMetadata(0));
public Int32 CurrentScanIndex
{
get { return (Int32)GetValue(CurrentScanIndexProperty); }
set { SetValue(CurrentScanIndexProperty, value); }
In my XAML I bind the slider control as follows:
<Slider x:Name="scanIndexSlider" Minimum=0 Maximum = 100 Value="{Binding CurrentScanIndex, Mode=TwoWay, Delay=5}"
I have a 3rd object that participates as well:
public class CustomIndicator : DependencyObject
{
public static readonly DependencyProperty ScanIndexProperty = DependencyProperty.Register("ScanIndex", typeof(Int32), typeof(CustomIndicator), new PropertyMetadata(0));
public Int32 ScanIndex
{
get { return (Int32)GetValue(ScanIndexProperty); }
set { SetValue(ScanIndexProperty, value); }
}
public CustomIndicator(ScanViewModel ViewModel)
{
// Data bind to the view model programmatically:
Binding binding = new Binding("CurrentScanIndex");
binding.source = ViewModel;
binding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(this, CustomIndicator.ScanIndexProperty, binding);
}
I assign an instance of "ScanViewModel" as the DataContext to the view containing the slider and the binding works i.e. I manipulate the slider and the dependency property on the view model changes to reflect the new slider value. However, the view models' new value is not then passed on to the "CustomIndicator.ScanIndex" dependency property that it was bound to during the CustomIndicator constructor method. If I run through step by step I can see the binding in the constructor seems to work initially...after the programmatic bindg in customIndicator contructor is executed the objects' "ScanIndex" reflects the same value as the view model to which it was just bound, so the binding works initially. However, "ScanIndex" on "CustomIndicator" never changes after that initial change. It's as if the binding works once (in the constructor) and then never again after that. As I mentioned, the binding between the Slider control and the view model works fine.
I should add that after instantiation the "CustomIndicator" object is then added to the Children collection of a custom UserControl that has its own DataContext (a different type). Could this be the problem?

Xamarin BindableProperty in custom control

I am using a bindable property in a custom control in order to set a property from the xaml code. However, it seems like my property always will get the default value that I've specified for the bindable property.
My xaml code:
<controls:MyView ID="4" />
My code behind:
public partial class MyView : ContentView
{
public static readonly BindableProperty IDProperty = BindableProperty.Create(
nameof(ID),
typeof(int),
typeof(MyView),
15);
public int ID
{
get
{
return (int)GetValue(IDProperty);
}
set
{
SetValue(IDProperty, value);
}
}
private MyViewViewModel viewModel;
public MyView()
{
InitializeComponent();
viewModel = new MyViewViewModel() {};
BindingContext = viewModel;
}
}
I expect that my property should get value 4 in this example, but it always get the default value 15. Should the property be set in the constructor or later?
What am I doing wrong?
Why do you embed a ViewModel inside your custom control? It is weird and even wrong. The idea behind a custom control is that you could reuse and bind it to the parent's ViewModel. Think of a simple Button control, it is reusable by simple placing it on the screen and setting the BindableProperties like Text, Command and etc. It is working because it's BindingContext by default is the same as it's parent.
In your case you sort of isolate your control from any modifications, since you set the BindingContext to a private custom ViewModel class. You have to rethink your solution.
It should be as simple as:
public partial class MyView : ContentView
{
public static readonly BindableProperty IDProperty = BindableProperty.Create(
nameof(ID),
typeof(int),
typeof(MyView),
15);
public int ID
{
get => (int)GetValue(IDProperty);
set => SetValue(IDProperty, value);
}
public MyView()
{
InitializeComponent();
}
}
I see it's too late, but i have been suffering for a while now, that - for a reason that i don't know - a ContentView(Custom view element) won't bind when you set it's BindingContext property in any way other than this:
<ContentView x:Class="mynamespace.CustomViews.MyView"
....
x:Name="this">
then on the main container element (in my case a frame) set the BindingContext
<Frame BindingContext="{x:Reference this}"
....>
Setting the BindingContext in the constructor - in MyView.xaml.cs - does not work, while this way - and other ways - work in binding a View to another class (a view model), it does not - i repeat - work in binding ContentView to its code_behind.cs file.
In you xaml , do
<controls:MyView ID="{Binding Id}" />
And then in ViewModel, Create a porperty called Id
public int Id {get; set;} = 4;
You don't need Bindable property if your are not binding , Just Create a Normal Property of type int With ID as property name.And then you can assign the ID from XAML.(Intellisense will also show the ID property)
public int ID
{
get;set;
}

Expose child properties of user control directly as property of user control

Without breaking MVVM, is there a way to expose some properties of a child control in a user control so that the window or other user control that utilizes it can access these properties directly?
For instance I have a user control that has a listview set up with gridviewcolumns, headers, and is bound to a view model. But the list view in the user control has selected item properties and such that I'd like to expose to the host without having to do something like usercontrol.customListView.property. Or is that how I should do it? I'd like to go just usercontrol.property, omitting customListView. Perhap I should just create properties in the user controls code behind that return the list view controls properties that I want attached directly to the user control?
I feel like that latter option doesn't really break MVVM since they are exposed for the host to interact with, not really related to the view itself. Any suggested would be appreciated.
EDIT: In fact, I'd really like to have a SelectedItem property directly on the user control that is not ListViewItem or object, but actually of the datatype contained that doe like:
public MyDataType SelectedItem {
get {
return customListView.SelectedItem as MyDataType;
}
}
Would that be permissible in MVVM? Because I don't see how I could have that in the ViewModel, seems like it would have to be in the partial class code behind.
This is pretty common task when you want to put something repeated into UserControl. The simplest approach to do so is when you are not creating specialized ViewModel for that UserControl, but sort of making custom control (build with the use of UserControl for simplicity). End result may looks like this
<UserControl x:Class="SomeNamespace.SomeUserControl" ...>
...
<TextBlock Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}" ...>
</UserControl>
.
public partial class SomeUserControl : UserControl
{
// simple dependency property to bind to
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(SomeUserControl), new PropertyMetadata());
// has some complicated logic
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(SomeUserControl),
new PropertyMetadata((d, a) => ((SomeUserControl)d).ValueChanged()));
private void ValueChanged()
{
... // do something complicated here
// e.g. create complicated dynamic animation
}
...
}
Usage will looks like this in containing window
<l:SomeUserControl Text="Text" Value="{Binding SomeValue}" ... />
As you can see SomeValue is bound to Value and there is no MVVM violations.
Of course, you can create a proper ViewModel if view logic is complicated or required too much bindings and it's rather easier to allow ViewModels to communicate directly (via properties/methods).

Correct approach to UserControl creation when using MVVM

This is more of a conceptual question rather than a practical one. I'm just starting to learn the MVVM concept for developing UI , and I've come across a dillema I'm not sure the answer to:
Say I have a main window and a little pop-up window (meaning it's a small window with some UI elements in it). The structure of the program will look something like this:
MainWindow
model <-- MainWindowViewModel.cs <-- MainWindowView.xaml (containing no code-behind)
PopUpWindow (A UserControl)
model <-- PopUpWindowViewModel.cs <-- PopUpWindowView.xaml (containing no code-behind)
*the model is just a bunch of BL classes that are irrelevant for this question.
Now , lets say I want to create a new PopUp window from inside the MainWindowViewModel (or even save an instance of it in a private data-member). What is the correct way of doing so?
The way I see it I can't do something like this :
PopUpWindow pop = new PopUpWindow()
Because it kind of defeats the purpose of abstracting the view from the view model(What if a year from now i'll want to create a better version of the PopUpWindow using the same PopUpWindowViewModel?).
On the other hand , I can't initialize a new instnace of the PopUpWindow using just it's view model (The viewModel as I understand is not supposed to know anything about the view that will use it).
Hope it all makes sense... so what would you do in that situation?
*Just to clarify it further , let's say for argument's sake that the situation I'm describing is a button on the MainWindowView that upon clicking will open a PopUpWindowView.
Thanks in advnace.
I had somewhat a similar dilemma and I'll explain how I solved it.
Let's say you have MainWindow and a SettingsWindow, which you want to display when the SettingsButton is clicked.
You have two respective view models, MainWindowViewModel and SettingsViewModel, which you will be passing as their Window.DataContext properties.
Your MainWindowViewModel should expose an ICommand property named SettingsButtonCommand (or similar). Bind this command to the SettingsButton.Command.
Now your command should invoke something like this:
void OnSettingsButtonClicked()
{
var viewModel = new SettingsViewModel();
var window = new SettingsWindow();
window.DataContext = viewModel;
window.Show();
}
There is a slight issue when you want to use Window.ShowDialog(), because you need to resume execution.
For these cases I have an asynchronous variant of the DelegateCommand:
public sealed class AsyncDelegateCommand : ICommand
{
readonly Func<object, Task> onExecute;
readonly Predicate<object> onCanExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public AsyncDelegateCommand(Func<object, Task> onExecute)
: this(onExecute, null) { }
public AsyncDelegateCommand(Func<object, Task> onExecute, Predicate<object> onCanExecute)
{
if (onExecute == null)
throw new ArgumentNullException("onExecute");
this.onExecute = onExecute;
this.onCanExecute = onCanExecute;
}
#region ICommand Methods
public async void Execute(object parameter)
{
await onExecute(parameter);
}
public bool CanExecute(object parameter)
{
return onCanExecute != null ? onCanExecute(parameter) : true;
}
#endregion
}
You've specifically said that the popup is a UserControl so you can use basic data templating. First create view models for your main window and popup control:
public class MainViewModel : ViewModelBase
{
private PopUpViewModel _PopUp;
public PopUpViewModel PopUp
{
get { return _PopUp; }
set { _PopUp = value; RaisePropertyChanged(() => this.PopUp); }
}
}
public class PopUpViewModel : ViewModelBase
{
private string _Message;
public string Message
{
get { return _Message; }
set { _Message = value; RaisePropertyChanged(() => this.Message); }
}
}
The MainViewModel's PopUp member is initially null, we'll set it to an instance of PopUpViewModel when we want the popup to appear. To do that we create a content control on the main window and set it's content to that member. We also use a data template to specify the type of child control to create when the popup view model has been set:
<Window.Resources>
<DataTemplate DataType="{x:Type local:PopUpViewModel}">
<local:PopUpWindow />
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Show PopUp" Click="Button_Click_1" HorizontalAlignment="Left"/>
<ContentControl Content="{Binding PopUp}" />
</StackPanel>
I'm doing a big no-no here by creating the view model in the code-behind along with a click handler, but it's just for illustrative purposes:
public partial class MainWindow : Window
{
MainViewModel VM = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = this.VM;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.VM.PopUp = new PopUpViewModel { Message = "Hello World!" };
}
}
That's it! Click the button, popup window appears underneath it showing the content. Now it may not always be this simple, sometimes you may want to create multiple children on a parent control...in that case you'd set up an ItemsControl, set its panel to a Grid (say) and modify the data templates to set the Margin etc on each element to position them. Or you may not always know what type of view model is going to be created, in which case you need to add multiple data templates for each type you're expecting. Either way you still have good separation of concerns because it is the views that are deciding how to display the content in the view models. The view models themselves still don't know anything about the views and they can be unit-tested etc independently.

Dependency property in User Control is null after assigning value

I have a user control, has a Frame as a dependency property
public partial class NavigationBar : UserControl
{
public NavigationBar()
{
this.InitializeComponent();
}
public Frame NavigationFrame
{
get { return (Frame)GetValue(NavigationFrameProperty); }
set { SetValue(NavigationFrameProperty, value); }
}
// Using a DependencyProperty as the backing store for NavigationFrame. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NavigationFrameProperty =
DependencyProperty.Register("NavigationFrame", typeof(Frame), typeof(NavigationBar), new UIPropertyMetadata());
}
Using the User Control code:
<userControls:NavigationBar NavigationFrame="{Binding ElementName=masterPage, Path=frameNavigations}" />
<Frame x:Name="frameNavigations"/>
Why the NavigationFrame dependency property still null after loading ?
Thanks in advance
That will look for an element in your page with a name of masterPage, and then look for a property on that object called frameNavigations. I think you actually want to bind to the element with the name frameNavigations, so you would just write:
<userControls:NavigationBar
NavigationFrame="{Binding ElementName=frameNavigations}"/>
<Frame x:Name="frameNavigations"/>

Categories

Resources