In my application, I have quite a few grids so I was working on creating a user control that has all of the possible buttons we would use with a grid (Add, Remove, Insert, Move Up, Move Down, etc). I use dependency properties to set the visibility and state (enabled or disabled) of the buttons and I use routed events for the click events.
I'm using Caliburn.Micro in my project so I'm using Message.Attach to specify multiple events and the actions they are attached to. On their own, these work without issue.
The problem I have is when the actions have Can* properties to go with them. So for example, I have Add and Remove methods and CanAdd and CanRemove properties to set their state. If CanAdd or CanRemove is false (and the other is true), both buttons get disabled (probably the whole user control is getting disabled).
I can get around this issue by using properties for the states that don't conform to the Caliburn.Micro naming (ex. CanAddRecord and CanRemoveRecord instead of CanAdd and CanRemove) so it doesn't automatically use them and then set the binding in each of the dependency properties. I would prefer not to do this but will if it's in the only way.
I created a simple example project to show this. The project was written in VS 2015 and uses .NET 4.5.2. I'm using Caliburn.Micro v3.1.0.
The application has a main window with the user control on the top. The user control has Add and Remove buttons. Under the user control is a label that shows a count. The Add button increments the count and the Remove button decrements the count. The Add button is always enabled. The Remove button is only enabled if the count is greater than 0 (so you can't set the count to a negative). The count starts at 1 so both buttons start enabled. If you click Add, both buttons stay enabled. If you click Remove till the count gets to 0, both buttons end up disabled.
Is this by design because the messages are attached to the user control as opposed to the specific buttons? Is there a way around this?
I originally tried putting the Message.Attach statements inside of the user control but when clicking the buttons it seemed to want to bind to Add and Remove methods inside of the user control.
App.xaml
<Application
x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:AppBootstrapper x:Key="Bootstrapper" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
AppBootStrapper.cs
using System.Windows;
using Caliburn.Micro;
namespace WpfApplication1
{
public class AppBootstrapper : BootstrapperBase
{
public AppBootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainViewModel>();
}
}
}
GridButtonBar.xaml
<UserControl
x:Class="WpfApplication1.GridButtonBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="40" d:DesignWidth="230">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
x:Name="AddButton"
Content="Add"
Grid.Column="0"
Margin="3"
IsEnabled="{Binding AddButtonIsEnabled}"
Click="Add_OnClick" />
<Button
x:Name="RemoveButton"
Content="Remove"
Grid.Column="1"
Margin="3"
IsEnabled="{Binding RemoveButtonIsEnabled}"
Click="Remove_OnClick" />
</Grid>
</UserControl>
GridButtonBar.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class GridButtonBar : UserControl
{
public static readonly DependencyProperty AddButtonIsEnabledProperty =
DependencyProperty.Register(nameof(AddButtonIsEnabled), typeof(bool), typeof(GridButtonBar),
new UIPropertyMetadata(true));
public static readonly DependencyProperty RemoveButtonIsEnabledProperty =
DependencyProperty.Register(nameof(RemoveButtonIsEnabled), typeof(bool), typeof(GridButtonBar),
new UIPropertyMetadata(true));
public static readonly RoutedEvent AddButtonClickedEvent = EventManager.RegisterRoutedEvent("AddButtonClicked",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridButtonBar));
public static readonly RoutedEvent RemoveButtonClickedEvent =
EventManager.RegisterRoutedEvent("RemoveButtonClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler),
typeof(GridButtonBar));
public GridButtonBar()
{
InitializeComponent();
}
public bool AddButtonIsEnabled
{
get { return (bool) GetValue(AddButtonIsEnabledProperty); }
set { SetValue(AddButtonIsEnabledProperty, value); }
}
public bool RemoveButtonIsEnabled
{
get { return (bool) GetValue(RemoveButtonIsEnabledProperty); }
set { SetValue(RemoveButtonIsEnabledProperty, value); }
}
public event RoutedEventHandler AddButtonClicked
{
add { AddHandler(AddButtonClickedEvent, value); }
remove { RemoveHandler(AddButtonClickedEvent, value); }
}
public event RoutedEventHandler RemoveButtonClicked
{
add { AddHandler(RemoveButtonClickedEvent, value); }
remove { RemoveHandler(RemoveButtonClickedEvent, value); }
}
private void Add_OnClick(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(AddButtonClickedEvent));
}
private void Remove_OnClick(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(RemoveButtonClickedEvent));
}
}
}
MainView.xaml
<Window
x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
Title="{Binding WindowTitle}"
WindowStyle="SingleBorderWindow"
ResizeMode="NoResize"
Height="140" Width="250">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<local:GridButtonBar
Grid.Row="0"
cal:Message.Attach="[Event AddButtonClicked] = [Action Add];[Event RemoveButtonClicked] = [Action Remove]" />
<Label
Grid.Row="1"
Margin="5"
BorderBrush="Black"
BorderThickness="1"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="{Binding Count}" />
</Grid>
</Window>
MainViewModel.cs
using Caliburn.Micro;
namespace WpfApplication1
{
public class MainViewModel : PropertyChangedBase
{
private const string WindowTitleDefault = "Caliburn Micro Test";
private int _count = 1;
private string _windowTitle = WindowTitleDefault;
public string WindowTitle
{
get { return _windowTitle; }
set
{
_windowTitle = value;
NotifyOfPropertyChange(() => WindowTitle);
}
}
public int Count
{
get { return _count; }
set
{
_count = value;
NotifyOfPropertyChange(() => Count);
}
}
public bool CanAdd => true;
public bool CanRemove => Count > 0;
public void Add()
{
Count++;
}
public void Remove()
{
Count--;
NotifyOfPropertyChange(() => CanRemove);
}
}
}