传递多个参数给 WPF 命令

6

我有以下层次结构:

abstract class TicketBase
{
    public DateTime PublishedDate { get; set; }
}

class TicketTypeA:TicketBase
{
     public string PropertyA { get; set; }
}   

class TicketTypeB:TicketBase
{
     public string PropertyB { get; set; }
}

在我的虚拟机中,我有一个 List<TicketBase> Tickets。当用户在我的应用程序上点击一个按钮时,他们希望看到某个属性的以前值的列表,例如:
<Button Tag="{x:Type Types:TicketTypeA}" 
        Command="{Binding ListHistoryCommand}"
        CommandParameter="{Binding Tag, RelativeSource={RelativeSource Self}}" />

正如您所看到的,我将我的Tag属性设置为TicketTypeA并将其作为参数传递给我的命令:

private void ListHistory(object o)
{
   if (Tickets.Count == 0)
       return;
   Type ty = o as Type;
   ValueHistory = new ObservableCollection<TicketBase>(GetTicketsOfType(ty).Select(t => t)); // <- Need to return t.PropertyA here, but dynamically
}

IEnumerable<TicketBase> GetTicketsOfType(Type type)
{
    if (!typeof(TicketBase).IsAssignableFrom(type))
        throw new ArgumentException("Parameter 'type' is not a TicketBase");
    return Tickets.Where(p => p.GetType() == type);
}

ValueHistory是另一个我在网格上设置为ItemsSource的集合)

但是我还需要传递属性名称,这样我就可以在网格中仅显示该属性,如下所示:

Published Time     |  PropertyA
===================================================
09:00              | <value of PropertyA at 09:00>
08:55              | <value of PropertyA at 08:55>

所以问题基本上是如何以最清晰的方式将属性名称作为另一个参数传递到我的命令中?

2个回答

13

查看这个问题
使用WPF绑定传递两个命令参数

更新
如果您需要在Button上存储类型和属性名称,您将不得不像您所说的那样使用一个附加属性。要将这两个参数传递给命令,可以尝试以下代码:

<Button Tag="{x:Type Types:TicketTypeA}"
        local:ParameterNameBehavior.ParameterName="{Binding Source='Parameter A'}"
        Command="{Binding ListHistoryCommand}">
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource PassThroughConverter}">
            <Binding Path="Tag" RelativeSource="{RelativeSource Self}"/>
            <Binding Path="(local:ParameterNameBehavior.ParameterName)"
                     RelativeSource="{RelativeSource Self}"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>

参数名称行为

public static class ParameterNameBehavior
{
    private static readonly DependencyProperty ParameterNameProperty = 
        DependencyProperty.RegisterAttached("ParameterName",
                                            typeof(string),
                                            typeof(ParameterNameBehavior));
    public static void SetParameterName(DependencyObject element, string value)
    {
        element.SetValue(ParameterNameProperty, value);
    }
    public static string GetParameterName(DependencyObject element)
    {
        return (string)element.GetValue(ParameterNameProperty);
    }
}
PassThroughConverter
public class PassThroughConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values.ToList();
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

@cjroebuck:啊,好的。所以你们两个都想将它存储并作为CommandParameter发送?在这种情况下,我认为你们必须像你说的那样使用一个Attach Property。 - Fredrik Hedblad
@cjroebuck:请查看我的更新答案。您是否有将属性存储在“Button”本身上的特殊原因,或者您认为类似这样的东西会起作用?也许我在这里漏掉了问题的某些部分 :) - Fredrik Hedblad
我有一个按钮数组,每个按钮对应不同类型的票务属性。每个按钮的内容都绑定到该属性的最新值。当用户点击其中一个按钮时,属性的历史值将在网格中列出。所以,是的,我认为按钮是存储属性的明显位置。你有更好的想法吗? - cjroebuck
@cjroebuck:没有,没有更好的想法 :) 只是没有完全理解你的问题背景。现在我明白你为什么需要那个了。 - Fredrik Hedblad
感谢您在这个问题上的帮助,Meleak。非常感激。 - cjroebuck
显示剩余3条评论

5

我通过在 Xaml 中使用 x:Name 属性,并将其作为 MultiBinding 与 Tag 一起传递给 CommandParameter,而无需采用 Attached Properties 的方式使其工作。从前端到后端:

在我的视图中:

 <Button Content="{Binding PropertyA}" x:Name="PropertyA" Tag="{x:Type Types:TicketTypeA}" Style="{StaticResource LinkButton}"/>

 <Button Content="{Binding PropertyB}" x:Name="PropertyB" Tag="{x:Type Types:TicketTypeB}" Style="{StaticResource LinkButton}"/>

每个按钮的样式:

 <Style x:Key="LinkButton" TargetType="Button">
        <Setter Property="Command" Value="{Binding DataContext.ListHistoryCommand, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />

        <Setter Property="CommandParameter">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource propertyConverter}">
                    <MultiBinding.Bindings>
                        <Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}"/>
                        <Binding Path="Name" RelativeSource="{RelativeSource Mode=Self}"/>
                    </MultiBinding.Bindings>
                </MultiBinding>
            </Setter.Value>
        </Setter>

在我的转换器中:
public class PropertyConverter : IMultiValueConverter
{
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            //Type t = values[0] as Type;
            //String propName = values[1] as string;

            Type t = values[0] as Type;
            if (t == null)
                return typeof(TicketBase);
            String s = values[1] as String;

            return new Tuple<Type,String>(t,s);
        }
}

在我的“视图模型”中:
private void ListHistory(object o)
    {
        if (Tickets.Count == 0)
            return;
        var tuple = o as Tuple<Type,String>;

        // Now write some code to dynamically select the propertyName (tuple.Item2) from the type (tuple.Item1)  

    }

我现在在我的命令中接收类型和属性名称。现在,我只需要在运行时编译lambda表达式以动态选择类型中的属性名称


你如何创建转换器类的实例,以便 Converter="{StaticResource propertyConverter}" 能够正常工作? - mins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接