This is a migrated thread and some comments may be shown as answers.

DataTemplate binding to arbitrary property?

3 Answers 576 Views
PropertyGrid
This is a migrated thread and some comments may be shown as answers.
Kristoffer
Top achievements
Rank 1
Kristoffer asked on 05 Jun 2013, 07:18 AM
Consider my DataTemplateSelector below. I want all my RadPropertyGrid to have this particular editor for all string properties. This implies a problem when binding to the property in XAML - since the property name is unknown! How can I get this working?

My initial thought was to map Path to the property so that I can later bind to the Path instead of a specified property name. Ideas?

 

public class MyPropertyGridDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var pd = item as PropertyDefinition;
 
        if (pd != null && pd.SourceProperty.PropertyType == typeof(string))
        {
            /* Something like this?
            pd.Binding = new Binding
            {
                Path = new PropertyPath(pd.SourceProperty.Name),
                Mode = BindingMode.TwoWay,
                UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
            };
            */
 
            return FilePathDataTemplate;
        }
 
        return null;
    }
 
    public DataTemplate FilePathDataTemplate { get; set; }
}

Binding becomes a problem since the property name is unknown:
<local:MyPropertyGridDataTemplateSelector x:Key="dataTemplateSelector">
        <local:MyPropertyGridDataTemplateSelector.FilePathDataTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <!-- This is where the problem begin... -->
                    <TextBox Text="{Binding Path}" Grid.Column="0" />
                    <Button Content="..." Width="25" Height="25" Grid.Column="1" Command="{Binding DataContext.OpenCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding Path}" />
                </Grid>
            </DataTemplate>
        </local:MyPropertyGridDataTemplateSelector.FilePathDataTemplate>
    </local:MyPropertyGridDataTemplateSelector>

3 Answers, 1 is accepted

Sort by
0
Ivan Ivanov
Telerik team
answered on 05 Jun 2013, 12:29 PM
Hi,

Reusing DataTemplates is a common problem in WPF. We have tried to solve it in the scope of RadPropertyGrid, by introducing the AutoBind attached behavior. Have you considered using it?

Regards,
Ivan Ivanov
Telerik

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Kristoffer
Top achievements
Rank 1
answered on 07 Jun 2013, 07:55 AM
Hi,

Thanks, but I'm not sure how this AutoBind thing would help? If the property is called "Foo" or "Bar", how can I make it bind to that property? It certainly cannot be done in XAML, which is why I build my data template in code-behind. That way, I can bind to e.SourceProperty.Name in runtime. Still, it doesn't work.

So far, I have this:
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
    var pd = item as PropertyDefinition;
 
    if (pd != null && pd.SourceProperty.PropertyType == typeof(IFileName))
    {
        var gridElement = new FrameworkElementFactory(typeof(Grid));
        var column1 = new FrameworkElementFactory(typeof(ColumnDefinition));
        var column2 = new FrameworkElementFactory(typeof(ColumnDefinition));
 
        column2.SetValue(ColumnDefinition.WidthProperty, GridLength.Auto);
 
        gridElement.AppendChild(column1);
        gridElement.AppendChild(column2);
 
        var textBoxElement = new FrameworkElementFactory(typeof(TextBox));
 
        textBoxElement.SetValue(Button.NameProperty, "PART_Value");
 
        var binding = new Binding()
        {
            Path = new PropertyPath(string.Format("{0}.Filename", pd.SourceProperty.Name)),
            Mode = BindingMode.TwoWay,
            UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
        };
 
        textBoxElement.SetBinding(TextBox.TextProperty, binding);
 
        textBoxElement.SetValue(AutoBindBehavior.UpdateBindingOnElementLoadedProperty, "Text");
 
        var buttonElement = new FrameworkElementFactory(typeof(Button));
 
        buttonElement.SetValue(Button.NameProperty, "PART_Editor");
        buttonElement.SetValue(Button.ContentProperty, "...");
        buttonElement.SetValue(Button.WidthProperty, 25.0);
        buttonElement.SetValue(Button.HeightProperty, 25.0);
        buttonElement.SetValue(Grid.ColumnProperty, 1);
 
        // This does NOT work:
        // buttonElement.AddHandler(Button.ClickEvent, new RoutedEventHandler(SelectFile));
 
        gridElement.AppendChild(textBoxElement);
        gridElement.AppendChild(buttonElement);
 
        var dataTemplate = new DataTemplate();
        dataTemplate.VisualTree = gridElement;
 
        dataTemplate.Seal();
 
        return dataTemplate;
    }
 
    return null;
}


And in the FieldLoaded event, I set up a click handler for the editor:
private void RadPropertyGrid_FieldLoaded(object sender, Telerik.Windows.Controls.Data.PropertyGrid.FieldEventArgs e)
{
    if (e.Field.Content is Grid)
    {
        var grid = e.Field.Content as Grid;
 
        var field = grid.ChildrenOfType<TextBox>()
            .Where(c => c.Name == "PART_Value")
            .FirstOrDefault();
 
        var editor = grid.ChildrenOfType<Button>()
            .Where(c => c.Name == "PART_Editor")
            .FirstOrDefault();
 
        if (editor != null)
        {
            editor.Click += (s, ev) =>
            {
                var dlg = new Microsoft.Win32.OpenFileDialog();
                dlg.Filter = "All Files|*.*";
 
                Nullable<bool> result = dlg.ShowDialog();
                if (result == true)
                {
                    field.Text = dlg.FileName;
                }
            };
        }
    }
}


It seems to work, but the binding does not update the property when field.Text is updated. I'm not sure I'm event using the right technique here.
0
Ivan Ivanov
Telerik team
answered on 12 Jun 2013, 10:54 AM
Hi,

The idea behind the AutoBind attached behavior is to enable reusable data templates in the scope of RadPropertyGrid. You can define your data template in xaml and specify its "display property" i.e. TextBox.TextProperty. You do not have to set any bindings in the DataTemplate. When the control, which has the behavior set, gets loaded, our internal logic copies the associated PropertyDefinitions's binding and binds the "display property" with it. Then, if you have a PropertyDefinition bound to "Foo" and one bound to "Bar", one and the same EditorTemplate can be used. Please, refer to the article that I have mentioned into the previous post of mine. There is a very simple example that illustrates the AutoBind mechanics.

Regards,
Ivan Ivanov
Telerik

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

Tags
PropertyGrid
Asked by
Kristoffer
Top achievements
Rank 1
Answers by
Ivan Ivanov
Telerik team
Kristoffer
Top achievements
Rank 1
Share this question
or