WPF:从ControlTemplate绑定到命令

时间:2010-10-06 09:18:07

标签: wpf binding command controltemplate

我正在尝试向自定义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。这按预期工作。为什么命令不能以相同的方式工作?有关如何使这项工作的任何线索?

2 个答案:

答案 0 :(得分:6)

首先应该为命令static设置包装属性并使用

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

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

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

*的 * UPDATE

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

public static RoutedCommand ShowColumnPickerCommand
{
    get; private set;
}

static MyListView()
{        
    ShowColumnPickerCommand = new RoutedCommand("ShowColumnPickerCommand", typeof(MyListView));
}

答案 1 :(得分:3)

我不确定这是否是正确的方法。在评论中阅读源代码有点困难,所以我把这个回复写成答案......

这是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}" />