2

I have this problems where I have all these CommandBindings defined in the MainWindow, and said commands are available within that window to be used in any Button or MenuItem. The problem is that if in other windows the command binding is not available (it's always false), even if the new Window's owner is the MainWindow.

You can see the problem here.

Any help really appreciated.

Here's the code.

XAML MainWindow:

<Window x:Class="ContextMenuDialogProblem.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContextMenuDialogProblem"
        Title="MainWindow" Height="350" Width="525"
        FocusManager.FocusedElement="{Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneTime}">
    <Window.CommandBindings>
        <CommandBinding Command="local:LocalCommandManager.ShowDialogCommand" CanExecute="CanExecuteShowDialogCommand" Executed="ShowDialogCommandExecuted" />
    </Window.CommandBindings>
    <Window.ContextMenu>
        <ContextMenu>
            <MenuItem Command="local:LocalCommandManager.ShowDialogCommand" />
        </ContextMenu>
    </Window.ContextMenu>
    <Grid Background="Red">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button Grid.Row="0"
                Content="Open SubWindow"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Padding="6"
                Click="Button_Click" />
        <Button Grid.Row="1"
                Content="Show Dialog Command Test"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Padding="6"
                Command="local:LocalCommandManager.ShowDialogCommand" />
    </Grid>
</Window>

CS MainWindow:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void CanExecuteShowDialogCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void ShowDialogCommandExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("Show Dialog");   
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Window wnd = new SubWindow() { Owner = this };
        wnd.Show();
    }
}

CS LocalCommandManager:

public static class LocalCommandManager
{
    private static object syncRoot = new object();

    private static RoutedUICommand _showDialogCommand;
    public static RoutedUICommand ShowDialogCommand
    {
        get
        {
            lock (syncRoot)
            {
                if (_showDialogCommand == null)
                    _showDialogCommand = new RoutedUICommand("Show Dialog", "ShowDialogCommand", typeof(LocalCommandManager));
                return _showDialogCommand;
            }
        }
    }
}

XAML SubWindow:

<Window x:Class="ContextMenuDialogProblem.SubWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:ContextMenuDialogProblem"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SubWindow" Height="300" Width="300">
    <Grid>
        <Button Command="local:LocalCommandManager.ShowDialogCommand" Content="Show Dialog" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="6" />
    </Grid>
</Window>
AusCBloke
  • 18,014
  • 6
  • 40
  • 44
Carlo
  • 25,602
  • 32
  • 128
  • 176

3 Answers3

3

The scope of CommandBindings is limited to the element where it's defined, so this behavior is perfectly normal. You have to add the CommandBinding to SubWindow if you want to use it there.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Hmmm I was afraid that was the problem... do you know of any workaround for this? – Carlo Oct 24 '11 at 23:20
  • 2
    Declaring the CommandBinding in the SubWindow *is* the workaround ;) – Thomas Levesque Oct 24 '11 at 23:22
  • Or you could programmatically add the CommandBinding from the main window to the child window before you call Show – Thomas Levesque Oct 24 '11 at 23:23
  • haha no, that's the hard solution =(, ok I'll give it a shot, the problem is that the command bindings are fairly complex (not too much though), thanks! – Carlo Oct 24 '11 at 23:23
  • I like the second one better =). I'll give both a shot. Thanks! – Carlo Oct 24 '11 at 23:24
  • Yeah, I found some really old code where we had this problem and we solved it this way too. Thanks! – Carlo Oct 25 '11 at 00:00
  • The first solution didn't work under my scenario. Do you have an example of your second solution? "Or you could programmatically add the CommandBinding from the main window to the child window before you call Show" – Carlo Oct 25 '11 at 01:59
1
<StackPanel Background="Transparent">
    <StackPanel.ContextMenu>
        <ContextMenu ItemsSource="{Binding Path=AnotherWindow.CommandBindings}">
            <ContextMenu.ItemContainerStyle>
                <Style TargetType="{x:Type MenuItem}">
                    <Setter Property="Header" Value="{Binding Path=Command.Name}" />
                    <Setter Property="Command">
                        <Setter.Value>
                            <MultiBinding Converter="{StaticResource commandConverter}">
                                <Binding />
                                <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}" />
                            </MultiBinding>


public class CommandConverter : IMultiValueConverter
{
    public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var cb = value[0] as CommandBinding;
        var cm = value[1] as ContextMenu;

        if(cb == null || cm == null)
            return null;

        cm.CommandBindings.Add(cb);
        return cb.Command;
    }

    public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

work fine on .net 4.0

ARSolog
  • 11
  • 2
1

If you want to add a command for all windows this is the solution:

    public partial class App : Application
    {
        public App()
        {
            var binding = new CommandBinding(MyCommands.DoSomethingCommand, DoSomething, CanDoSomething);

            // Register CommandBinding for all windows.
            CommandManager.RegisterClassCommandBinding(typeof(Window), binding);
        }

        private void DoSomething(object sender, ExecutedRoutedEventArgs e)
        {
            ...
        }

        private void CanDoSomething(object sender, CanExecuteRoutedEventArgs e)
        {
            ...
            e.CanExecute = true;
        }
    }
afruzan
  • 1,454
  • 19
  • 20