DataTemplate binding to arbitrary property?

4 posts, 0 answers
  1. Kristoffer
    Kristoffer avatar
    158 posts
    Member since:
    Dec 2012

    Posted 05 Jun 2013 Link to this post

    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>
  2. Ivan Ivanov
    Admin
    Ivan Ivanov avatar
    1127 posts

    Posted 05 Jun 2013 Link to this post

    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.

  3. UI for WPF is Visual Studio 2017 Ready
  4. Kristoffer
    Kristoffer avatar
    158 posts
    Member since:
    Dec 2012

    Posted 07 Jun 2013 Link to this post

    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.
  5. Ivan Ivanov
    Admin
    Ivan Ivanov avatar
    1127 posts

    Posted 12 Jun 2013 Link to this post

    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.

Back to Top