【问题标题】:How to bind a command to StackPanel or Grid tap event?如何将命令绑定到 StackPanel 或 Grid 点击事件?
【发布时间】:2015-05-10 10:57:41
【问题描述】:

我已经看到一些关于 WP8 或其他的答案,但似乎 WP8.1 中没有触发器(或者我遗漏了什么?)

我有一个从代码绑定的数据模板(它是一个集线器数据模板,我混合了静态和动态集线器部分,因此需要从代码中设置此数据模板)。

此数据模板在单独的 xaml 文件中定义,它包括一个列表框(或列表视图)以及为项目定义的另一个数据模板。

我需要在项目的点击或列表框选择更改(或等效的东西)上绑定命令。但是,模板中定义的点击事件没有被调用,因此我想到了在 UI 元素上绑定命令,但是这些似乎不支持命令和交互触发器。

关于如何处理的任何线索? :)

在下面的示例中,我没有收到事件 Item_Tapped 或 ListBox_SelectionChanged,无论如何我更愿意将其中一个绑定到视图模型中的命令。

<DataTemplate x:Key="HubSectionTemplate">
    <Grid>
        <ListBox ItemsSource="{Binding MyNodes}"
            SelectionChanged="ListBox_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Height="64" Tapped="Item_Tapped" >
                        <TextBlock Text="{Binding MyText}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</DataTemplate>

这是从代码中使用它的方式:

HubSection hs = new HubSection()
{
    ContentTemplate = Application.Current.Resources[HUBSECTION_TEMPLATE] as DataTemplate,
    DataContext = model,
    Tag = model.UniqueId,
};
Hub.Sections.Insert(firstSectIdx + 1, hs);


public class Model
{
    public Guid UniqueId {get;set;}
    public List<ItemModel> MyNodes {get;set;}
}

public class ItemModel
{
    public string MyText {get;set;}
}

PS:ItemModel 是在另一个程序集中定义的,因此不应编辑(如果可能,该命令应在 Model 类中)

--- 编辑 ---

为了简化问题,我使用以下模型:

public class Model
{
    public Guid UniqueId {get;set;}
    public List<ItemModel> MyNodes {get;set;}
    public ICommand MyCommand {get;set;}
}

public class ItemModel
{
    Model _Model;
    public ItemModel(Model m) {_Model = m; }
    public string MyText {get;set;}
    public ICommand MyCommand { get { return _Model.MyCommand; }}
}

而我的(临时)解决方案是在 itemtemplate 中使用一个按钮:

    <ListView.ItemTemplate>
        <DataTemplate>
            <Button HorizontalAlignment="Stretch"  Command="{Binding TapCommand}" Height="64">
                <TextBlock Text="{Binding MyText}" />
            </Button>
        </DataTemplate>
    </ListView.ItemTemplate>

【问题讨论】:

  • 不直接使用 StackPanel,将其包装在样式按钮中怎么样?这样,您就拥有了可用于拦截点击的 Button Command 绑定。另一件事,您要求点击项目或更改选择。请注意,两者的行为不同,如果您选择两次相同的项目,点击会触发,但更改的选择不会。
  • 关于行为这并不重要,因为我正在构建类似文件资源管理器的东西,因此如果点击该项目,应该会发生一些事情并且不能发生两次。如果我没有找到任何其他选项,我将使用按钮结束,但我真的不喜欢在不应该是一个按钮时使用它。
  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • 我同意“一般”,但这是一个非常具体的问题,因为我已经能够在其他 XAML 平台上处理这个问题,但在 WP8.1 上却没有。这就是为什么我在标题中指定它,因为它很重要。

标签: c# xaml windows-phone-8.1


【解决方案1】:

您可以使用行为 SDK
在 Visual Studio 中,转到“工具 -> 扩展和更新”并安装行为 SDK (XAML)。然后使用添加引用对话框在您的项目中引用它。
之后将以下命名空间添加到您的页面:

xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"

现在您可以使用以下语法注册事件,例如点击堆栈面板:

<DataTemplate>
    <StackPanel Orientation="Horizontal" Height="64">
        <TextBlock Text="{Binding MyText}" />
        <interactivity:Interaction.Behaviors>
            <core:EventTriggerBehavior EventName="Tapped">
                <core:InvokeCommandAction  Command="{Binding YourCommand}"/>
            </core:EventTriggerBehavior>
        </interactivity:Interaction.Behaviors>
    </StackPanel>
</DataTemplate>

但是,此代码仅在您的 Command 在您的 ItemModel 类中定义时才有效。如果你想绑定到父元素Command,你可以试试这样的(未测试):

{Binding ElementName=LayoutRoot, Path=DataContext.ParentCommand}

但我更希望在您的 ItemModel 类上使用命令


编辑: 没有 Behaviors SDK 的解决方案:
如果您使用ListView(或从ListViewBase 继承的东西),您可以使用ItemClick 事件。为了使其更可重用和对 Mvvm 更友好,您可以像这样实现 DependencyProperty:

public static class ItemClickCommand
{
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached("Command", typeof(ICommand),
        typeof(ItemClickCommand), new PropertyMetadata(null, OnCommandPropertyChanged));

    public static void SetCommand(DependencyObject d, ICommand value)
    {
        d.SetValue(CommandProperty, value);
    }

    public static ICommand GetCommand(DependencyObject d)
    {
        return (ICommand)d.GetValue(CommandProperty);
    }

    private static void OnCommandPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var control = d as ListViewBase;
        if (control != null)
        {
            control.ItemClick += OnItemClick;                
        }

    }

    private static void OnItemClick(object sender, ItemClickEventArgs e)
    {
        var control = sender as ListViewBase;
        var command = GetCommand(control);

        if (command != null && command.CanExecute(e.ClickedItem))
        {
            command.Execute(e.ClickedItem);
        }
    }
}

那么您的 ListView 将如下所示:

<ListView 
    IsItemClickEnabled="True" 
    helpers:ItemClickCommand.Command="{Binding YourCommand}"
    ItemsSource="{Binding MyNodes}"
    ItemTemplate="{StaticResource YourDataTemplate}" />

在这种情况下,您的子项作为参数传递给您的命令,因此它还应该解决您在父模型中定义的命令的问题。

【讨论】:

  • 这就是我要找的东西,但是我找不到。我正在使用 Visual Studio 2015 RC,这可能是它不能作为扩展使用的原因?
  • 我使用的是 VS 2013,所以我不知道是什么导致了这个问题。我发布了另一个没有 Behaviors SDK 的解决方案。希望对您有所帮助。
  • 我看到了,我还没有时间尝试,但是它看起来是一个不错的解决方案,而且或多或少是我正在寻找的解决方案,谢谢! :)
  • 由于我不明白的原因,ItemClickCommand 注册到事件,但在应该引发事件时不会触发(永远不会调用 OnItemClick)。
  • 因此,我的临时解决方案是一个带有封装内容的按钮的 itemtemplate,绑定到指向 ItemModel 的命令,它本身就是来自集合模型的重定向。 public class Model { public Guid UniqueId {get;set;} public List&lt;ItemModel&gt; MyNodes {get;set;} public ICommand MyCommand {get;set;} } public class ItemModel { Model _Model; public ItemModel(Model m) {_Model = m; } public string MyText {get;set;} public ICommand MyCommand { get { return _Model.MyCommand; }} }
猜你喜欢
  • 2010-10-30
  • 1970-01-01
  • 2011-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-01
  • 2016-10-18
  • 1970-01-01
相关资源
最近更新 更多