Reputation: 1743
I need to create a class that is able to create dynamic commands where I can bind to. I have a highly dynamic application with plugins, where the developer creates the plugin only in the backend and controls all things from the backend. The frontend is created in a different "remote" application that has the backend as the DataContext. Here's an example (not the full example), only to show what I want to do:
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Command="{Binding PressMe}">PressMe</Button>
</Grid>
</Window>
namespace WpfApplication7
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
TaskViewModel Model = new TaskViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = Model;
Model.AddCommand("PressMe", (o) => { Console.WriteLine("TEST"); });
}
}
}
So the question is, how do I create something like this, if the XAML needs "well-defined" or "known" properties on which the binding can look at.
I thought of some TaskViewModel and implements IExpando, but somehow the reflection is not performed when hooking into the IExpando methods. Are there other ways to do something like this.
Thanks Martin
Upvotes: 1
Views: 1416
Reputation: 44048
As always, Items-based UIs in WPF are implemented using an ItemsControl
, and leveraging Data Templating, regardless of the visual appearance intended for each item, or the actual behavior of the underlying classes.
In this case, you're looking for a dynamic collection of Commands:
// This class is not fully implemented. Replace by your own DelegateCommand or
// Grab an ICommand implementation from any of the well known MVVM Frameworks out there.
// This only exists for the sake of the example.
public class Command : ICommand
{
private readonly Action action;
public string DisplayName { get; private set; }
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
action();
}
public Command(string displayName, Action action)
{
this.action = action;
this.DisplayName = displayName;
}
}
ViewModel:
public class ViewModel
{
public ObservableCollection<Command> Commands { get; private set; }
public ViewModel()
{
Commands = new ObservableCollection<Command>();
}
// You will add commands at some point at runtime.
public void AddSomeCommands()
{
Commands.Add(new Command("Command1", () => MessageBox.Show("This is Command1!")));
Commands.Add(new Command("Command2", () => MessageBox.Show("This is Command2!!")));
Commands.Add(new Command("Command3", () => MessageBox.Show("This is Command3!!!")));
Commands.Add(new Command("Command4", () => MessageBox.Show("This is Command4!!!!")));
}
}
Then you can create an ItemsControl which shows Button
s for each Command item:
<Window x:Class="WpfApplication22.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ItemsControl ItemsSource="{Binding Commands}"
VerticalAlignment="Top">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding}"
Content="{Binding DisplayName}"
Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.AddSomeCommands();
this.DataContext = vm;
}
}
Result:
Edit:
If you need to bind these commands to hotkeys, simply add a property to the Command:
public Key HotKey { get; set; }
and then add something like this in code behind:
foreach (var c in vm.Commands)
this.InputBindings.Add(new KeyBinding(c, c.HotKey, ModifierKeys.None));
Upvotes: 2