WPF:在命令参数MultiBinding中绑定自定义依赖项属性

时间:2015-08-28 13:15:19

标签: c# wpf xaml

我想将字典绑定到按钮的CommandParameter。 这是我的xaml代码:

<Button.CommandParameter>
    <MultiBinding Converter="{StaticResource MyMultiValueConverter}">
        <Binding>
            <Binding.Source>
                <system:String>SomeString</system:String>
            </Binding.Source>
        </Binding>
        <Binding>
            <Binding.Source>
                <models:StringObjectPair Key="UserId" Value="{Binding User.Id, UpdateSourceTrigger=PropertyChanged}" />
            </Binding.Source>
        </Binding>
        <Binding>
            <Binding.Source>
                <models:StringObjectPair Key="UserName" Value="{Binding User.Name, UpdateSourceTrigger=PropertyChanged}" />
            </Binding.Source>
        </Binding>
    </MultiBinding>
</Button.CommandParameter>

StringObjectPair类:

public class StringObjectPair : FrameworkElement
{
    public string Key { get; set; }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(StringObjectPair), new PropertyMetadata(defaultValue: null));

    public object Value
    {
        get { return GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
}

在MyMultiValueConverter中输入值[1] .Value和values [2] .Value属性我看到空值,但User.Id和User.Name不等于null。 在输出窗口中没有错误。 我怎么绑这个?

2 个答案:

答案 0 :(得分:0)

我找到了解决方案

StringObjectPair类的变化:

public class StringObjectPair
{
    public string Key { get; set; }

    public object Value { get; set; }
}

XAML的变化:

<Button.CommandParameter>
    <MultiBinding Converter="{StaticResource MyMultiValueConverter}">
        <Binding /><!--This is DataContext-->
        <Binding>
            <Binding.Source>
                <system:String>SomeString</system:String>
            </Binding.Source>
        </Binding>
        <Binding>
            <Binding.Source>
                <!--Just string in Value-->
                <models:StringObjectPair Key="UserId" Value="User.Id" />
            </Binding.Source>
        </Binding>
        <Binding>
            <Binding.Source>
                <models:StringObjectPair Key="UserName" Value="User.Name" />
            </Binding.Source>
        </Binding>
    </MultiBinding>
</Button.CommandParameter>

在MyMultiValueConverter中,我只是根据值得到属性:

var dataContext = values[0];
var someString = values[1] as string;
var parameters = new Dictionary<string, object>();
for (var i = 2; i < values.Length; i++)
{
    var pair = values[i] as StringObjectPair;
    if (!string.IsNullOrEmpty(pair?.Key) && !parameters.ContainsKey(pair.Key))
    {
        var props = (pair.Value as string)?.Split('.') ?? Enumerable.Empty<string>();
        var value = props.Aggregate(dataContext, (current, prop) => current?.GetType().GetProperty(prop)?.GetValue(current, null));
        parameters.Add(pair.Key, value);
    }
}

答案 1 :(得分:0)

首先,对于这种情况,使用将逻辑从转换器移动到视图模型要容易得多。应该可以从viewmodel访问该命令的所有输入。那么你根本不需要命令参数:

public class MainWindowViewModel 
{
    private User _user;
    public MainWindowViewModel()
    {
        MyCommand = new DelegateCommand(() =>
        {
            //Here you can access to User.Id, User.Name or anything from here
        });
    }

    public DelegateCommand MyCommand { get; private set; }

    public User User
    {
        get { return _user; }
        set
        {
            if (value == _user) return;
            _user = value;
            OnPropertyChanged();
        }
    }
}

其次,你原来绑定到User.Id没有用,因为你创建的StringObjectPairs不在VisualTree中,因此没有从父级继承的datacontext。但是,为什么不简化多重绑定:

<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
    <Binding Source="SomeString" />
    <Binding Path="User.Id" />
    <Binding Path="User.Name" />
</MultiBinding>
var someString = (string)values[0];
var userId = (int)(values[1] ?? 0);
var userName = (string)values[2];

我可以想象使用没有多重绑定的更简单的解决方案:

<Button Command="..."
        CommandParameter="{Binding User, Converter={StaticResource MyConverter}, 
                                         ConverterParameter=SomeString}" />
public class MyConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var user = (User)value;

        var someString = (string)paramenter;
        int userId = user.Id;
        string userName = user.Name;

        return ...
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

修改: 如果你坚持将键值对传递给命令(虽然我不认为它是一个好习惯)你可以像这样简化xaml:

<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
    <Binding Source="SomeString" ConverterParameter="SomeString" Converter="{StaticResource KeyValueConverter}" />
    <Binding Path="User.Id" ConverterParameter="Id" Converter="{StaticResource KeyValueConverter}"/>
    <Binding Path="User.Name" ConverterParameter="Name" Converter="{StaticResource KeyValueConverter}"/>
</MultiBinding>
public class KeyValueConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return new KeyValuePair<string, object>((string)parameter, value);
    }
}


public class DictionaryMultiConverter : IMultiValueConverter
{
    public object Convert(object[] keyValues, Type targetType, object parameter, CultureInfo culture)
    {
        return keyValues.Cast<KeyValuePair<string, object>>()
                        .ToDictionary(i => i.Key, i => i.Value);
    }
}