CollectionEditorPicker 的 Telerik RadPropertyGrid 内容

Telerik RadPropertyGrid Content of CollectionEditorPicker

如主题所示,我不想修改 CollectionEditorPicker 的内容。此控件用于打开嵌套属性列表的浮动 Window。 不幸的是,RadPropertyGrid 不显示有关字段中集合的任何信息。 我怎样才能在那里设置一些值?例如 "Click here to open the collection" 或 "xx Items" 或 "Item 1, Item 2, Item 3..." 这样的占位符,因此请查看有关该字段的一些预览或信息。

我已经用模板选择器试过了,但如果我这样做了,打开的弹出窗口就不能再调整大小了。它还会丢失默认 CollectionEditorPicker 中的一些信息。 你能帮帮我吗?

下面是一个最小的工作示例。

XAML:

<Window x:Class="TelerikPropertyGridTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
        xmlns:model="clr-namespace:TelerikPropertyGridTest.Model"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <model:TemplateSelector x:Key="RadPropertyListTemplateSelector">
                <!-- Not Working -->
                <model:TemplateSelector.CollectionsDataTemplate>
                        <DataTemplate>
                           <telerik:RadDropDownButton Content="Test">
                               <telerik:RadDropDownButton.DropDownContent>
                                   <telerik:CollectionEditor telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Source"
                                                             ></telerik:CollectionEditor>
                               </telerik:RadDropDownButton.DropDownContent>
                           </telerik:RadDropDownButton>
                        </DataTemplate>
                </model:TemplateSelector.CollectionsDataTemplate>
                <model:TemplateSelector.FloatNumberTemplate>
                    <DataTemplate>
                        <telerik:RadNumericUpDown telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Value" />
                    </DataTemplate>
                </model:TemplateSelector.FloatNumberTemplate>
                <model:TemplateSelector.IntNumberTemplate>
                    <DataTemplate>
                        <telerik:RadNumericUpDown telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Value"
                                                  NumberDecimalDigits="0" />
                    </DataTemplate>
                </model:TemplateSelector.IntNumberTemplate>
            </model:TemplateSelector>
        </Grid.Resources>
        <telerik:RadPropertyGrid Item="{Binding ObjectToBind}" 
                                 AutoGeneratingPropertyDefinition="RadPropertyGrid_OnAutoGeneratingPropertyDefinition"
                                 EditorTemplateSelector="{StaticResource RadPropertyListTemplateSelector}">
        </telerik:RadPropertyGrid>
    </Grid>
</Window>

ViewModel(生成用于测试的随机对象)

public class MainWindowViewModel : BindableBase
{

    private readonly Random _random = new Random();

    private IExampleInterface _objectToBind;

    public MainWindowViewModel()
    {
        this.ObjectToBind = new ExampleImplementation
                            {
                                SomeBooleanValue = this._random.Next() % 2 == 1,
                                SomeDateValue = this.RandomDay(),
                                SomeIntValue = this._random.Next(),
                                SomeString = Guid.NewGuid().ToString(),
                                SubClasses = new List<IExampleInterface>
                                             {
                                                 new ExampleImplementation
                                                 {
                                                     SomeBooleanValue = this._random.Next() % 2 == 1,
                                                     SomeDateValue = this.RandomDay(),
                                                     SomeIntValue = this._random.Next(),
                                                     SomeString = Guid.NewGuid().ToString(),
                                                     SubClasses = new List<IExampleInterface>
                                                                  {
                                                                      new ExampleImplementation
                                                                      {
                                                                          SomeBooleanValue =
                                                                              this._random.Next() % 2 == 1,
                                                                          SomeDateValue = this.RandomDay(),
                                                                          SomeIntValue = this._random.Next(),
                                                                          SomeString = Guid.NewGuid().ToString()
                                                                      }
                                                                  }
                                                 }
                                             }
                            };
    }

    public IExampleInterface ObjectToBind
    {
        get { return this._objectToBind; }
        set
        {
            if (this._objectToBind != value)
            {
                this._objectToBind = value;
                this.OnPropertyChanged("ObjectToBind");
            }
        }
    }

    private DateTime RandomDay()
    {
        var start = new DateTime(1995, 1, 1);
        var range = (DateTime.Today - start).Days;
        return start.AddDays(this._random.Next(range));
    }

}

IExampleInterface(应该稍后在真实接口上):

public interface IExampleInterface
{
    string SomeString { get; set; }
    int SomeIntValue { get; set; }
    double SomeDouble { get; set; }
    IList<IExampleInterface> SubClasses { get; set; }
    IList<IExampleInterface> SubClasses2 { get; set; }
    bool SomeBooleanValue { get; set; }
    DateTime SomeDateValue { get; set; }
    SomeEnum SomeEnumValue { get; set; }
}

ExampleImplementation(稍后应该有一个带有附加属性的真实实现)。

public class ExampleImplementation : BindableBase, IExampleInterface
{

    private bool _someBooleanValue;
    private DateTime _someDateValue;
    private double _someDouble;
    private SomeEnum _someEnumValue;
    private int _someIntValue;
    private string _someString;
    private ObservableCollection<IExampleInterface> _subClasses;

    private ObservableCollection<IExampleInterface> _subClasses2;

    public bool SomeBooleanValue
    {
        get { return this._someBooleanValue; }
        set
        {
            if (this._someBooleanValue != value)
            {
                this._someBooleanValue = value;
                this.OnPropertyChanged("SomeBooleanValue");
            }
        }
    }

    public DateTime SomeDateValue
    {
        get { return this._someDateValue; }
        set
        {
            if (this._someDateValue != value)
            {
                this._someDateValue = value;
                this.OnPropertyChanged("SomeDateValue");
            }
        }
    }

    public double SomeDouble
    {
        get { return this._someDouble; }
        set
        {
            if (Math.Abs(this._someDouble - value) > 0.01)
            {
                this._someDouble = value;
                this.OnPropertyChanged("SomeDouble");
            }
        }
    }

    public SomeEnum SomeEnumValue
    {
        get { return this._someEnumValue; }
        set
        {
            if (this._someEnumValue != value)
            {
                this._someEnumValue = value;
                this.OnPropertyChanged("SomeEnumValue");
            }
        }
    }

    public int SomeIntValue
    {
        get { return this._someIntValue; }
        set
        {
            if (this._someIntValue != value)
            {
                this._someIntValue = value;
                this.OnPropertyChanged("SomeIntValue");
            }
        }
    }

    [Display(Name = @"TestString", GroupName = @"TestGroup", Description = @"TestDescription")]
    public string SomeString
    {
        get { return this._someString; }
        set
        {
            if (this._someString != value)
            {
                this._someString = value;
                this.OnPropertyChanged("SomeString");
            }
        }
    }

    [Display(Name = @"Some Subclasses")]
    public IList<IExampleInterface> SubClasses
    {
        get { return this._subClasses; }
        set
        {
            if (!Equals(this._subClasses, value))
            {
                this._subClasses = new ObservableCollection<IExampleInterface>(value);
                this.OnPropertyChanged("SubClasses");
            }
        }
    }

    public IList<IExampleInterface> SubClasses2
    {
        get { return this._subClasses2; }
        set
        {
            if (!Equals(this._subClasses2, value))
            {
                this._subClasses2 = new ObservableCollection<IExampleInterface>(value);
                this.OnPropertyChanged("SubClasses2");
            }
        }
    }

}

最后是模板选择器

  public class TemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var def = item as PropertyDefinition;
        if (def == null || def.SourceProperty == null)
        {
            return base.SelectTemplate(item, container);
        }

        if (typeof (IEnumerable).IsAssignableFrom(def.SourceProperty.PropertyType) && typeof(string) != def.SourceProperty.PropertyType)
        {
            return this.CollectionsDataTemplate;
        }

        if (typeof (double).IsAssignableFrom(def.SourceProperty.PropertyType))
        {
            return this.FloatNumberTemplate;
        }

        if (typeof (int).IsAssignableFrom(def.SourceProperty.PropertyType))
        {
            return this.IntNumberTemplate;
        }

        return base.SelectTemplate(item, container);
    }

    public DataTemplate CollectionsDataTemplate { get; set; }
    public DataTemplate FloatNumberTemplate { get; set; }
    public DataTemplate IntNumberTemplate { get; set; }
}

This is what I expect

最佳解决方案是在 TextBlock 中获取详细信息,例如项目 1、项目 2 等。

谢谢。

// 编辑: 我已经找出 NullReferenceException 并得到一个可以工作的演示,这样我就可以修改文本。但是弹出窗口与默认的不同。您有修复它的想法吗?

我已经更新了文本和示例。

在浪费了几个小时之后,我想出了一个解决方案来实现这一点。

我已将自定义行为添加到 Collection 模板。此行为会在 Collection 编辑器加载或更新后立即设置 Header。

下面你可以看到我的修改:

模板:

<model:TemplateSelector.CollectionsDataTemplate>
    <DataTemplate>
        <telerik:RadDropDownButton Content="Click to edit the collection">
            <telerik:RadDropDownButton.DropDownContent>
                <telerik:CollectionEditor telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Source"
                                          ResizeGripperVisibility="Visible">
                    <i:Interaction.Behaviors>
                        <model:CollectionEditorBehavior />
                    </i:Interaction.Behaviors>
                </telerik:CollectionEditor>
            </telerik:RadDropDownButton.DropDownContent>
        </telerik:RadDropDownButton>
    </DataTemplate>
</model:TemplateSelector.CollectionsDataTemplate>

行为:

internal class CollectionEditorBehavior : Behavior<CollectionEditor>
{

    protected override void OnAttached()
    {
        this.AssociatedObject.SourceUpdated += (sender, args) => this.PrepareHeader();
        this.AssociatedObject.DataContextChanged += (sender, args) => this.PrepareHeader();
        this.AssociatedObject.Loaded += (sender, args) => this.PrepareHeader();
    }

    private void PrepareHeader()
    {
        if (this.AssociatedObject == null)
        {
            // Error Case
            return;
        }

        if (this.AssociatedObject.CollectionView == null ||
            this.AssociatedObject.CollectionView.SourceCollection == null)
        {
            // Source not set
            this.AssociatedObject.Header = "Collection";
            return;
        }

        // Get the property from the DataContext to retrieve HeaderInformation
        var propInfo = this.AssociatedObject.DataContext
                           .GetType()
                           .GetProperties()
                           .FirstOrDefault(
                                           propertyInfo =>
                                           Equals(propertyInfo.GetValue(this.AssociatedObject.DataContext),
                                                  this.AssociatedObject.CollectionView.SourceCollection));


        if (propInfo == null)
        {
            // We didn't got the property Information,  using default value
            this.AssociatedObject.Header = "Collection";
            return;
        }

        // Getting the DisplayName Attribute
        var attr = Attribute.GetCustomAttribute(propInfo,
                                                typeof (DisplayNameAttribute)) as DisplayNameAttribute;

        if (attr != null)
        {
            // We have a DisplayName attribute
            this.AssociatedObject.Header = attr.DisplayName;
            return;
        }

        // Alternative: Get the Display Attribute
        var attr2 = Attribute.GetCustomAttribute(propInfo,
                                                 typeof (DisplayAttribute)) as DisplayAttribute;
        if (attr2 != null)
        {
            // We have the Display Attribute
            this.AssociatedObject.Header = attr2.Name;
            return;
        }

        // We have no DisplayAttribute and no DisplayName attribute, set it to the PropertyName
        this.AssociatedObject.Header = propInfo.Name;
    }

}