我有以下层次结构:
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>
所以问题基本上是什么是将属性名称作为另一个参数传入我的命令的最简洁方法?
答案 0 :(得分:12)
看到这个问题
Passing two command parameters using a WPF binding
<强>更新强>
如果您需要在Button
上存储类型和属性名称,则必须使用您所说的附加属性。要将这两个参数传递给Command,这样的东西应该可以工作
<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>
<强> ParameterNameBehavior 强>
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();
}
}
答案 1 :(得分:5)
我通过使用Xaml中的x:Name
属性,然后将其作为MultiBinding传递给我的CommandParameter以及Tag,从而无需借助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)
}
我现在在命令中收到Type和PropertyName。现在,我只需要在运行时to dynamically Select the PropertyName from the Type编译一个lambda表达式。