Monday, March 28, 2016

WCF custom control--inject into WPF and MVVM Command System


Execute/CanExecute=ICommand=RoutedCmd
route only, no app code, need to find cmdbinding up VTree
Decoupled Design. DelegateCommand, ApplicationCommand implements ICmd
<Button Command="AppCmds.Cut" /> <Window.CmdBindings> has app code 
if(..) e.CanExec=true
   <CmdBdg Executed="codebhd_appCode" CanExec="cdbh"/> note Execute(d).

routing without xaml VTree 
--- target Binding, could be element outside up-VTree
    <Button Command="Cut" CmdTarget={Binding Elem=_tb1}
--- or OnApplyTemplate() inject code

btn.Cliked +=(s,e)=>{ApplicationCommands.Cut.Execute(null,__tb1);}

Custom Routed Command --need to set CmdBinding somewhere
static class CmdHost { static RoutedCmd CustCmd;}
public CustomControl1() {
   CommandBindins.Add(new CommandBinding(CmdHost.CustCmd, Exec,CanExec));
now CustCmd is injected into WPF Command System to enable the following:
(1) user <Button Command="CmdHost.CustCmd" Target="_ctl1"/>
(2) user Code Behind {Cmd.Host.CustCmd.Exec("Text RontedCmd(param,tgt");}
(3) ctr itself Exec(sender, e) { _ctl1 is updated here} 


Custom Control must Implements ICommandSource to support MVVM
propdep snippet, manual modify ICommandSource

install-package relaycommand bring down source
install-package netfx-system.windows.input.delegatecommand
DelegateCommand have ()=>true for CanExec


    [TemplatePart(Name = "PART_BDR", Type = typeof(TextBlock))]
    public class CustomControl1 : ContentControl, ICommandSource
    {
 
        #region Minimum MVVM support---DP Cmd, OnApplyTemp hookup and 

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            Border b = GetTemplateChild("PART_border") as Border;
            b.MouseLeftButtonDown += (s, e) => {
                if (Command is RoutedCommand) (Command as RoutedCommand).Execute(CommandParameter, CommandTarget);
                else Command.Execute(CommandParameter);
            };
        }

        public static readonly DependencyProperty CommandProperty =
                 DependencyProperty.Register("Command", typeof(ICommand),
                 typeof(CustomControl1), new PropertyMetadata((ICommand)null, 
                     new PropertyChangedCallback(OnCommandChanged)));

        [TypeConverter(typeof(CommandConverter))]
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        #endregion

        #region Only for CanExecute =IsEnabled from VM DelegateCommand

        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var cc = d as CustomControl1;
            if(cc!= null)
            {
                cc.OnCommandChanged((ICommand)e.OldValue,(ICommand)e.NewValue);
            }
        }

        EventHandler _persistHandleToAvoidGC;
        protected virtual void OnCommandChanged(ICommand oldCmd, ICommand newCmd)  // future extension point
        {
            HookUnhookCommand(oldCmd, newCmd);  //hook/unhook for mem clean up
        }

        private void HookUnhookCommand(ICommand oldCmd, ICommand newCmd)
        {
            _persistHandleToAvoidGC = new EventHandler((s,e)=> {
                // finnally, disable ctl through cmd.CanExe to VM DelegateCmd ()=>T/F
                IsEnabled = Command.CanExecute(CommandParameter);
            });
            if(newCmd!=null) newCmd.CanExecuteChanged += _persistHandleToAvoidGC;
            if (oldCmd != null) oldCmd.CanExecuteChanged -= _persistHandleToAvoidGC;
        }

        #endregion

        #region CmdParam/Target are related to WPF Routed Cmd, not MVVM

        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object), typeof(CustomControl1), new PropertyMetadata(null));

        public IInputElement CommandTarget
        {
            get { return (IInputElement)GetValue(CommandTargetProperty); }
            set { SetValue(CommandTargetProperty, value); }
        }

        public static readonly DependencyProperty CommandTargetProperty =
            DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(CustomControl1), new PropertyMetadata(null));

        #endregion
    }
}

 <cc:CustomControl1 Command="{Binding TestCommand}"  Background="Transparent" />

   public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            Loaded += (s,e) => // INPC need to set in loaded
            {
                TestCommand = new DelegateCommand(() =>
                { MessageBox.Show("Command Triggered"); }, 
                () => false); //true=CanExecute
            }; 
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private DelegateCommand _testCommand;
        public DelegateCommand TestCommand
        {
            get { return _testCommand; }
            set
            {
                _testCommand = value;
                if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("TestCommand"));
            }
        }
    }

1 comment: