Dynamically add button when loading viewmodel

Topics: Conventions, UI Architecture
Nov 22, 2012 at 4:05 PM

I'm fairly new to WPF so please bear with me.

I have a ShellView which contains a two-column Grid with a StackPanel in one column and a ContentControl in the second column. The intent being that the StackPanel will be a simple menu of Button's which control what is the ActiveItem of the ContentControl, so that it can switch between different views depending on which button is clicked. Nice and simple.

Is there a way in which I can dynamically register a Button in the StackPanel for a given view model? One in which I can avoid statically wiring the properties on the ShellViewModel for each button. (e.g. dynamically loading from a plugin assembly via IoC).

Nov 23, 2012 at 12:08 PM

I've come up with an approach that works. Whether it is the 'right' way to do this I don't know. I'm using Structuremap, as it's the container which I'm most familiar with.

x.For<ShellViewModel>().Use<ShellViewModel>()
	.OnCreation((ctx, v) =>
		{
			var screens = ctx.GetAllInstances<Screen>();
			v.Items.AddRange(screens);
			v.ActivateItem(ctx.GetInstance<LoginViewModel>());

			var shellView =
				(ShellView)
				ViewLocator.LocateForModelType(typeof(ShellViewModel), null, null);
			
			foreach(var screen in screens)
			{
				var btn = new Button
							  {
								  Content = screen.DisplayName,
								  Height = 20
							  };
				var screen1 = screen;
				btn.Click += (sender, args) => v.ActivateItem(screen1);
				shellView.spMenu.Children.Add(btn);
			}

			ViewModelBinder.Bind(v, shellView, null);
		});

I'm happy that what I've come up with works, but if there is a better way to do this then I would like to know.

Nov 26, 2012 at 4:21 PM

So I discovered that there is a much simpler way of doing this:

<StackPanel>
	<ItemsControl x:Name="Items">
		<ItemsControl.ItemTemplate>
			<DataTemplate >
				<Button Content="{Binding DisplayName}"
						cal:Message.Attach="[Action ActivateItem($this)"/>
			</DataTemplate>
		</ItemsControl.ItemTemplate>
	</ItemsControl>
</StackPanel>
<ContentControl x:Name="ActiveItem" Grid.Column="1" />

Is there a way to dynamically bind to a property on the ViewModel to determine if the button should be active?

I tried IsEnabled="{Binding MyBoolProperty}", but it doesn't seem to work?

Any suggestions?

Nov 27, 2012 at 1:32 AM

The execute and canexecute model (builtin CM) allows for this so all you need to do is create a property called CanActivateItem of type bool and do some logic in the getter to cause the button to enable disable without to much magic in the xaml space.

Nov 27, 2012 at 8:21 AM

That's it exactly. I didn't realise I could use the canexecute model with ActivateItem. Thanks.