1

XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Persons}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Id}" />
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTemplateColumn Header="Name1" CellEditingTemplate ="{Binding Path=Cars, Converter={StaticResource myConverter}}" />
    </DataGrid.Columns>
</DataGrid>

Converter:

public class myConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is ObservableCollection<Car> test)
        {
            //create the data template
            DataTemplate cardLayout = new DataTemplate();

            foreach (var t in test)
            {
                //set up the stack panel
                FrameworkElementFactory spFactory = new FrameworkElementFactory(typeof(StackPanel));
                spFactory.Name = "myComboFactory";
                spFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

                //set up the textblock
                FrameworkElementFactory childFactory;
                childFactory = new FrameworkElementFactory(typeof(TextBlock));
                childFactory.SetValue(TextBlock.TextProperty, t.Color);
                spFactory.AppendChild(childFactory);

                //set up the textblock
                childFactory = new FrameworkElementFactory(typeof(TextBlock));
                childFactory.SetValue(TextBlock.TextProperty, t.Speed);
                spFactory.AppendChild(childFactory);

                //set the visual tree of the data template
                cardLayout.VisualTree = spFactory;
            }

            return cardLayout;
        }

        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        //Will do.
        return DependencyProperty.UnsetValue;
    }
}

C#:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ObservableCollection<Car> Cars { get; set; }
}

public class Car
{
    public string Color { get; set; }
    public int Speed { get; set; }
}

public class ViewModelsWindow2
{
    public ObservableCollection<Person> Persons { get; }

    public ViewModelsWindow2()
    {
        Persons = new ObservableCollection<Person>();
        Persons.Add(new Person() { Id = 177, Name = "John", Cars = new ObservableCollection<Car>() { new Car() { Color = "Green", Speed = 360 } } });
    }
}

I want to create own DataGridTemplateColumn with some dynamic TextBoxes (at example two TextBlocks). I want to do it over converter because I need twoways of DataGridTemplateColumn. I haven't still written code for ConvertBack. Number of TextBoxes will be different at each row.

But I have one problem. The converter doesn't execute by WPF. I set up the breakpoint at the begin of Convert and the code has not stopped.

Please, help me to decide this question. What do I do wrong?

It's like this. It's static but I want dynamic.

I will perhaps add properties to Car and the person perhaps has two, three... cars with different colour and speed.

And the data will edit by user.

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Persons}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Id}" />
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTemplateColumn Header="Name1" CellEditingTemplate ="{Binding Path=Cars, Converter={StaticResource myConverter}}">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBox Text="Green"/>
                                <TextBox Text="360"/>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBox Text="Red"/>
                                <TextBox Text="360"/>
                                ...
                            </StackPanel>
                            ...
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

Update:

I define Converter as:

xmlns:vm="clr-namespace:WpfApp1.ViewModels"

...

<Window.Resources>
    <ResourceDictionary>
        <vm:myConverter x:Key="myConverter" />
    </ResourceDictionary>
</Window.Resources>
<Window.DataContext>
    <vm:ViewModelsWindow2 />
</Window.DataContext>
Pro
  • 673
  • 2
  • 6
  • 15
  • 2
    On a different thought - Why don't you use `CellEditingTemplateSelector`? See https://msdn.microsoft.com/en-us/library/system.windows.controls.datagridtemplatecolumn.celleditingtemplateselector(v=vs.110).aspx. – user1672994 Jul 04 '18 at 09:56
  • How have you defined `myConverter` key in your XAML? – user1672994 Jul 04 '18 at 09:58
  • Try `CellEditingTemplate ="{Binding Path=., Converter={StaticResource myConverter}}"` and see whether your `converter` is getting called. – dhilmathy Jul 04 '18 at 10:02
  • @user1672994 I updated the post. – Pro Jul 04 '18 at 10:24
  • This answer claims that you can't edit the column template directly and that you can template the cells instead: https://stackoverflow.com/questions/10361122/create-common-datagridtemplatecolumn-in-wpf – o_weisman Jul 04 '18 at 10:42
  • You can't bind to the CellEditingTemplate like this. What exaclty are you trying to do here? Where is the Cars property defined? – mm8 Jul 04 '18 at 12:10
  • @mm8 I updated the post again I had written more precisely what I want. – Pro Jul 05 '18 at 01:11
  • @Kirill: Is `Cars` a property of `Person`? – mm8 Jul 05 '18 at 13:59
  • @mm8 Yeap, it has ObservableCollection type. – Pro Jul 05 '18 at 19:47
  • @mm8 It may be a field if you are asking about it. – Pro Jul 05 '18 at 20:13
  • @Kirill: You should use a CellEditingTemplateSelector instead of a converter. See my answer. – mm8 Jul 06 '18 at 08:24

1 Answers1

1

You can't bind to the CellEditingTemplate property like this. A DataGridTemplateColumn is not a FrameworkElement and thus it doesn't have any DataContext to bind to.

What you should do is to use a CellEditingTemplateSelector:

public class Selector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        Person person = item as Person;
        if(person != null && person.Cars != null)
        {
            DataTemplate cardLayout = new DataTemplate();

            foreach (Car car in person.Cars)
            {
                //build your template..
            }

            return cardLayout;
        }

        return null;
    }
}

XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Persons}">
    <DataGrid.Resources>
        <local:Selector x:Key="selector" />
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Id}" />
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTemplateColumn Header="Name1" IsReadOnly="False" CellEditingTemplateSelector="{StaticResource selector}" />
    </DataGrid.Columns>
</DataGrid>
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Can I edit Cars via WPF form? You have `IsReadOnly="False"` . – Pro Jul 06 '18 at 12:19
  • Do I right think I must set up `selector` in Resource of DataGrid? – Pro Jul 06 '18 at 12:21
  • @Kirill: Yes and yes. Or atleast you must define the "selector" resource somwhere. See my edited markup. – mm8 Jul 06 '18 at 12:23
  • Thank you for the solution. – Pro Jul 06 '18 at 12:27
  • Do I also want to ask, how will be organised the backlink? I mean how does the code know about changed Speed in WPF? Must I use `Binding` class in `SelectTemplate`? – Pro Jul 08 '18 at 04:51
  • Please don't use the comment field to ask additional questions. You should ask another question if you have another issue. – mm8 Jul 08 '18 at 19:37