I have a bad time trying to pass a parameter to a command.
I have the following in XAML code:
<Button Text="{Binding ButtonText}" x:Name="btnCaptureNegotiation" BackgroundColor="#3276b1"
TextColor="White" Clicked="OnCaptureNegotiationClicked"
CommandParameter="{Binding Client, Path=cod_cte}" Command="{Binding LoadULastNegotiationCommand}" ></Button>
<StackLayout Orientation="Vertical" x:Name="captureLayout" IsVisible="{Binding IsVisible}">
<!-- more code -->
And in code-behind I binded like this:
public Client client;
public NegociationVM negotiation = new NegotiationVM();
public ClientItemPage(Client client)
{
this.client = client;
negotiation.Client = client; //STOP WORKING after adding this line
InitializeComponent();
captureLayout.BindingContext = negotiation;
btnCaptureNegotiation.BindingContext = negotiation;
}
private void OnCaptureNegotiationClicked(object sender, EventArgs args)
{
negotiation.IsVisible = !negotiation.IsVisible;
}
...
And NegotiationVM class:
public class NegotiationVM : INotifyPropertyChanged
{
private bool _isVisible = false;
private string _buttonText = "Capturar Seguimiento";
private Client _client;
public Client Client{
get { return _client; }
set {
if (this._client != value)
_client = value;
NotifyPropertyChanged("Client");
}
}
private Models.NegotiationRepository _negotiationRepo;
public ICommand LoadULastNegotiationCommand { get; private set; }
public int LoadLasNegotiationResult { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public NegotiationVM(){
LoadULastNegotiationCommand = new Command<string (LoadLastNegotiationAsync);
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
async void LoadLastNegotiationAsync(string value)
{
_negotiationRepo = new Models.NegotiationRepository();
LoadLasNegotiationResult = await _negotiationRepo.GetLastNegotiationActiveAsync(value);
NotifyPropertyChanged("LoadLastNegotiationAsync");
}
public bool IsVisible
{
get
{
return _isVisible;
}
set
{
if (this._isVisible != value)
_isVisible = value;
if (this._isVisible){
this.ButtonText = "Cancel";
}else{
this.ButtonText = "Capture Negotation";
}
NotifyPropertyChanged("IsVisible");
}
}
public string ButtonText {
get
{
return _buttonText;
}
set
{
if (this._buttonText != value)
_buttonText = value;
NotifyPropertyChanged("ButtonText");
}
}
}
I found that the command is fired and tries to get resource from service, but I get 404 because I found that is not sending a parameter, I just put a breakpoint in async void LoadLastNegotiationAsync(string value) method to find that.
Because it wasn't sending anything, In code-behind Page, in the public constructor, I set the Client to the property of the same name in negotation (instance of NegotiationVM). As the comment suggest, the command STOP working and never gets fired by the button just by adding that line.
What is wrong with that binding? How can I properly send the string property of that Client?
If Cliente has a property named cod_cte. bind like so:
CommandParameter="{Binding Cliente.cod_cte}"
If the property is named Client rather than Cliente, omit the trailing e on Cliente:
CommandParameter="{Binding Client.cod_cte}"
Related
I basically used a Model's (UserAccount) Property from my ViewModel(CreateAccountViewModel) to bind to my View, and call to my Command (CreateAccountCommand).
My Model(UserAccount):
public class UserAccount : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
private string _username;
private string _password;
private DateTime _dateTime;
public int Id
{
get { return _id; }
set { _id = value; OnPropertyChanged(nameof(Id)); }
}
public string Username
{
get { return _username; }
set { _username = value; OnPropertyChanged(nameof(Username)); }
}
public string Password
{
get { return _password; }
set { _password = value; OnPropertyChanged(nameof(Password)); }
}
public DateTime DateCreated
{
get { return _dateTime; }
set { _dateTime = value; OnPropertyChanged(nameof(DateCreated)); }
}
public virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
My ViewModel(CreateAccountViewModel):
public class CreateAccountViewModel: ViewModelBase
{
private UserAccount _userAccount;
public UserAccount CurrentUserAccount
{
get { return _userAccount; }
set { _userAccount = value; OnPropertyChanged(nameof(CurrentUserAccount)); }
}
public ICommand CreateAccountCommand{ get; }
public CreateAccountViewModel()
{
CreateAccountCommand= new CreateAccountCommand(this, Test);
CurrentUserAccount = new UserAccount();
}
public void Test()
{
MessageBox.Show("Random Message");
//I'm going to put my Create functionality here
}
}
My View (CreateAccountView):
<!--The TextBox for username-->
<TextBox Grid.Column="1"
Margin="10,0,0,0"
Text="{Binding Path=CurrentUserAccount.Username, UpdateSourceTrigger=PropertyChanged}" />
<!--The PasswordBox for password-->
<components:BindablePasswordBox Grid.Column="1"
Margin="10,0,0,0"
Password="{Binding Path=CurrentUserAccount.Password, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Grid.ColumnSpan="2" />
<!--The Create user button-->
<Button Grid.Row="2"
Margin="0,20,0,0"
HorizontalAlignment="Center"
Command="{Binding CreateAccountCommand}"
Content="Create Account" />
My Command(CreateAccountCommand):
public class CreateAccountCommand: ICommand
{
private readonly CreateAccountViewModel _viewModel;
private readonly Action RunCommand;
public CreateAccountCommand(CreateAccountViewModel viewModel , Action runCommand)
{
_viewModel = viewModel;
_viewModel.PropertyChanged += ViewModel_PropertyChanged;
RunCommand = runCommand;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
//This is supposed to check whether the Username textbox and Password passwordbox is blank (if both of them are blank, the button should be disabled, else disabled
return !string.IsNullOrEmpty(_viewModel.CurrentUserAccount.Username) && !string.IsNullOrEmpty(_viewModel.CurrentUserAccount.Password);
}
public void Execute(object parameter)
{
RunCommand();
}
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
My PasswordBox is bindable because I created a custom PasswordBox with DependencyProperty:
public partial class BindablePasswordBox : UserControl
{
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register("Password", typeof(string), typeof(BindablePasswordBox),
new PropertyMetadata(string.Empty));
public string Password
{
get { return (string)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
public BindablePasswordBox()
{
InitializeComponent();
}
//This method will notify us, whenever a password in our passwordBox changes
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
Password = passwordBox.Password; //sets the value of the DependencyProperty (PasswordProperty)
}
}
My problem here, is that, the button in my View does not change enable/disable even if I set my command's CanExecute to do so. Am I missing something obvious here? I really have to ask because I've been stuck here since yesterday. (My Main goal here is to disable the Create Account button if the Textbox and PasswordBox have no input. Any solutions are okay)
Lets do a small refactoring.
use CallerMemberNameAttribute (see here how) to have shorter property setters in vm;
write once reusable ICommand implementation and use it for all commands, see DelegateCommand;
rise command CanExecuteChanged in vm when you change one of command canExecuted condition;
UserAccount needs notifications (you have done it in the edit), if it's a model, then you need an extra vm to act as a wrapper, otherwise you wouldn't be able to catch changes done by the bound controls;
Since the properties of UserAccount are part of command canExecuted, you need to monitor for them.
With all changes your button using the command should be property enabled/disabled.
Below is pseudo-code (can contain mistakes):
public class CreateAccountViewModel: ViewModelBase
{
UserAccount _userAccount;
public UserAccount CurrentUserAccount
{
get => _userAccount;
set
{
// unsubscribe
if(_userAccount != null)
_userAccount -= UserAccount_PropertyChanged;
_userAccount = value;
// subscribe
if(_userAccount != null)
_userAccount += UserAccount_PropertyChanged;
// notifications
OnPropertyChanged(); // shorter syntax with CallerMemberNameAttribute
CreateAccountCommand.RaiseCanExecuteChanged();
}
}
public ICommand CreateAccountCommand { get; }
public CreateAccountViewModel()
{
CurrentUserAccount = new UserAccount();
CreateAccountCommand = new DelegateCommand(Test,
o => !string.IsNullOrEmpty(CurrentUserAccount.Username) && !string.IsNullOrEmpty(CurrentUserAccount.Password));
}
void Test(object parameter)
{
MessageBox.Show("Random Message");
//I'm going to put my Create functionality here
}
void UserAccount_PropertyChanged(object sender, NotifyPropertyChangedEventArgs e) =>
CreateAccountCommand.RaiseCanExecuteChanged(); // rise always of check for specific properties changes
}
The CreateAccountCommand hooks up en event handler to the view model's PropertyChanged but there is no such event raised when you set the Username and Password properties of the UserAccount object.
Either implement INotifyPropertyChanged in UserAccount or bind to wrapper properties of the CreateAccountViewModel:
public string Username
{
get { return _userAccount?.Username; }
set
{
if (_userAccount != null)
_userAccount.Username = value;
OnPropertyChanged();
}
}
If you decide to implement INotifyPropertyChanged in UserAccount, you still need to notify the command when the properties have been updated.
Since your CurrentUserAccount property may be set to a new value dynamically, you should remove and add the event handler dynamically:
private UserAccount _userAccount;
public UserAccount CurrentUserAccount
{
get { return _userAccount; }
set
{
if (_userAccount != null)
_userAccount.PropertyChanged -= OnUserAccountPropertyChanged;
_userAccount = value;
if (_userAccount != null)
_userAccount.PropertyChanged += OnUserAccountPropertyChanged;
OnPropertyChanged(nameof(CurrentUserAccount));
}
}
private void OnUserAccountPropertyChanged(object sender, PropertyChangedEventArgs e) =>
OnPropertyChanged(null);
Please forgive the newbie question but I am struggling to understand where I have gone wrong...
I am trying to change an imagebutton (in a grid) by binding the image source in xaml:
<ImageButton x:Name="playButton"
Source="{Binding PlayImage}"
Command="{Binding PlayCommand}"
Grid.Row="1"
Grid.Column="0"
BorderColor="#fafafa"
BackgroundColor="#fafafa"
/>
The ImageButton loads up with the correct 'play.png' initially.
The Command 'PlayCommand' is working with the binding. This should change the value of the PlayImage to show the 'pause.png' image when the user clicks the imagebutton. Although the value of the PlayImage variable is changed, the image will not update. Please can someone tell me what I am missing? Here is my ViewModel:
public class SermonDetailViewModel : BaseViewModel
{
public Sermon Sermon { get; set; }
public ICommand PlayCommand { private set; get; }
private ImageSource _playImage;
public ImageSource PlayImage
{
get { return _playImage; }
set
{
_playImage = value;
SetProperty(ref _playImage, value);
}
}
public SermonDetailViewModel(Sermon sermon = null)
{
if (sermon != null)
{
Title = sermon.STitle;
MP3Filepath = sermon.SLink;
PlayCommand = new Command(async () => await StartPlayer());
_playImage = "play.png";
}
Sermon = sermon;
}
async Task StartPlayer()
{
await CrossMediaManager.Current.Play(MP3Filepath);
_playImage = "pause.png";
Console.WriteLine(_playImage);
Console.WriteLine(PlayImage);
}
and this is my baseViewModel code which uses the class INotifyPropertyChanged and sets up the setProperty method:
public class BaseViewModel : INotifyPropertyChanged
{
public IDataStore<Item> DataStore => DependencyService.Get<IDataStore<Item>>();
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
string mp3filepath = string.Empty;
public string MP3Filepath
{
get { return mp3filepath; }
set { SetProperty(ref mp3filepath, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
I would really appreciate some help here.....thanks!
I try your code and do one sample to test, and I agree with Jason's opinion, please try to set PlayImage value, don't set _playImage, I find that although the value of PlayImage has been updated, it is not bound to ImageButton.
Please change the code like following code:
public SermonDetailViewModel(Sermon sermon = null)
{
if (sermon != null)
{
Title = sermon.STitle;
MP3Filepath = sermon.SLink;
PlayCommand = new Command(async () => await StartPlayer());
PlayImage= "play.png";
}
Sermon = sermon;
}
async Task StartPlayer()
{
await CrossMediaManager.Current.Play(MP3Filepath);
PlayImage= "pause.png";
Console.WriteLine(_playImage);
Console.WriteLine(PlayImage);
}
I also do one sample that you can take a look:
<ImageButton
BackgroundColor="#fafafa"
BorderColor="#fafafa"
Command="{Binding PlayCommand}"
HeightRequest="50"
Source="{Binding PlayImage}"
WidthRequest="50" />
public partial class Page4 : ContentPage
{
public Page4()
{
InitializeComponent();
this.BindingContext = new SermonDetailViewModel();
}
}
public class SermonDetailViewModel:ViewModelBase
{
public ICommand PlayCommand { private set; get; }
private ImageSource _playImage;
public ImageSource PlayImage
{
get { return _playImage; }
set
{
_playImage = value;
RaisePropertyChanged("PlayImage");
}
}
public SermonDetailViewModel()
{
PlayCommand = new Command(method1);
_playImage = "check.png";
}
private void method1()
{
PlayImage = "plu3.png";
}
}
Try
Source="{Binding PlayImage, Mode=TwoWay }"
This can also help you with binding
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/binding-mode
you are setting the private backing field, not the public property. This means that the PropertyChanged event will not be raised
async Task StartPlayer()
{
await CrossMediaManager.Current.Play(MP3Filepath);
// should be PlayImage = "pause.png";
_playImage = "pause.png";
Console.WriteLine(_playImage);
Console.WriteLine(PlayImage);
}
It works with the code:
private ImageSource _playImage;
public ImageSource PlayImage
{
get { return _playImage; }
set
{
_playImage = value;
Notify("PlayImage");
}
}
protected void Notify(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I'm trying to get my head around data binding in Xamarin.Forms. I've read lots of the guides and played with some examples and I am now trying to implement some of my own basic binding.
I've got a Strings file in which I've declared an empty variable:
public static class Strings
{
public static string UserDisplayName;
}
On load of my View, it runs an async function to grab data from a Azure SQL DB which then populates the string
Strings.UserDisplayName = user.FirstName;
In my view page I've bound a label to a variable userDisplayNm
<Label Text="{Binding UserDisplayNm}"></Label>
In my ViewModel I have the following to set UserDisplayNm, however it only ever returns "Welcome, ". How do i get it to fire this again after the sync service has completed & the Strings.UserDisplayname value changes? I think I'm missing a link to an PropertyChanged event or something?
namespace Travel_Comp.ViewModels
{
public sealed class MenuViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MenuViewModel()
{
this.UserDisplayNm = Strings.UserDisplayName;
}
public string UserDisplayNm
{
set
{
if (Strings.UserDisplayName != value)
{
value = Strings.UserDisplayName;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("UserDisplayNm"));
}
}
}
get
{
return "Welcome, " + Strings.UserDisplayName;
}
}
}
}
EDIT:
Thanks for your replies. I think I'm getting closer based on the replies below, here is what I've now got, although The MenuViewModel.LoadAsync() is throwing an error "Inaccessible due to its protection level", so i can't compile to check it yet. Is this what you were suggesting & any ideas on the Protection level issue??
Strings file:
public static class Strings
{
public static string UserDisplayName;
}
ViewModel:
namespace Travel_Comp.ViewModels
{
public sealed class MenuViewModel : INotifyPropertyChanged
{
//Azure sync process
ServerManager manager;
public event PropertyChangedEventHandler PropertyChanged;
public MenuViewModel()
{
//Initial set of UserDisplayNm
this.UserDisplayNm = Strings.UserDisplayName;
}
async void LoadAsync()
{
try
{
//Run process to populate Strings.UserDisplayNm, set Syncitems to true to sync with Server
foreach (var user in await manager.GetUsersAsync(syncItems: true))
{
Strings.UserDisplayName = user.FirstName;
}
}
catch (Exception e)
{
Console.WriteLine($"Error while retrieving user name: {e}");
}
}
public string UserDisplayNm
{
set
{
if (Strings.UserDisplayName != value)
{
value = Strings.UserDisplayName;
if (PropertyChanged != null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserDisplayNm)));
}
}
}
get
{
return "Welcome, " + Strings.UserDisplayName;
}
}
}
}
View:
protected override async void OnAppearing()
{
base.OnAppearing();
ViewModels.MenuViewModel.LoadAsync();
}
So if you're looking some guidance for MVVM, you should know that usually you put your dependencies in your view model constructor, here your Azure service.
Also you could use a existing MVVM framework that will make things easy for you, like Prism or FreshMVVM.
But if you want to go for full vanilla you can also call your vm code from the view code behind.
So I'm suggesting this modification to your MenuViewModel:
private IAzureService _azureService;
private string _userDisplayNm;
public MenuViewModel(IAzureService azureService)
{
_azureService = azureService;
}
public string UserDisplayNm
{
get
{
return _userDisplayNm;
}
set
{
if (_userDisplayNm != value)
{
_userDisplayNm = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserDisplayNm)));
}
}
}
public async void LoadAsync()
{
try
{
UserDisplayNm = await _azureService.GetUserName();
}
catch (Exception exception)
{
Debug.WriteLine($"Error while retrieving user name: {exception}")
}
}
Then in you view code behind:
void OnAppearing()
{
_menuViewModel.LoadAsync();
}
To resolve the question: Inaccessible due to its protection level, you can try to add the public access modifier before the function of LoadAsync.
public async void LoadAsync(){
//....
}
And I have created a simple demo to simulate your code.
The main code is:
public sealed class TestModel: INotifyPropertyChanged
{
//*******************************************
string _userDisplayName;
public string UserDisplayName {
set { SetProperty(ref _userDisplayName, value); }
get { return _userDisplayName; }
}
public async void LoadAsync()
{
try
{
UserDisplayName = "updated value: Angela";
Strings.UserDisplayName = UserDisplayName;
}
catch (Exception exception)
{
Debug.WriteLine($"Error while retrieving user name: {exception}");
}
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
xaml
<Label Text="{Binding UserDisplayName }" BackgroundColor="Yellow"
VerticalOptions="Center" HorizontalOptions="Fill" HeightRequest="50" />
<Button Text="update the Label value" Clicked="Button_Clicked"/>
And use like this:
public partial class MyPage1 : ContentPage
{
TestModel model;
public MyPage1 ()
{
InitializeComponent ();
model = new TestModel();
BindingContext = model;
}
private void Button_Clicked(object sender, EventArgs e)
{
model.LoadAsync();
}
}
The effect is:
I am a student that just finished up a summer internship, and I brought home a project to work on briefly before school starts up. This project has a stopwatch in it, and I would rather use an ObservableCollection bound to my ListBox for my split times, rather that using the listbox.Items.Add(). When I add to the ObservableCollection, the ListBox UI does not update. Could anyone point me in the right direction on what I missed or what I did wrong?
I have my TimeSplits class:
public class TimeSplits : INotifyPropertyChanged
{
private int _hours;
private int _minutes;
private int _seconds;
public int hours
{
get
{
return _hours;
}
set
{
_hours = value;
NotifyPropertyChanged(hours);
}
}
public int minutes
{
get
{
return _minutes;
}
set
{
_minutes = value;
NotifyPropertyChanged(minutes);
}
}
public int seconds
{
get
{
return _seconds;
}
set
{
_seconds = value;
NotifyPropertyChanged(seconds);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(int propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(nameof(propertyName)));
}
}
public override string ToString()
{
return hours.ToString() + ":" + minutes.ToString() + ":" + seconds.ToString();
}
}
and my ObservableCollection in my Page:
public partial class StopwatchPage : Page , INotifyPropertyChanged
{
...
public ObservableCollection<TimeSplits> splits = new ObservableCollection<TimeSplits>();
...
public StopwatchPage()
{
DataContext = this;
InitializeComponent();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += new EventHandler(stopwatchTimer);
}
...
private void splitButton_Click(object sender, RoutedEventArgs e)
{
TimeSplits split = new TimeSplits();
split.hours = Hours;
split.minutes = Minutes;
split.seconds = Seconds;
splits.Add(split);
}
...
}
and my xaml:
<ListBox x:Name="newSplitListBox" HorizontalAlignment="Left" Margin="139,0,0,47" Width="185" Height="268" VerticalAlignment="Bottom" ItemsSource="{Binding splits}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding hours}"/>
<TextBlock Text="{Binding minutes}"/>
<TextBlock Text="{Binding seconds}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am sure it is something small that I have no clue about, as I just started learning data binding this summer. Any help is greatly appreciated! Thanks in advance.
It looks like you have nameof() in the wrong place. The way your current code reads, it will always send the value of "propertyName" as the name of the property that changed, regardless of what property actually changed.
Try this:
public int hours
{
get
{
return _hours;
}
set
{
_hours = value;
NotifyPropertyChanged();
}
}
Then, in your NotifyPropertyChanged(), do this:
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}
Edit: Added fix for the following:
Also, the ObservableCollection needs to be a property. Change this code:
public ObservableCollection<TimeSplits> splits = new ObservableCollection<TimeSplits>();
To this:
public ObservableCollection<TimeSplits> Splits { get; set; } = new ObservableCollection<TimeSplits>();
I learned a trick from Xamarin's ViewModel template that helped me immensely. Here is the code that it generates that handles an observable View Model (much like the ObservableCollection).
protected bool SetProperty<T>(ref T backingStore, T value,
Action onChanged = null,
[CallerMemberName]string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
Then, to use this, simply add this to your properties:
private string _title = string.Empty;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
Currenlty, I'm using as Below.
In xaml,
<Button Content="X" Width="33" Height="16" Padding="1,-2,1,0"
Command="{Binding ElementName=UserControlName, Path=DataContext.DenyCommand}"
<Button.CommandParameter>
<wpfext:UICommandParameter UICommandCallerCallback="{Binding ElementName=UserControlName, Path=UIDenyCallBackCommand}"/>
</Button.CommandParameter>
</Button>
In xaml.cs,
public UICommandCallerCallback UIDenyCallBackCommand
{
get;
private set;
}
public UserControlName()
{
this.UIDenyCallBackCommand = this.UIAccessDenyCallBack;
this.InitializeComponent();
}
public void UIAccessDenyCallBack(object commandParameter, object callbackData)
{
ShowADenyMsgBox();
}
private void ShowDenyMsgBox()
{
RightsDenied win = new RightsDenied(); //xaml window
win.Owner = GetImmediateWindow();
win.WindowStartupLocation = WindowStartupLocation.CenterScreen;
win.ShowDialog();
}
In ViewModel.cs,
internal ViewModel()
{
this.DenyCommand= new DenyCommand(this.AccessDeny);
}
public void AccessDeny(ICommandState commandState)
{
commandState.InvokeCallerCallback("AccessDenied");
}
public CommandCallback DenyCommand
{
get;
private set;
}
UICommandCallerCallback is declared as below.
public delegate void UICommandCallerCallback(object commandParameter, object callbackData);
CommandCallback class is as below.
public class CommandCallback:ICommand
{
private readonly Action<ICommandState> executeMethod;
private readonly Func<ICommandState, bool> canExecuteMethod;
public CommandCallback(Action<ICommandState> executeMethod)
: this(executeMethod, null)
{
}
public CommandCallback(Action<ICommandState> executeMethod, Func<ICommandState, bool> canExecuteMethod)
{
if (executeMethod == null)
{
throw new ArgumentNullException("executeMethod");
}
this.executeMethod = executeMethod;
this.canExecuteMethod = canExecuteMethod;
}
public bool CanExecute(object parameter)
{
return this.canExecuteMethod != null ? this.canExecuteMethod((ICommandState)parameter) : true;
}
public void Execute(object parameter)
{
if (parameter == null)
{
throw new ArgumentNullException("parameter","CommandCallback parameter cannot be null");
}
if (!(parameter is ICommandState))
{
throw new ArgumentException("expects a parameter of type ICommandState","parameter");
}
ICommandState state = (ICommandState)parameter;
this.executeMethod.Invoke(state);
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
}
It's working fine if it just to pop up the dialog box, but I want to wait for the result of the dialog and want to continue AccessDeny() function. For eg.
public void AccessDeny(ICommandState commandState)
{
1. processs
2. open xaml window and wait for the dialogresult. (i.e Yes No or Cancel)
3. Based on the result, continue processing.
}
What could be the best way to do this work flow? Please advise. Thanks.
Read through User Interaction Patterns in this documentation.