So in this datagrid and then I want double click on a datarow and then pass the selecteditem to the next user control.
I'm using a navigation service which i have seen on SingletonSean.
However, I'm not sure how to combine those 2 things.
My DoubleClickCommand is for sending my selecteditem to the next screen.
The cmdNavigatePerson is for opening the screen.
View:
<DataGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding DoubleClickCommand}"
CommandParameter="{Binding ElementName=dgResults, Path=SelectedItem}" />
</DataGrid.InputBindings>
ViewModel:
public ICommand DoubleClickCommand { get; set; }
public ICommand cmdNavigatePerson { get; }
public clsPersonVM(IGenericNavigationService<clsPersonVM> personNavigationService)
{
cmdNavigatePerson = new cmdNavigation<clsPersonVM>(personNavigationService);
MijnService = new clsPersonDataService();
DoubleClickCommand = new clsCustomCommand(Execute_DoubleClickCommand, CanExecute_DoubleClickCommand);
LoadData();
clsMessenger.Default.Register<clsUpdateListMessages>(this, OnUpdateListMessageReceived);
}
private bool CanExecute_DoubleClickCommand(object obj)
{
return true;
}
private void Execute_DoubleClickCommand(object obj)
{
clsMessenger.Default.Send<clsPersonsM>(MijnSelectedItem);
}
private void OnUpdateListMessageReceived(clsUpdateListMessages obj)
{
LoadData();
}
CmdNavigation:
public class cmdNavigation<TViewModel> : clsCommandBase
where TViewModel : ViewModelBase
{
private readonly clsNavigationStore _clsNavigationStore;
private readonly Func<TViewModel> _createViewModel;
private readonly IGenericNavigationService<TViewModel> _navigationService;
public cmdNavigation(clsNavigationStore clsNavigationStore, Func<TViewModel> createViewModel)
{
_clsNavigationStore = clsNavigationStore;
_createViewModel = createViewModel;
}
public cmdNavigation(IGenericNavigationService<TViewModel> navigationService)
{
_navigationService = navigationService;
}
public override void Execute(object parameter)
{
_navigationService.Navigate();
}
}
In the XAML i've tried to put 2 mousebindings with the same MouseAction but it only performs the last one.
Related
Trying to get to grips with MVVM in WPF c#.
I am a slow learner...
I have my MainWindow.xaml.
This is the markup in question:
<Viewbox x:Name="vbxucProductCostMenu" Stretch="{Binding Stretch}" StretchDirection="Both">
//a user control
</Viewbox>
<Button Command="{Binding MagnifyMinimiseCommand}" CommandParameter="UniformToFill">
<Image Source="Images/button_plus_green.png"/>
</Button>
Part of my MainWindow.cs
public MainWindow()
{
InitializeComponent();
this.DataContext = new MagnifyMinimise();
}
My Viewmodel?
public class MagnifyMinimise : INotifyPropertyChanged
{
public MagnifyMinimise()
{
Minimise();
}
MagnifyMinimiseCommand _magnifyMinimiseCommand = new MagnifyMinimiseCommand();
public MagnifyMinimiseCommand MagnifyMinimiseCommand
{
get { return _magnifyMinimiseCommand; }
}
public event PropertyChangedEventHandler PropertyChanged;
public void Magnify()
{
Stretch = "UniformToFill";
}
public void Minimise()
{
Stretch = "None";
}
public string Stretch { get; set; }
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
my 'ICommand' class:
public class MagnifyMinimiseCommand : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
//how do I set the property of stretch here!!
}
}
When I run this it starts up minimized which is good.
I then want to 'maximize' the viewbox when the user clicks that button.
By setting the breakpoint in the 'Execute' method i can see that it is being invoked and the 'parameter' is set to 'UniformToFill'.
But how do I get the Stretch property to 'read' that?
ADDITONAL:
I have changed it all to this (which does not work):
public class MagnifyMinimise : INotifyPropertyChanged
{
private ActionCommand<string> _magnifyMinimiseCommand;
public MagnifyMinimise()
{
Minimise();
_magnifyMinimiseCommand = new ActionCommand<string>(Magnify);
}
private void Magnify(string stretch)
{
// now the viewmodel handles it instead of the action
Stretch = stretch;
}
public event PropertyChangedEventHandler PropertyChanged;
public void Magnify()
{
Stretch = "UniformToFill";
}
public void Minimise()
{
Stretch = "None";
}
public string Stretch { get; set; }
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class ActionCommand<T> : ICommand where T : class
{
private readonly Action<T> mAction;
public ActionCommand(Action<T> action)
{
mAction = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
mAction(parameter as T);
}
public event EventHandler CanExecuteChanged;
}
<Button Command="{Binding ActionCommand}" CommandParameter="UniformToFill">
<Image Source="Images/button_plus_green.png" />
</Button>
The easiest way is, like suggested by #Default, to use a RelayCommand. There is one (or an alternative) provided in every major MVVM framework (Prism, MVVM Light, Caliburn.Micro, ...).
That said, if you wanted to solve the issue with your vanilla implementation of a command, you'd just have to pass a reference to the viewmodel in the constructor:
public class MagnifyMinimiseCommand : ICommand
{
public MagnifyMinimiseCommand(MagnifyMinimise viewModel)
{
this.ViewModel = viewModel;
}
protected MagnifyMinimise ViewModel { get; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
this.ViewModel.IsMagnifying = "...";
}
}
You need to invoke PropertyChanged for Stretch. That's how i would do it:
private string _stretch;
public string Stretch
{
get { return _stretch; }
set {_stretch = value; OnPropertyChanged("Stretch"); }
}
Also you might want to consider using RelayCommand or DelegateCommand
Another sidenote: In MVVM try not to write any code in the view's code behind. Use App.xaml.cs for setting the DataContext of the view.
EDIT: To answer your question, i would create a DelegateCommand class like this:
class DelegateCommand : ICommand
{
private readonly Action<Object> _execute;
private readonly Func<Object, Boolean> _canExecute;
public DelegateCommand(Action<Object> execute) : this(null, execute) { }
public DelegateCommand(Func<Object, Boolean> canExecute, Action<Object> execute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public Boolean CanExecute(Object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(Object parameter)
{
if (!CanExecute(parameter))
{
throw new InvalidOperationException("Command execution is disabled.");
}
_execute(parameter);
}
public void OnCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
and use it like this in your viewmodel:
public DelegateCommand MagnifyMinimiseCommand { get; private set; }
.....
MagnifyMinimiseCommand = new DelegateCommand(param => { Stretch = UniformToFill; });
then
<Button Command="{Binding MagnifyMinimiseCommand}">
<Image Source="Images/button_plus_green.png"/>
</Button>
Instead of using such a specific type of Command, you can create a more generic command and allow the viewmodel to handle the action itself. So create a generic type of ICommand:
public class ActionCommand<T> : ICommand where T : class
{
private readonly Action<T> mAction;
public ActionCommand(Action<T> action)
{
mAction = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
mAction(parameter as T);
}
public event EventHandler CanExecuteChanged;
}
and then create it like this:
private ActionCommand<string> _magnifyMinimiseCommand;
public MagnifyMinimise()
{
_magnifyMinimiseCommand = new ActionCommand<string>(Magnify);
....
}
private void Magnify(string stretch)
{
// now the viewmodel handles it instead of the action
Stretch = stretch;
}
Also, as a common practice I usually expose the properties to the View as it's interfaces, so the MagnifyMinimiseCommand would for instance be an ICommand instead (you can still use the field to access the ActionCommands stuff).
I've added a DialogService in order to open a ProductView, so far the ShowDetailDialog() is working as expected.
Issue:
I call Close() on the ProductView, the view isn't closed. I debugged this issue by setting a break point on the call to the dialog service close method.
When I stepped through the code, the null check shows that productView is null, which prevents Close() from being called.
Does anyone have idea why productView is null? (although it's showing data on the view)
DialogService:(hosts the Show and Close methods)
namespace MongoDBApp.Services
{
class DialogService : IDialogService
{
Window productView = null;
ProductView _productView;
public DialogService()
{
_productView = new ProductView();
}
public void CloseDetailDialog()
{
if (productView != null)
productView.Close();
}
public void ShowDetailDialog()
{
_productView.ShowDialog();
}
}
}
ProductViewModel: (summary of ProductVM, calls the close method on SaveCommand)
private void SaveProduct(object product)
{
_dialogService.CloseDetailDialog();
Messenger.Default.Send<ProductModel>(SelectedProduct);
}
CustomerOrdersViewmodel: (Where the ShowDetailDialog() is called initially)
private void EditOrder(object obj)
{
Messenger.Default.Send<ProductModel>(SelectedProduct);
_dialogService.ShowDetailDialog();
}
This is how I have always closed my windows.
Here would be my command:
class CancelCommand : ICommand
{
private NewTruckViewModel newTruck;
public CancelCommand(NewTruckViewModel vm)
{
newTruck = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
newTruck.Cancel();
}
}
Here is my view Model and the method that gets called from my command:
private NewTruck myWnd; //View Declaration
//Ctor where I set myView (myWnd) equal to a view that is passed in.
public NewTruckViewModel(ObservableCollection<Truck> Trucks, NewTruck wnd, bool inEditTruck)
{
myEngine.stopHeartBeatTimer();
editTruck = inEditTruck;
myWnd = wnd;
SaveTruckCommand = new SaveTruckCommand(this);
CancelCommand = new CancelCommand(this);
ClearCommand = new ClearCommand(this);
SetLevel1MTCommand = new SetLevel1MTCommand(this);
SetLevel2MTCommand = new SetLevel2MTCommand(this);
SetLevel3MTCommand = new SetLevel3MTCommand(this);
SetLevel1FLCommand = new SetLevel1FLCommand(this);
SetLevel2FLCommand = new SetLevel2FLCommand(this);
SetLevel3FLCommand = new SetLevel3FLCommand(this);
myTrucks = Trucks;
}
public void Cancel()
{
myWnd.Close();
}
This works for me.
I resolved the issue by implementing an IDialogService on the View. Then calling the Show() and Close() methods from the ViewModel.
Solution:
Interface:
public interface IDialogService
{
void CloseDialog();
void ShowDialog(EditProductViewModel prodVM);
}
View:
public partial class ProductView : Window, IDialogService
{
public ProductView()
{
InitializeComponent();
this.DataContext = new EditProductViewModel(this);
}
public void CloseDialog()
{
if (this != null)
this.Visibility = Visibility.Collapsed;
}
public void ShowDialog(EditProductViewModel prodVM)
{
this.DataContext = prodVM;
this.Show();
}
private void Window_Closed(object sender, EventArgs e)
{
this.Visibility = Visibility.Collapsed;
}
}
ViewModel #1:
private IDialogService _dialogService;
public CustomerOrdersViewModel(IDialogService dialogservice)
{
this._dialogService = dialogservice;
}
private void EditOrder(object obj)
{
EditProductViewModel pvm = new EditProductViewModel(_dialogService);
pvm.Present(pvm);
Messenger.Default.Send<ProductModel>(SelectedProduct);
}
ViewModel #2:
private IDialogService _dialogService;
public EditProductViewModel(IDialogService dialogService)
{
this._dialogService = dialogService;
}
private void SaveProduct(object product)
{
SelectedProduct = SelectedProductTemp;
_dialogService.CloseDialog();
}
public void Present(EditProductViewModel prodVM)
{
_dialogService.ShowDialog(prodVM);
}
New to WPF and C# from VB web forms, so sorry for this poorly structured question I will add to as needed to improve. I am trying to implement an example by adding database calls to MySQL to populate an On-Demand Tree View control. Here is the link to the sample code...
sample code
Got my db connection working and data is populating my dataset. I iterate to place in a List. But can not seem to figure out the issue with passing the List to the Class to populate the control...
public class Level1
{
public Level1(string level1Name)
{
this.Level1Name = level1Name;
}
public string Level1Name { get; private set; }
readonly List<Level2> _level2s = new List<Level2>();
public List<Level2> Level2s
{
get { return _level2s; }
}
}
I have a database class that queries the db and parses the data....
List<string> level1s = new List<string>();
DataSet ds = new DataSet();
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
level1s.Add((string)row["name"]);
}
}
**UPDATE**: Trying to return the list...
return new Level1[]
{
foreach(DataRow row in level1s)
{
// iterate here
}
};
My level1s List is properly populated, I am just drawing a blank on returning the values.
thanks,
UPDATE - I am including the ViewModel code here as well....
using BusinessLib;
namespace TreeViewWithViewModelTOC.LoadOnDemand
{
public class Level1ViewModel : TreeViewItemViewModel
{
readonly Level1 _level1;
public Level1ViewModel(Level1 level1)
: base(null, true)
{
_level1 = level1;
}
public string Level1Name
{
get { return _level1.Level1Name; }
}
protected override void LoadChildren()
{
foreach (Level2 level2 in Database.GetLevel2s(_level1))
base.Children.Add(new Level2ViewModel(level2, this));
}
}
}
Try like this below,
List<Level1> L1=new List<Level1>();
foreach(var row in level1s)
{
Level1 L=new Level1();
// L.Level1Name = row.ToString(); here add items as you need
L1.Add(L);
}
return L1.ToArray();
You should be using MVVM design pattern to solve this. There aren't many requirements listed in your questions so I will assume my own, which should lead you along the right path.
First thing is determining whether or not you're records are going to be ready/pulled at run-time--before the TreeView is rendered and if they will be changed/updated/added/removed from the structure during the lifecycle of the application. If the structure isn't going to be changed, you can continue to use List as your collection. If you're (or a user is) going to be adding/removing from the collection, ultimately changing the structure, then you need to notify the UI that a change occurred on the collection; so you would use the built in ObservableCollection for that. Here is a MVVM-purist solution, with the assumption that your data will be pulled at application startup and you will be modifying the collection:
Note: RelayCommand implementation was taken from here
Models
public class First
{
public string Name
{
get;
set;
}
public readonly List<Second> Children;
public First(string name)
{
Name = name;
Children = new List<Second>
{
new Second(1),
new Second(2),
new Second(3),
};
}
public void AddChild(Second child)
{
Children.Add(child);
ChildAdded(this, new ChildAddedEventArgs(child));
}
public EventHandler<ChildAddedEventArgs> ChildAdded;
}
public class ChildAddedEventArgs //technically, not considered a model
{
public readonly Second ChildAdded;
public ChildAddedEventArgs(Second childAdded)
{
ChildAdded = childAdded;
}
}
public class Second
{
public int Number
{
get;
set;
}
public Second(int number)
{
Number = number;
}
}
ViewModels
public class MainViewModel : INotifyPropertyChanged
{
private readonly ObservableCollection<FirstViewModel> _items;
private readonly ICommand _addFirstFirstChildCommand;
private readonly ICommand _addSecondFirstChildCommand;
private readonly ICommand _toggleExpandCollapseCommand;
private bool _firstAddedFlag;
public MainViewModel(IEnumerable<First> records)
{
_items = new ObservableCollection<FirstViewModel>();
foreach(var r in records)
{
_items.Add(new FirstViewModel(r));
}
_addFirstFirstChildCommand = new RelayCommand(param => AddFirst(), param => CanAddFirst);
_addSecondFirstChildCommand = new RelayCommand(param => AddSecond(), param => CanAddSecond);
_toggleExpandCollapseCommand = new RelayCommand(param => ExpandCollapseAll(), param =>
{
return true;
});
}
public ObservableCollection<FirstViewModel> Items
{
get
{
return _items;
}
}
public ICommand AddFirstFirstChildCommand
{
get
{
return _addFirstFirstChildCommand;
}
}
public ICommand AddSecondFirstChildCommand
{
get
{
return _addSecondFirstChildCommand;
}
}
public ICommand ToggleExpandCollapseCommand
{
get
{
return _toggleExpandCollapseCommand;
}
}
public bool CanAddFirst
{
get
{
return true;
}
}
public bool CanAddSecond
{
get
{
//Only allow second to be added if we added to first, first
return _firstAddedFlag;
}
}
public void AddFirstChild(FirstViewModel item)
{
Items.Add(item);
}
private void AddFirst()
{
_items[0].AddChild(new Second(10));
_firstAddedFlag = true;
}
private void AddSecond()
{
_items[1].AddChild(new Second(20));
}
private void ExpandCollapseAll()
{
foreach(var i in Items)
{
i.IsExpanded = !i.IsExpanded;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class FirstViewModel : INotifyPropertyChanged
{
private readonly First model;
private readonly ObservableCollection<SecondViewModel> _children;
private bool _isExpanded;
public FirstViewModel(First first)
{
_children = new ObservableCollection<SecondViewModel>();
model = first;
foreach(var s in first.Children)
{
Children.Add(new SecondViewModel(s));
}
model.ChildAdded += OnChildAdded;
}
public string FirstName
{
get
{
return model.Name;
}
set
{
model.Name = value;
NotifyPropertyChanged();
}
}
public ObservableCollection<SecondViewModel> Children
{
get
{
return _children;
}
}
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
_isExpanded = value;
NotifyPropertyChanged();
}
}
internal void AddChild(Second second)
{
model.AddChild(second);
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnChildAdded(object sender, ChildAddedEventArgs args)
{
if(Children != null)
{
Children.Add(new SecondViewModel(args.ChildAdded));
}
}
}
public class SecondViewModel : INotifyPropertyChanged
{
private readonly Second model;
private bool _isExpanded;
public SecondViewModel(Second second)
{
model = second;
}
public int SecondNumber
{
get
{
return model.Number;
}
set
{
model.Number = value;
NotifyPropertyChanged();
}
}
//Added property to avoid warnings in output window
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
_isExpanded = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Model Provider
public class Database
{
public static IEnumerable<First> GetChildren()
{
List<First> firsts = new List<First>();
firsts.Add(new First("John"));
firsts.Add(new First("Roxanne"));
return firsts;
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainViewModel mvm;
public MainWindow()
{
var db = Database.GetChildren();
mvm = new MainViewModel(db);
InitializeComponent();
DataContext = mvm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//Do not do this, example only
var f = new First("Billy");
mvm.AddFirstChild(new FirstViewModel(f));
//Prove that the event was raised in First, FirstViewModel see & handles it, and
//the UI is updated
f.AddChild(new Second(int.MaxValue));
}
}
MainWindow.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow">
<Grid>
<TreeView ItemsSource="{Binding Items}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:FirstViewModel}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding FirstName}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:SecondViewModel}">
<TextBlock Text="{Binding SecondNumber}" />
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<StackPanel Orientation="Vertical"
VerticalAlignment="Bottom">
<StackPanel Orientation="Horizontal">
<Button Content="Add Child to first First"
Command="{Binding AddFirstFirstChildCommand}" />
<Button Content="Toggle Expand"
Command="{Binding ToggleExpandCollapseCommand}" />
<Button Content="Add Child to second First"
Command="{Binding AddSecondFirstChildCommand}" />
</StackPanel>
<Button Content="Bad Codebehind Button"
Click="Button_Click"/>
</StackPanel>
</Grid>
</Window>
this returns array of Level1 from first table in DataSet (usually there's only one table)
public void Level1[] GetLevels()
{
DataSet ds = ....
return ds.Tables[0].Rows
.Select(row => new Level1((string)row["name"]))
.ToArray();
}
if you had more than one table in the dataset, you can use this method to loop trough all tables:
public void Level1[] GetLevels()
{
DataSet ds = ....
return ds.Tables
.SelectMany(t => t.Rows)
.Select(row => new Level1((string)row["name"]))
.ToArray();
}
The second code sample does exactly the same as your code in the question.
Understanding linq is extremely useful.
I am trying to use a RoutedCommand on my view so that I can use the CanExecute functionality, but the only way I can get it to work is with a DelegateCommand from Prism. When I try to use the RoutedCommand the button stays inactive and the CanExecute function never gets used.
I've tried putting a CommandBinding on my XAML but that gives a "Only instance methods on the generated or code-behind class are valid." error. Here is that code:
<Window.CommandBindings>
<CommandBinding Command="AddCommand"
Executed="my:SettingsDialogViewModel.AddCommandMethod"
CanExecute="my:SettingsDialogViewModel.AddCommandMethodCanExecute" />
</Window.CommandBindings>
I've also tried setting up a CommandBinding in code, but that doesn't help either. I'm just not sure how to get it to work, short of sticking it in the code-behind, or implementing some ridiculously complicated looking thing I've found on the web.
Thanks for any help :)
EDIT:
Here are the methods I am trying to use:
public void AddCommandMethod()
{
if (SelectedMain != null)
{
SelectedMain.IsDirty = true;
_faveAppList.Add(SelectedMain);
SelectedMain.ListOrder = _faveAppList.Count;
_mainAppList.Remove(SelectedMain);
_listDirty = true;
}
}
public void AddCommandMethodCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
That isn't the proper MVVM notation. I'll provide one way of doing this.
// MyView.cs
public class MyView : UserControl
{
public MyViewViewModel ViewModel
{
get { return (MyViewViewModel) DataContext;}
set { DataContext = value; }
}
}
// DelegateCommand.cs
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute)
: this(execute, null) {}
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public override bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public override void Execute(object parameter)
{
_execute(parameter);
}
}
// MyViewViewModel.cs
public class MyViewViewModel
{
public ICommand AddCommand {get;set;}
public MyViewViewModel()
{
AddCommand = new DelegateCommand (AddCommandMethod, AddCommandMethodCanExecute);
}
private void AddCommandMethod (object parameter)
{
}
private bool AddCommandMethodCanExecute(object parameter)
{
// Logic here
return true;
}
}
// MyView.xaml
<Button Command="{Binding AddCommand}" />
A better option would be to implement the ICommand interface and write your logic in the implemented methods. Then your view model can return your custom command and you could just bind to it from your view.
This will separate the actual command implementation from your view model but you can still nicely implement the logic within your view model.
Something like this:
public abstract class BaseCommand : ICommand
{
// needed to connect to WPF's commanding system
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public abstract bool CanExecute(object parameter);
public abstract void Execute(object parameter);
}
public class AddCommand : BaseCommand
{
private readonly MyViewModel _vm;
public AddCommand(MyViewModel vm)
{
this._vm = vm;
}
public override bool CanExecute(object parameter)
{
// delegate back to your view model
return _vm.CanExecuteAddCommand(parameter);
}
public override void Execute(object parameter)
{
_vm.ExecuteAddCommand(parameter);
}
}
public class MyViewModel
{
public ICommand AddCommand { get; private set; }
public MyViewModel()
{
AddCommand = new AddCommand(this);
}
public bool CanExecuteAddCommand(object parameter)
{
}
public void ExecuteAddCommand(object parameter)
{
}
}
Then just bind controls that issues the command.
<Button Command="{Binding AddCommand}">...</Button>
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.