Custom Editor doesn't get its template applied

8 posts, 1 answers
  1. BENN
    BENN avatar
    59 posts
    Member since:
    Dec 2011

    Posted 09 Dec 2015 Link to this post

    I'm experimenting the attribute Telerik.Windows.Controls.Data.PropertyGrid.Editor, and in your example, you show the usage of a RadColorEditor as a cusom editor.

    I'm doing the same thing with a control (which currently only shows the color), but the template is not being applied. I have put the style with the template in the Themes\Generic.xaml and still nothing.

    If I put an instance of that control on the XAML directory, then I see it.

     

    Here is my View Model:

     

    public class Person : DependencyObject , INotifyPropertyChanged
    {
        public Person()
        {
            currentColor = Colors.Red;
        }
     
        private string _firstName = "Sponge";
        [Display(Name="First Name", GroupName="Details", Order = 1, Prompt="tttt"), CustomValidation(typeof(Person), "blabla"), Browsable(true)]
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }
        private string _lastName = "Bob";
     
        [DisplayName("Last Name"), Category("Details"), Browsable(true)]
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }
        private string _blabla;
     
        public string Blabla
        {
            get { return _blabla; }
            set { _blabla = value; }
        }
     
        [Telerik.Windows.Controls.Data.PropertyGrid.Editor(typeof(MyEditor), "SelectedColor", Telerik.Windows.Controls.Data.PropertyGrid.EditorStyle.None), Browsable(true)]
        public Color CurrentColor
        {
            get
            {
                return this.currentColor;
            }
            set
            {
                if (this.currentColor != value)
                {
                    this.currentColor = value;
                    this.OnPropertyChanged("CurrentColor");
                }
            }
        }
     
        public void blabla()
        {
        }
     
     
     
        public Visibility Visibility
        {
            get { return (Visibility)GetValue(VisibilityProperty); }
            set { SetValue(VisibilityProperty, value); }
        }
     
        // Using a DependencyProperty as the backing store for Visibility.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty VisibilityProperty =
            DependencyProperty.Register("Visibility", typeof(Visibility), typeof(Person), new UIPropertyMetadata(Visibility.Visible));
     
     
     
        private Color currentColor { get; set; }
     
        public event PropertyChangedEventHandler PropertyChanged;
     
        public void OnPropertyChanged(string propertyName)
        {
            if (propertyName != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

     

     This is the XAML:

     

    <Window x:Class="TelerikPropertyGrid.MainWindow"
            xmlns:myEditors="clr-namespace:MyEditors;assembly=MyEditors"
            xmlns:local="clr-namespace:TelerikPropertyGrid"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <telerik:RadPropertyGrid x:Name="pg" DescriptionPanelVisibility="Collapsed" Item="{Binding Item}" telerik:StyleManager.Theme="Windows8" AutoGeneratingPropertyDefinition="pg_AutoGeneratingPropertyDefinition" BeginningEdit="pg_BeginningEdit" EditEnded="pg_EditEnded" FieldLoaded="pg_FieldLoaded" Grouped="pg_Grouped" ItemChanged="pg_ItemChanged" SelectionChanged="pg_SelectionChanged" SourceUpdated="pg_SourceUpdated"  />
       </Grid>
    </Window>

     

     

    This is the code-behind of that window:

    public partial class MainWindow : Window
    {
        private Person _person = new Person();
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }
     
        public Person Item
        {
            get
            {
                return _person;
            }
        }
     
        private void pg_AutoGeneratingPropertyDefinition(object sender, Telerik.Windows.Controls.Data.PropertyGrid.AutoGeneratingPropertyDefinitionEventArgs e)
        {
            var pd = e.PropertyDefinition;
            var desc = pd.SourceProperty.Descriptor as PropertyDescriptor;
            bool canAdd = false;
            for (int i = 0; i < desc.Attributes.Count; i++)
            {
                var att = desc.Attributes[i];
                if (att is BrowsableAttribute && (att as BrowsableAttribute).Browsable)
                {
                    canAdd = true;
                }
            }
     
            if (!canAdd)
                e.Cancel = true;
     
            if (pd.DisplayName == "First Name")
            {
                pd.DisplayName = "First[Name2]";
                Binding binding = new Binding("Visibility");
                binding.Source = Item;
                BindingOperations.SetBinding(pd, PropertyDefinition.VisibilityProperty, binding);
            }
            
     
             
     
        }
     
        private void pg_BeginningEdit(object sender, Telerik.Windows.Controls.Data.PropertyGrid.PropertyGridBeginningEditEventArgs e)
        {
     
        }
     
        private void pg_EditEnded(object sender, Telerik.Windows.Controls.Data.PropertyGrid.PropertyGridEditEndedEventArgs e)
        {
     
        }
     
        private void pg_FieldLoaded(object sender, Telerik.Windows.Controls.Data.PropertyGrid.FieldEventArgs e)
        {
     
        }
     
        private void pg_Grouped(object sender, Telerik.Windows.RadRoutedEventArgs e)
        {
     
        }
     
        private void pg_ItemChanged(object sender, Telerik.Windows.Controls.Data.PropertyGrid.PropertyGridItemChangedEventArgs e)
        {
     
        }
     
        private void pg_SelectionChanged(object sender, Telerik.Windows.Controls.SelectionChangeEventArgs e)
        {
     
        }
     
        private void pg_SourceUpdated(object sender, DataTransferEventArgs e)
        {
     
        }
     }
     

     

    The Editor, MyEditor is located on a different DLL:

    MyEditor.cs... the break point of the OnApplyTemplate is never reached:

    public class MyEditor : Control
    {
     
        static MyEditor()
        {
            FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(MyEditor), new FrameworkPropertyMetadata(typeof(MyEditor)));
        }
     
        public MyEditor()
        {
            this.SetValue(FrameworkElement.DefaultStyleKeyProperty, typeof(MyEditor));
        }
     
        public Color SelectedColor
        {
            get { return (Color)GetValue(SelectedColorProperty); }
            set { SetValue(SelectedColorProperty, value); }
        }
     
        // Using a DependencyProperty as the backing store for SelectedColor.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedColorProperty =
            DependencyProperty.Register("SelectedColor", typeof(Color), typeof(MyEditor), new UIPropertyMetadata(Colors.White, onSelectedColorChanged));
     
     
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }
     
        public static void onSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

     

    // This is the code of the converter

        public class ColorToBrushConverter : IValueConverter
         {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value is Color)
                {
                    return new SolidColorBrush((Color)value);
                }

                return null;
            }

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

    }

     

     Generic.xaml located under the Themes directory

                        xmlns:local="clr-namespace:MyEditors"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     
        <local:ColorToBrushConverter x:Key="ctb" />
        <Style TargetType="{x:Type local:MyEditor}" x:Key="{x:Type local:MyEditor}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:MyEditor}">
                        <Border x:Name="border" Background="{TemplateBinding SelectedColor, Converter={StaticResource ctb}}">
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ResourceDictionary>

     

    I can do the following code once, when the LayoutUpdated is called for the first time, but it looks dirty, and I'm sure that there are better ways:
    Style ts = Application.Current.FindResource(typeof(MyEditor)) as Style;
    this.Style = ts;

     

    What am I doing wrong?

    What am I missing, and should I do in order to see the editor in the property grid.

     

    Thanks.

  2. Petya
    Admin
    Petya avatar
    975 posts

    Posted 14 Dec 2015 Link to this post

    Hi BENN,

    I tried the code you've sent us and everything is working fine on my end, so I'm not sure why it doesn't work for you. I'm sending you the project I created, please review it and try to find the differences between your original solution and the sample.

    On a side note, I noticed you're using the StyleManager to set a theme for the Telerik controls in your application, so I wanted to mention that we recommend using implicit styles instead. This blog post sheds some more information on the differences between the two and the benefits of using the latter approach.

    I hope this helps.

    Regards,
    Petya
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  3. UI for WPF is Visual Studio 2017 Ready
  4. BENN
    BENN avatar
    59 posts
    Member since:
    Dec 2011

    Posted 14 Dec 2015 in reply to Petya Link to this post

    Hi, I see that in your code you set the style with the following lines:

    Style ts = Application.Current.FindResource(typeof(MyEditor)) as Style;
    this.Style = ts;

     I've suggested it as a solution on my first post, but this looks like a dirty solution.

     

    Looking at your sources, I don't see that you do the same, for example, for the RadColorPicker. (Unless I'm missing something).

     

    If you remove the lines of the "dirty" solution, then you will see that the template of the editor is not being applied.

    I was hoping that editors will still have their template applied just like on any other case, where a Themes\Generic.xaml file is used.

     

    What am I doing wrong?

  5. BENN
    BENN avatar
    59 posts
    Member since:
    Dec 2011

    Posted 15 Dec 2015 Link to this post

    Hi, I'm pretty sure that the problem is with the Property Grid. I'll explain:

    Normally, you just need to have a Themes\Generic.xaml in order to see the control (and if I just put it on XAML, then I see it, and the template is being applied). However, this is not the case when using the control as an editor inside the Property Grid.

     

    I tried using the Editor Property on the ColorPicker that is supplied with Extended WPF Toolkit and also the Syncfusion one.

    Both of them suffer from the same problem, so even if I fix the problem by setting the style on the layout updated event, then I would still have problems with 3rd party controls which I can't or don't want to edit.... And I can't contain Syncfusion (or Infragistics) and tell them "Hey, would you mind setting the style on the layout updated so the Telerik Property Grid will work correctly?"

     

  6. BENN
    BENN avatar
    59 posts
    Member since:
    Dec 2011

    Posted 15 Dec 2015 in reply to BENN Link to this post

    After further investigating this issue using Telerik's sources, I can see that if I pass over the code:

    StyleManager.SetDefaultStyleKey(this, typeof(RadColorPicker));

     

    (On both the "ResetTheme" and the OnInitialized)

    Then the RadColorPicker will not have its template applied as well.

     

    On the other hand, if I disable the Style Manager when the on the MainWindow Ctor (with StyleManager.IsEnabled = false), then:

    * The property Grid looks like one with an old style (I think that it;s the Office Black style)

    * The RadColorPicker and MyEditor (as well as other editors), now works.

     

    Is that the expected behavior? 

    Why to I have to explicitly disable the Style Manager in order to have the non-Telerik editors working?

  7. BENN
    BENN avatar
    59 posts
    Member since:
    Dec 2011

    Posted 16 Dec 2015 Link to this post

    Ok, I have continued investigating this issue, and I have found that the FrameworkElement.DefaultStyleKeyProperty is being overridden.

    I've traced the code back to: PropertyGridEditorFactory.SetDefaultStyleToEditor

    This function called the StyleManager.SetTheme which eventually executes

     

    if (StyleManager.IsEnabled)
    {
        var themeKey = ThemeResourceKey.GetDefaultStyleKey(StyleManager.GetTheme(control), controlType);
     
        Telerik.Windows.Controls.Theme.DefaultStyleKeyHelper.SetDefaultStyleKey(control, themeKey);
    }

     

     

     

    Shouldn't the function SetDefaultStyleToEditor check that the editor/control is IThemable (or at least one of your controls) before trying to apply the default style key?

     

    if (StyleManager.IsEnabled)
    {
        var themeKey = ThemeResourceKey.GetDefaultStyleKey(StyleManager.GetTheme(control), controlType);
     
        Telerik.Windows.Controls.Theme.DefaultStyleKeyHelper.SetDefaultStyleKey(control, themeKey);
    }
  8. Answer
    Petya
    Admin
    Petya avatar
    975 posts

    Posted 17 Dec 2015 Link to this post

    Hi Benn,

    Sorry for the confusion, I was testing different things and it appears I attached the wrong version of the project.

    If you take a look at the blog post I sent in my previous reply, you'll notice one of the reasons implicit styles and NoXaml binaries were introduced and they became the recommended approach is because styles for non-Telerik controls couldn't be applied easily. Again, I recommend reading the blog itself as it goes in more details about this and your observation concerning this case are exactly correct - the StyleManager sets the theme for you.

    Here are the two options I can suggest:
    1. Set a ThemeResourceKey to your custom control, so it will be properly styled by the StyleManager. I'm attaching the modified project demonstrating this approach.

    3. Switch to NoXaml binaries and style the Telerik controls implicitly. This would cause the Generic.xaml to be resolved as expected without you having to do anything additional.

    The latter approach is the one I recommend due to the reasons explained in the aforementioned blog post.

    I hope this makes sense.

    Regards,
    Petya
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  9. BENN
    BENN avatar
    59 posts
    Member since:
    Dec 2011

    Posted 17 Dec 2015 Link to this post

    Thanks
Back to Top
UI for WPF is Visual Studio 2017 Ready