¿Cómo se puede vincular un botón a un comando en un modelo de vista como en WPF con MVVM?Enlace a comandos en WinForms
Respuesta
He adjuntado ICommand
objetos a la propiedad Tag
de Button
y MenuItem
objetos antes.
Entonces, acabo de ver si puedo fundido y ejecutarlo si puedo, ejemplo:
private void button1_Click(object sender, EventArgs e)
{
ICommand command = ((Control)(sender)).Tag as ICommand;
if (command != null)
{
command.Execute();
}
}
Porque incluso una vida más fácil, tratar de crear subclases de los controles (por ejemplo Button
, MenuItem
)
No creo que pueda hacerlo directamente, pero ¿qué tal si utiliza el controlador de clics del botón para invocar el comando? No es tan limpio como WPF pero aún así obtienes tu separación.
Recomiendo implementar INotifyPropertyChanged, puede usarlo tanto en WinForms como en WPF. Consulte here para una introducción y here para obtener más información.
Usted podría encontrar el WAF Windows Forms Adapter interesante. Muestra cómo aplicar el patrón Model-View-ViewModel (MVVM) en una aplicación Windows Forms. La implementación del adaptador proporciona una solución para el soporte que falta en Windows Forms.
Me preguntaba si lo mismo se podría hacer y terminó escribiendo un Administrador de comandos simple que consulta los comandos registrados (en el caso Application.Idle) y utiliza el enlace de datos para cambiar el estado Habilitado del control
Esto es el código que estoy usando en este momento:
public class CommandManager: Component
{
private IList<ICommand> Commands { get; set; }
private IList<ICommandBinder> Binders { get; set; }
public CommandManager()
{
Commands = new List<ICommand>();
Binders = new List<ICommandBinder>
{
new ControlBinder(),
new MenuItemCommandBinder()
};
Application.Idle += UpdateCommandState;
}
private void UpdateCommandState(object sender, EventArgs e)
{
Commands.Do(c => c.Enabled);
}
public CommandManager Bind(ICommand command, IComponent component)
{
if (!Commands.Contains(command))
Commands.Add(command);
FindBinder(component).Bind(command, component);
return this;
}
protected ICommandBinder FindBinder(IComponent component)
{
var binder = GetBinderFor(component);
if (binder == null)
throw new Exception(string.Format("No binding found for component of type {0}", component.GetType().Name));
return binder;
}
private ICommandBinder GetBinderFor(IComponent component)
{
var type = component.GetType();
while (type != null)
{
var binder = Binders.FirstOrDefault(x => x.SourceType == type);
if (binder != null)
return binder;
type = type.BaseType;
}
return null;
}
protected override void Dispose(bool disposing)
{
if (disposing)
Application.Idle -= UpdateCommandState;
base.Dispose(disposing);
}
}
public static class Extensions
{
public static void Do<T>(this IEnumerable<T> @this, Func<T, object> lambda)
{
foreach (var item in @this)
lambda(item);
}
}
public abstract class CommandBinder<T> : ICommandBinder where T: IComponent
{
public Type SourceType
{
get { return typeof (T); }
}
public void Bind(ICommand command, object source)
{
Bind(command, (T) source);
}
protected abstract void Bind(ICommand command, T source);
}
public class ControlBinder: CommandBinder<Control>
{
protected override void Bind(ICommand command, Control source)
{
source.DataBindings.Add("Enabled", command, "Enabled");
source.DataBindings.Add("Text", command, "Name");
source.Click += (o, e) => command.Execute();
}
}
public class MenuItemCommandBinder : CommandBinder<ToolStripItem>
{
protected override void Bind(ICommand command, ToolStripItem source)
{
source.Text = command.Name;
source.Enabled = command.Enabled;
source.Click += (o, e) => command.Execute();
command.PropertyChanged += (o, e) => source.Enabled = command.Enabled;
}
}
y esto es un exmaple de cómo usarlo:
public partial class Form1 : Form
{
private CommandManager commandManager;
public ICommand CommandA { get; set; }
public ICommand CommandB { get; set; }
public bool condition;
public Form1()
{
InitializeComponent();
commandManager = new CommandManager();
CommandA = new DelegateCommand("Command 1", OnTrue, OnExecute);
CommandB = new DelegateCommand("Command 2", OnFalse, OnExecute);
commandManager.Bind(CommandA, button1);
commandManager.Bind(CommandB, button2);
commandManager.Bind(CommandA, command1ToolStripMenuItem);
commandManager.Bind(CommandB, command2ToolStripMenuItem);
}
private bool OnFalse()
{
return !condition;
}
private bool OnTrue()
{
return condition;
}
private void OnExecute()
{
condition = !condition;
}
}
Además, si necesita el código, que blogg ed about it here
su solución funciona realmente bien y hace que mis componentes de diálogo sean mucho más fáciles y comprensibles :-). ¡muchas gracias! – rhe1980
me alegra que funcione bien y te gusta! –
¿Podría explicar por qué usa el evento 'Application.Idle' en lugar de usar' DataSourceUpdateMode' en el método 'DataBindings.Add (..)'? –
Puede crear una clase de enlace de comando genérica que permita vincular un comando a cualquier clase que herede de ButtonBase
.
public class CommandBinding<T> where T : ButtonBase
{
private T _invoker;
private ICommand _command;
public CommandBinding(T invoker, ICommand command)
{
_invoker = invoker;
_command = command;
_invoker.Enabled = _command.CanExecute(null);
_invoker.Click += delegate { _command.Execute(null); };
_command.CanExecuteChanged += delegate { _invoker.Enabled = _command.CanExecute(null); };
}
}
La unión luego se pueden configurar utilizando el siguiente código de comando:
CommandBinding<Button> cmdBinding =
new CommandBinding<Button>(btnCut, CutCommand);
esto es sólo el esqueleto de mi aplicación para darle un comienzo tan naturalmente hay algunas salvedades:
- El ejemplo asume el uso de la interfaz WPF
ICommand
, por lo que puede tener que modificarse si tiene su propia implementación del patrón de comando. - Los parámetros que se pasan deben revisarse para obtener referencias nulas.
- Una implementación más concreta debería tener algún método para eliminar los controladores de eventos para evitar pérdidas de memoria.
La restricción genérica también se puede cambiar a Control
que expone el evento Click
y la propiedad Enabled
que significa comandos se pueden unir a casi cualquier control.
button1.Click += (s, e) => new MyCommand().Execute();
Si desea enlazar el comando para el control mediante el diseñador, marque esta aplicación de demostración donde muestro cómo usar MVVM en Windows Forms:
https://bitbucket.org/lbras/mvvmforms
El único código que tiene escribir en el código subyacente es la creación de la instancia del modelo de vista.
- 1. VS2010 - WinForms - DataGridView - Enlace a DataSet
- 2. Comandos de enlace a ComboBoxItem en WPF
- 3. Enlace de datos de WinForms - Enlace a objetos en una lista
- 4. winForms + enlace DataGridView a una lista <T>
- 5. WPF: Enlace a comandos en el código detrás de
- 6. cmake, imprime comandos de compilación/enlace
- 7. Habilitar el enlace de comandos para TextBlock
- 8. WinForms enlace de datos con un botón Guardar?
- 9. WinForms: ¿alternativa a SplitContainer?
- 10. IDataErrorInfo en winforms
- 11. ¿Cómo paso los argumentos de la línea de comandos a una aplicación WinForms?
- 12. Etiquetas alineadas a la derecha en WinForms
- 13. Stop the Bell en CTRL-A (WinForms)
- 14. Migración desde Winforms a WPF
- 15. Obtener enlace (url) a un evento de calendario en la secuencia de comandos de google apps
- 16. Enlace jerárquico xml a vista de árbol
- 17. WPF MVVM - Enlace de comandos dentro de ItemsControl
- 18. ¿Cómo se utiliza un ValueConverter con enlace de datos en Winforms
- 19. ¿Cómo hacer un enlace de datos bidireccional usando EF en winforms?
- 20. WinForms WebBrowser Control: Obliga a todos los enlaces a abrirse externamente en una nueva ventana (IE)
- 21. vinculante en WinForms desplegable
- 22. MVP en Winforms
- 23. Visual Treemap en Winforms
- 24. Rutas relativas en Winforms
- 25. "Índice Z" en winforms
- 26. Ventana emergente en winforms
- 27. Animación simple en WinForms
- 28. Controles WPF en WinForms
- 29. Eventos WPF en Winforms
- 30. HTMLEncode en Winforms
Sí, en realidad eso es lo que estoy haciendo ahora, solo me preguntaba si había una forma razonable de hacerlo con el enlace. –