基于在CheckComboBox中的选择动态地隐藏属性。

问题描述 投票:0回答:1

在WPF中,我希望有这样一个视图模型,在一个 PropertyGrid 它应该显示 CheckComboBox 使我能够动态地显示其他属性(基于选择)。

我填充了 CheckComboBox 具有以下属性的内容。

// A collection used as the data source for the CheckComboBox.
[RefreshProperties(RefreshProperties.Repaint)]
public ObservableCollection<TriggerTypeItem> TriggerTypes { get; } = new ObservableCollection<TriggerTypeItem>();

其中 TriggerTypeItem 实施 INotifyPropertyChanged 介面。

然而,似乎对 TriggerTypeItem 元素不会导致 TriggerTypes 的属性被认为是修改的,因此--动态变化的 Browsable 属性不反映在属性网格中。(该 SetBrowsableAttribute() 函数工作正常,你可以通过切换到 ShouldShow 属性的复选框)。)

我应该怎么做才能实现所需的行为?

MainWindow.xaml

<Window x:Class="WpfPlayground.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="300">
    <Grid>
        <xctk:PropertyGrid x:Name="PropertyGridControl">
            <xctk:PropertyGrid.EditorDefinitions>
                <xctk:EditorTemplateDefinition TargetProperties="TriggerTypes">
                    <xctk:EditorTemplateDefinition.EditingTemplate>
                        <DataTemplate>
                            <xctk:CheckComboBox ItemsSource="{Binding Instance.TriggerTypes}"
                                                DisplayMemberPath="TriggerType"
                                                SelectedMemberPath="Selected" />
                        </DataTemplate>
                    </xctk:EditorTemplateDefinition.EditingTemplate>
                </xctk:EditorTemplateDefinition>
            </xctk:PropertyGrid.EditorDefinitions>
        </xctk:PropertyGrid>
    </Grid>
</Window>

在WPF中,我想有这样一个视图模型,在一个属性网格中,它应该显示一个CheckComboBox,使我能够动态地显示其他属性(基于选择)。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;

namespace WpfPlayground
{
    public partial class MainWindow : Window
    {
        private readonly EventViewModel eventViewModel = new EventViewModel();

        public MainWindow()
        {
            InitializeComponent();
            PropertyGridControl.SelectedObject = eventViewModel;
        }
    }

    public abstract class PropertyChangedBase : Component, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public enum TriggerType
    {
        Timer,
    }

    public class EventViewModel : PropertyChangedBase
    {
        private bool _shouldShow;
        private readonly Dictionary<TriggerType, string> _triggerViewModels = new Dictionary<TriggerType, string>()
        {
            { TriggerType.Timer, nameof(ToogleProperty) }
        };

        // A "regular" checkbox for toogling.
        [RefreshProperties(RefreshProperties.Repaint)]
        public bool ShouldShow
        {
            get { return _shouldShow; }
            set
            {
                TypeDescriptor.GetProperties(this)[nameof(ToogleProperty)].SetBrowsableAttribute(value);

                _shouldShow = value;
                NotifyOfPropertyChange(nameof(ShouldShow));
            }
        }

        // A collection used as the data source for the CheckComboBox.
        [RefreshProperties(RefreshProperties.Repaint)]
        public ObservableCollection<TriggerTypeItem> TriggerTypes { get; } = new ObservableCollection<TriggerTypeItem>();

        // The property to be toogled.
        [ReadOnly(true)]
        public string ToogleProperty { get; set; } = "(Toggle me!)";

        public EventViewModel()
        {
            ShouldShow = true;
            foreach (TriggerType val in Enum.GetValues(typeof(TriggerType)))
            {
                TriggerTypes.Add(new TriggerTypeItem(triggerType: val, eventModel: this) { Selected = false });
            }
        }

        /// <summary>
        /// Dynamically sets the <c>Browsable</c> attribute.
        /// </summary>
        public void UpdatePropertyGridTriggers(TriggerType triggerType, bool newBrowsableState)
        {
            if (_triggerViewModels.TryGetValue(triggerType, out string propertyName))
            {
                TypeDescriptor.GetProperties(this)[propertyName].SetBrowsableAttribute(newBrowsableState);
                NotifyOfPropertyChange(nameof(propertyName));
            }
        }
    }

    /// <summary>
    /// An item in the CheckCoboBox.
    /// </summary>
    public class TriggerTypeItem : PropertyChangedBase
    {
        private TriggerType _triggerType;
        private bool _selected;

        public EventViewModel EventModel { get; private set; }

        public TriggerType TriggerType
        {
            get { return _triggerType; }
            set
            {
                _triggerType = value;
                NotifyOfPropertyChange(nameof(TriggerType));
            }
        }

        public bool Selected
        {
            get { return _selected; }
            set
            {
                if (_selected != value)
                {
                    _selected = value;
                    EventModel?.UpdatePropertyGridTriggers(TriggerType, value);
                    NotifyOfPropertyChange(nameof(Selected));
                }
            }
        }

        public TriggerTypeItem(TriggerType triggerType, EventViewModel eventModel)
        {
            EventModel = eventModel;
            TriggerType = triggerType;
        }
    }

    public static class PropertyDescriptorExtensions
    {
        /// See: http://www.reza-aghaei.com/make-a-property-read-only-in-propertygrid/ (Solution #2)
        public static void SetBrowsableAttribute(this PropertyDescriptor p, bool value)
        {
            var attributes = p.Attributes.Cast<Attribute>().Where(x => !(x is BrowsableAttribute)).ToList();
            attributes.Add(new BrowsableAttribute(value));
            typeof(MemberDescriptor).GetProperty("AttributeArray", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(p, attributes.ToArray());
        }
    }
}
c# wpf xceed
1个回答
1
投票

WPF中的ObservableCollection不会传播NotifyPropertyChanged事件。因此,当TriggerTypeItems被改变时,RefreshProperties事件不会触发。克服这个问题的一个方法是通过改变TriggerTypeItems中的 NotifyOfPropertyChange(nameof(propertyName)); 在UpdatePropertyGridTriggers中改为 NotifyPropertyChange("")

© www.soinside.com 2019 - 2024. All rights reserved.