【问题标题】:WPF: Bind to command from ControlTemplateWPF:从 ControlTemplate 绑定到命令
【发布时间】:2010-10-06 09:18:07
【问题描述】:

我正在尝试向自定义 ListView (MyListView) 添加一个按钮,该按钮触发在 MyListView 中定义的命令 (MyCustomCommand)。我通过应用 ControlTemplate 添加了按钮(和标题文本)。问题是我还没有找到在单击按钮时触发 MyCustomCommand 的方法。我最终想要实现的是打开一个 Popup 或 ContextMenu,我可以在其中选择哪些列应该在 ListView 中可见。

这是我的模板来源:

<Style TargetType="local:MyListView">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MyListView">
                <Border Name="Border" BorderThickness="1" BorderBrush="Black">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="30" />
                            <RowDefinition />
                        </Grid.RowDefinitions>

                        <Grid Background="LightSteelBlue">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Margin="3,3,3,3" Text="{TemplateBinding HeaderTitle}" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontSize="16" />
                            <Button Margin="3,3,3,3" Grid.Column="1" 
                                    VerticalAlignment="Center" HorizontalAlignment="Right" Height="20"
                                    Command="{TemplateBinding MyCustomCommand}">A button</Button>
                        </Grid>

                        <ScrollViewer Grid.Row="1" Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
                            <ItemsPresenter />
                        </ScrollViewer>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这是 MyListView 的定义:

public class MyListView : ListView
{
    public static readonly DependencyProperty MyCustomCommandProperty = 
        DependencyProperty.Register("MyCustomCommand", typeof(ICommand), typeof(MyListView));

    private static RoutedCommand myCustomCommand;

    public ICommand MyCustomCommand
    {
        get
        {
            if (myCustomCommand == null)
            {
                myCustomCommand = new RoutedCommand("MyCustomCommand", typeof(MyListView));

                var binding = new CommandBinding();
                binding.Command = myCustomCommand;
                binding.Executed += binding_Executed;

                CommandManager.RegisterClassCommandBinding(typeof(MyListView), binding);
            }
            return myCustomCommand;
        }
    }

    private static void binding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("Command Handled!");
    }


    public static readonly DependencyProperty HeaderTitleProperty =
        DependencyProperty.Register("HeaderTitle", typeof(string), typeof(MyListView));

    public string HeaderTitle { get; set; }
}

这里是创建一个简单的 MyListView 实例的 XAML:

<local:MyListView VerticalAlignment="Top" HeaderTitle="ListView title">
    <ListView.View>
        <GridView>
            <GridViewColumn Width="70" Header="Column 1" />
            <GridViewColumn Width="70" Header="Column 2" />
            <GridViewColumn Width="70" Header="Column 3" />
        </GridView>
    </ListView.View>

    <ListViewItem>1</ListViewItem>
    <ListViewItem>2</ListViewItem>
    <ListViewItem>1</ListViewItem>
    <ListViewItem>2</ListViewItem>
</local:MyListView>

注意 HeaderTitle 绑定到 MyListView 中的 DependencyProperty。这按预期工作。为什么它与命令的工作方式不同?有关如何使这项工作的任何线索?

【问题讨论】:

    标签: wpf binding command controltemplate


    【解决方案1】:

    您应该首先将命令的 wrapper 属性设为静态并使用

    Command={x:Static local:MyListView.MyCustomCommand}
    

    通常,如果命令在每个实例(如 Button)上设置为不同的值,或者类似于 ViewModel 上的 DelegateCommand/RelayCommand,您只需要 ICommand 属性。您还应该删除 getter 中的所有额外代码,改为内联或在静态构造函数中初始化命令,并在控件的实例构造函数中连接 CommandBinding。

    CommandBindings.Add(new CommandBinding(MyCustomCommand, binding_Executed));
    

    **更新

    RoutedCommand 本身应该被声明为静态的。 ICommand 实例属性适用于当您的控件的外部使用者传入要执行的命令时,这不是您想要的。这里也不需要 DP,并且您正在使用的 DP 声明不正确 - 为了可用,它们需要具有 GetValue/SetValue 的实例包装器属性。

    public static RoutedCommand ShowColumnPickerCommand
    {
        get; private set;
    }
    
    static MyListView()
    {        
        ShowColumnPickerCommand = new RoutedCommand("ShowColumnPickerCommand", typeof(MyListView));
    }
    

    【讨论】:

    • 非常感谢。这解决了我的情况:) 现在我可以在命令执行时打开一个弹出窗口。
    • 我遇到了一个新问题...触发命令的按钮仅在窗口中 MyListView 的第一个实例中可用(启用)。跟关键字Static in有什么关系吗:Command={x:Static local:MyListView.MyCustomCommand}
    • 当命令的 CanExecute 为 false 或命令没有附加执行处理程序时,带有命令的按钮将被禁用。确保 CanExecute 没有发生任何奇怪的事情,并且 CommandBinding 是在每个 ListView 实例上设置的,而不是在只会影响第一个实例的静态上下文中。
    • 再次感谢。我写了一个答案作为您最新评论的后续行动。
    • 阅读您的更新后,我将此行从公共构造函数移至静态构造函数: ShowColumnPickerCommand = new RoutedCommand("ShowColumnPickerCommand", typeof(MyListView));现在它完美地工作了。再次感谢:)
    【解决方案2】:

    我不确定这是否是正确的方法。在cmets中看源码有点难,所以写了这个回复作为答案...

    这里是MyListView的构造函数+命令绑定方法:

    public MyListView()
    {        
        showColumnPickerCommand = new RoutedCommand("ShowColumnPickerCommand", typeof(MyListView));
    
        var binding = new CommandBinding();
        binding.Command = showColumnPickerCommand;
        binding.Executed += ShowColumnPicker;
        binding.CanExecute += ShowColumnPickerCanExecute;
    
        CommandBindings.Add(binding);
    }
    
    private void ShowColumnPicker(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("Show column picker");          
    }
    
    private void ShowColumnPickerCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }
    

    绑定不是在静态上下文中设置的。唯一静态的东西是命令的 DependencyProperty 和命令本身:

    public static readonly DependencyProperty ShowColumnPickerCommandProperty =
        DependencyProperty.Register("ShowColumnPickerCommand", typeof(RoutedCommand), typeof(MyListView));
    
    private static RoutedCommand showColumnPickerCommand;
    
    public static RoutedCommand ShowColumnPickerCommand
    {
        get
        {
            return showColumnPickerCommand;
        }
    }
    

    该命令需要是静态的才能从 XAML 中绑定到它,如下所示:

    <Button Command="{x:Static local:MyListView.ShowColumnPickerCommand}" />
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 2016-05-07
      • 2013-09-16
      • 2019-06-17
      • 1970-01-01
      • 2012-02-23
      • 2011-04-04
      相关资源
      最近更新 更多