在PropertyGrid控件中显示只读属性

7
我正在使用WPF扩展工具包来显示一个“团队”对象的属性。其中一个属性是一个名为“Persons”的集合。点击下拉框后,我可以看到每个人的名字和年龄。保留HTML标签。

enter image description here

现在的问题是,我实际上不想将我的 Collection 公开。但是,一旦我将其 setter 设为 private,该属性就会被禁用,防止用户查看 Person collection 和 person details:

enter image description here

当我的Person Collection的setter是私有的时,我应该如何显示它?我可以用XAML模板来实现吗?如果可以,怎么做?我正在使用MVVM,所以不想在代码后台中添加任何内容。
更新
好的,@tencntraze的解决方案让我接近成功 - 谢谢。但是对于我这种情况下的对象集合,它不起作用。此外,可以简化它,使用CollectionControlDialog而不是下面实现的自定义ReadOnlyCollectionViewer。
XAML
<UserControl x:Class="DevExpressTreeList.ReadOnlyCollectionEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Name="MyUserControl"
             >
    <DockPanel>
        <Button Click="Button_OnClick" DockPanel.Dock="Right">
            <Label Content="˅" Padding="2,0,2,0" />
        </Button>
        <Label Name="CollectionLabel" Content="(Collection)" Padding="2,2,2,0" />
    </DockPanel>
</UserControl>

代码后台

public partial class ReadOnlyCollectionEditor : UserControl, ITypeEditor
{
    public ReadOnlyCollectionEditor()
    {
        InitializeComponent();
    }

    // Use typeof(object) to allow for any Collection<T>
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof(object), typeof(ReadOnlyCollectionEditor), new PropertyMetadata(default(object)));

    public object Value
    {
        // We are now using object so no need to cast
        get { return GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public FrameworkElement ResolveEditor(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
    {
        var binding = new Binding("Value")
        {
            Source = propertyItem,
            Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay
        };
        BindingOperations.SetBinding(this, ValueProperty, binding);
        return this;
    }

    private void Button_OnClick(object sender, RoutedEventArgs e)
    {
        var collectionControlDialog = new CollectionControlDialog
        {
            ItemsSource = (IList)this.Value
        };
        collectionControlDialog.ShowDialog();
    }
}

你尝试过使用只读集合吗? - Alberto
@Alberto 是的,我有,尽管这没有任何区别。 - openshac
为什么你不想让你的属性是公共的?你到底想要实现什么目标?你想看到列表但不能编辑它,或者可能不改变选择。你能详细说明一下吗? - J King
公平的问题。我正在使用一个接口,无法真正更改它。尽管对于该接口仅具有只读属性是有意义的。我只需要能够向用户显示集合的内容。简单地告诉他们“这是一个集合”并没有多大帮助。 - openshac
你有在“客户ItemsSource”部分下检查过这里吗? https://wpftoolkit.codeplex.com/wikipage?title=PropertyGrid&referringTitle=Home - Xcalibur37
@Xcalibur37 是的,我尝试添加了ItemsSource属性,但没有生效。 - openshac
2个回答

10
我认为你最好的选择是按照Xceed Documentation的要求实现自己的编辑器。这样,您就可以提供任何您想要显示给用户的UI,而不需要将值提交回底层对象。请注意,此方法适用于私有setter以及没有任何setter的属性。 ReadOnlyCollectionEditor XAML
<UserControl x:Class="WpfApplication2.ReadOnlyCollectionEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Name="uc">
    <Button Click="Button_OnClick" Height="20" />
</UserControl>

代码后台

public partial class ReadOnlyCollectionEditor : UserControl, ITypeEditor
{
    public ReadOnlyCollectionEditor()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof (IList<string>), typeof (ReadOnlyCollectionEditor), new PropertyMetadata(default(IList<string>)));

    public IList<string> Value
    {
        get { return (IList<string>)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public FrameworkElement ResolveEditor(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
    {
        var binding = new Binding("Value")
        {
            Source = propertyItem,
            Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay
        };
        BindingOperations.SetBinding(this, ValueProperty, binding);
        return this;
    }

    private void Button_OnClick(object sender, RoutedEventArgs e)
    {
        ReadOnlyCollectionViewer viewer = new ReadOnlyCollectionViewer {DataContext = this};
        viewer.ShowDialog();
    }
}

ReadOnlyCollectionViewer

<Window x:Class="WpfApplication2.ReadOnlyCollectionViewer"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ReadOnlyCollectionViewer" Height="300" Width="300">
    <ListBox ItemsSource="{Binding Value}" />
</Window>

示例属性类

public class MyDataObjects
{
    public MyDataObjects()
    {
        this.CollectionProperty = new Collection<string> {"Item 1", "Item 2", "Item 3"};            
        this.StringProperty = "Hi!";
    }

    public string StringProperty { get; set; }

    [Editor(typeof(ReadOnlyCollectionEditor), typeof(ReadOnlyCollectionEditor))]
    public ICollection<string> CollectionProperty { get; private set; } 
}   

将属性网格分配

this.propertyGrid.SelectedObject = new MyDataObjects();

结果

Main Window

enter image description here

编辑

我意识到你想使用MVVM,当在WPF中使用时,我强烈鼓励这样做,但是为了这个示例的目的,我认为保持简单有助于阐明重点,否则会引起其他问题,比如从MVVM中显示模态对话框,所以我只是在按钮单击时显示对话框。


这是一个很好的答案,但正如你所提到的,它不太符合MVVM的友好性。我刚刚问了一个沿着同样方向的问题,我想将该按钮点击的控制权传递给包含PropertyGrid的View的ViewModel...如果您知道答案,请在此处提供链接:https://stackoverflow.com/questions/45125563/xceed-wpftoolkit-propertygrid-custom-typeeditor-bindable-command - IgorMF

1
public Collection<Person> People
{
    get { return _people; }
    set { throw new NotSupportedException(); }
}

也许不是最好的解决方案,但它可以与PropertyGrid一起使用,同时防止用户设置新集合。

1
在这种情况下,抛出异常不是一个选项。 - openshac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接