Sinatr
Sinatr

Reputation: 21998

How to get in ConvertBack parameters from Convert?

The problem:

I am using MultiBinding with converter to pass (x,y) coordinates into method.

And I can't make it working in back direction:

public class MyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var x = (int)values[0];
        var y = (int)values[1];
        return Model.Get(x, y);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        Model.Set(x, y, value); // how to get x, y here?
        return new object[] { Binding.DoNothing, Binding.DoNothing };
    }
}

Additional info:

The data will be visualized in a form of table. Here is cell template:

<TextBox>
    <TextBox.Text>
        <MultiBinding Converter="{StaticResource converter}" Mode="TwoWay">
            <Binding Path="X" Mode="OneWay" />
            <Binding Path="Y" Mode="OneWay" RelativeSource="..." />
        </MultiBinding>
    </TextBox.Text>
</TextBox>

The idea is to use converter, which receive x (from cell view model) and y (from parent column view model, notice RelativeSource) and calls Get(x,y) to display value.

However, when user entered something, ConvertBack is called and I need to call Set(x, y, value) method.

How do I pass x and y into ConvertBack?

Upvotes: 3

Views: 4251

Answers (3)

Sinatr
Sinatr

Reputation: 21998

It seems hard to pass parameters into ConvertBack. It might be possible, but there is a workaround, which makes ConvertBack unnecessary. Thanks to @XAMlMAX for an idea.

One possibility to achieve it (there could be a better way) is to use data templates. Instead of multi-binding TextBlock.Text with string we can bind ContentControl.Content with some viewmodel, and this viewmodel should do the rest, including Set(x, y, value) call.

Here is code:

public class ViewModel
{
    public int X { get; set; }
    public int Y { get; set; }

    string _text;
    public string Text
    {
        get { return _text; }
        set
        {
            // this should be only called by the view
            _text = value;
            Model.Set(X, Y, value);
        }
    }

    public ViewModel(string text)
    {
        _text = text;
    }
}

public class MyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var x = (int)values[0];
        var y = (int)values[1];
        return new ViewModel(Model.Get(x, y)) { X = x, Y = y };
    }

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

and the xaml will become

<ContentControl Focusable="False">
    <ContentControl.Content>
        <MultiBinding Converter="{StaticResource converter}">
            <Binding Path="X" />
            <Binding Path="Y" RelativeSource="..."/>
        </MultiBinding>
    </ContentControl.Content>
</ContentControl>

where data template is

<DataTemplate DataType="{x:Type local:ViewModel}">
    <TextBox Text="{Binding Text}" />
</DataTemplate>

Upvotes: 0

grek40
grek40

Reputation: 13458

There might be more-or-less dirty workarounds to get such a multivalue converter working. But I'd suggest you keep your multivalue converter one-way, but return a container object that wraps the actual text property.

Instead of directly binding to the TextBox.Text property, bind to some other property (eg. DataContext or Tag) and then bind the text to the container value.

Small example:

<TextBox Text="{Binding Value}">
    <TextBox.DataContext>
        <MultiBinding Converter="{StaticResource cMyConverter}">
            <Binding Path="X"/>
            <Binding Path="Y"/>
        </MultiBinding>
    </TextBox.DataContext>
</TextBox>

With container and converter:

public class ValueProxy
{
    public int X { get; set; }
    public int Y { get; set; }

    public string Value
    {
        get { return Model.Get(X, Y); }
        set { Model.Set(X, Y, value); }
    }
}
public class MyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var x = (int)values[0];
        var y = (int)values[1];
        return new ValueProxy { X = x, Y = y };
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return new object[] { Binding.DoNothing, Binding.DoNothing };
    }
}

Upvotes: 6

Daniel Marques
Daniel Marques

Reputation: 703

The short answer is that you can't directly get the values of xand y inside your ConvertBack method. The IMultiValueConverter Convert multiple values into a single value. So, the ConvertBack method will do the opposite: convert a single value into multiple values.

It all depends on what your Model.Get(x, y) method returns. It needs to return a value that is unique enough for you to get the separate values of x and y from it.

Example: create unique strings for each pair of (x,y).

Upvotes: 0

Related Questions