Командные привязки WPF, определенные в одном окне, недоступны в другом

#c# #wpf #commandbinding

#c# #wpf #привязка команд

Вопрос:

У меня возникают проблемы, когда у меня есть все эти привязки команд, определенные в главном окне, и указанные команды доступны в этом окне для использования в любой кнопке или элементе меню. Проблема в том, что если в других окнах привязка команд недоступна (это всегда false), даже если владельцем нового окна является MainWindow.

Вы можете увидеть проблему здесь.

Любая помощь действительно ценится.

Вот код.

Главное окно XAML:

 <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:

 <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>
 

Ответ №1:

Область действия CommandBindings ограничена элементом, в котором она определена, поэтому такое поведение совершенно нормально. Вы должны добавить CommandBinding to SubWindow , если хотите использовать его там.

Комментарии:

1. Хммм, я боялся, что это проблема… знаете ли вы какой-либо обходной путь для этого?

2. Объявление привязки команд в подокне является обходным решением 😉

3. Или вы можете программно добавить привязку команд из главного окна в дочернее окно перед вызовом Show

4. ха-ха, нет, это сложное решение = (, хорошо, я попробую, проблема в том, что привязки команд довольно сложные (хотя и не слишком), спасибо!

5. Мне больше нравится второй =). Я попробую оба варианта. Спасибо!

Ответ №2:

 <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();
    }
}
 

отлично работают в .net 4.0

Ответ №3:

Если вы хотите добавить команду для всех окон, это решение:

     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;
        }
    }