質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

3331閲覧

Xceed.Wpf.Toolkit.PropertyGrid にて、動的プロパティ登録をしたい

OlivePopeye.net

総合スコア26

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

0クリップ

投稿2021/08/10 15:56

編集2021/08/10 16:44

いつも活用させていただいております。
早速ですが、タイトルについて相談させてください。

前提・実現したいこと

ある項目定義に合わせてPropertyGridへ表示する項目内容を動的に変化させてデータを登録したい。
※現状は、XceedのPropertyGridで実現を検討しています。

発生している問題・エラーメッセージ

初期値の値が表示されない。また、入力した値がBindingした変数に反映されない。

image

該当のソースコード

XAML

1<Window x:Class="SensCmdApp.View.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:SensCmdApp.View" 7 mc:Ignorable="d" 8 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 9 xmlns:rp="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF" 10 xmlns:vm="clr-namespace:SensCmdApp.ViewModel" 11 xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:model="clr-namespace:SensCmdApp.Model" 12 Title="MainWindow" Height="450" Width="800"> 13 14 <Window.DataContext> 15 <vm:MainWindowViewModel /> 16 </Window.DataContext> 17 18 <Grid> 19 <Grid.ColumnDefinitions> 20 <ColumnDefinition MinWidth="100" Width="200" /> 21 <ColumnDefinition Width="5" /> 22 <ColumnDefinition Width="*" /> 23 </Grid.ColumnDefinitions> 24 25 <DockPanel Grid.Column="0" LastChildFill="True" > 26 <TreeView ItemsSource="{Binding CommandTree}" DockPanel.Dock="Top" Margin="5,5,0,5"> 27 28 <i:Interaction.Triggers> 29 <i:EventTrigger EventName="SelectedItemChanged"> 30 <rp:EventToReactiveCommand Command="{Binding SelectionChangedCommand}" /> 31 </i:EventTrigger> 32 </i:Interaction.Triggers> 33 34 <TreeView.ItemTemplate> 35 <HierarchicalDataTemplate ItemsSource="{Binding Children}" > 36 <TextBlock Text="{Binding Name}" /> 37 </HierarchicalDataTemplate> 38 </TreeView.ItemTemplate> 39 40 </TreeView> 41 </DockPanel> 42 43 <GridSplitter Grid.Column="1" Style="{StaticResource VerticalGridSplitter}" /> 44 45 <DockPanel Grid.Column="2" LastChildFill="True"> 46 <Button Content="Test" DockPanel.Dock="Bottom" Height="30" /> 47 <xctk:PropertyGrid DockPanel.Dock="Bottom" ShowSearchBox="False" ShowSortOptions="False" SelectedObject="{Binding CommandProperty.Value, UpdateSourceTrigger=PropertyChanged}" /> 48 </DockPanel> 49 </Grid> 50</Window>

c#

1/// CustomProperty/CustomPropertyDescriptor/CustomObjectConverter 2/// 3using System; 4using System.Collections.Generic; 5using System.Collections.ObjectModel; 6using System.ComponentModel; 7using System.Linq; 8 9namespace SensCmdApp.Model 10{ 11 /// <summary> 12 /// CustomClass (Which is binding to property grid) 13 /// </summary> 14 //[TypeConverter(typeof(CustomObjectConverter))] 15 [TypeConverter(typeof(CustomObjectConverter))] 16 public class CustomObjectType 17 { 18 [Browsable(false)] 19 public string Name { get; set; } 20 [Browsable(false)] 21 public ObservableCollection<CustomProperty> Properties { get; set; } = new ObservableCollection<CustomProperty>(); 22 } 23 /// <summary> 24 /// Custom property class 25 /// </summary> 26 public class CustomProperty 27 { 28 public string Category { get; set; } = string.Empty; 29 public string Name { get; set; } = string.Empty; 30 public object Value { get; set; } = null; 31 public string Description { get; set; } = string.Empty; 32 public bool IsReadOnly { get; set; } = false; 33 public bool IsVisible { get; set; } = true; 34 public Type Type { get; set; } = null; 35 } 36 /// <summary> 37 /// Custom PropertyDescriptor 38 /// </summary> 39 public class CustomPropertyDescriptor : PropertyDescriptor 40 { 41 CustomProperty _prop; 42 43 public CustomPropertyDescriptor(CustomProperty Property, Attribute[] Attributes) : base(Property.Name, Attributes) 44 { 45 _prop = Property; 46 } 47 48 #region PropertyDescriptor specific 49 public override bool CanResetValue(object component) => true; 50 public override Type ComponentType => _prop.Value.GetType(); 51 public override object GetValue(object component) => _prop.Value; 52 public override string Name => _prop.Name; 53 public override string Description => _prop.Description; 54 public override string Category => _prop.Category; 55 public override string DisplayName => _prop.Name; 56 public override bool IsReadOnly => _prop.IsReadOnly; 57 public override void ResetValue(object component) { /* Have to implement */ } 58 public override bool ShouldSerializeValue(object component) => true; 59 public override void SetValue(object component, object value) => _prop.Value = value; 60 public override Type PropertyType => _prop.Value.GetType(); 61 public override bool IsBrowsable => _prop.IsVisible; 62 #endregion 63 } 64 65 public class CustomObjectConverter : ExpandableObjectConverter 66 { 67 public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) 68 { 69 CustomObjectType obj = value as CustomObjectType; 70 List<CustomProperty> customProps = obj?.Properties.ToList(); 71 PropertyDescriptor[] props = new PropertyDescriptor[(customProps == null ? 0 : customProps.Count)]; 72 if (customProps != null) 73 { 74 for (int i = 0; i < customProps.Count; i++) 75 { 76 props[i] = new CustomPropertyDescriptor(customProps[i], attributes); 77 } 78 } 79 80 return new PropertyDescriptorCollection(props); 81 } 82 } 83}

C#

1// ~~ 省略 ~~ 2 3 public ReactiveProperty<CustomObjectType> CommandProperty { get; set; } = new ReactiveProperty<CustomObjectType>(); 4 5 public CustomObjectType GetCommandProperty() 6 { 7 var result = new CustomObjectType(); 8 9 try 10 { 11 var cmd = CommandInfo; 12 13 if (CommandInfo == null) { return null; } 14 15 // Command Information 16 var name = $"{cmd.CommandName}"; 17 var desc = $"{cmd.Summery}\n{cmd.Detail}"; 18 19 result.Name = name; 20 result.Properties.Add(new CustomProperty() { Category = "CommandInformation", Name = "CommandName", Type = typeof(string), Description = desc, Value = cmd.CommandName }); 21 result.Properties.Add(new CustomProperty() { Category = "CommandInformation", Name = "AdjustBlock", Type = typeof(string), Description = desc, Value = cmd.CommandName }); 22 result.Properties.Add(new CustomProperty() { Category = "CommandInformation", Name = "AdjustCode", Type = typeof(string), Description = desc, Value = cmd.CommandName }); 23 24 for (int i = 0; i < cmd.RequestParamNum; i++) 25 { 26 result.Properties.Add(new CustomProperty() { Category = "RequestParameter", Name = $"S{i}", Type = typeof(uint), Description = $"S{i}", Value = 0 }); 27 } 28 29 for (int i = 0; i < cmd.ResponseParamNum; i++) 30 { 31 result.Properties.Add(new CustomProperty() { Category = "ResponseParameter", Name = $"R{i}", Type = typeof(uint), Description = $"R{i}", Value = 0 }); 32 } 33 } 34 catch (Exception) 35 { 36 result = null; 37 } 38 39 CommandProperty.Value = result; 40 return result; 41 }

試したこと

上記を実行することで、動的にProperty自体は表示されるものの、PropertyGrid上に値が表示されない。
また、値をいれても、その値が、「CommandProperty」に反映されない。
※CustomPropertyDescriptor の「SetValue」「GetValue」にブレークポイントをはっても停止されない(実行されていない)

補足情報(FW/ツールのバージョンなど)

Visual Studio 2019
.NET Framework 4.6.1


SetValue/GetValueが呼ばれないことためだとは思うのですが、なぜ呼ばれないのかがわからず。。。
根本的に間違っているような気もするのですが、どこなのかがわからず困っています。
申し訳ありませんが、ご教授お願いしたいと思います。

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

参考URLが誤記とはどういうことでしょうか?

それに(あえてurlはだしませんが)こういった怪しげな翻訳サイト(最近日本語のも多く見ますね)ではなく元ネタを探しましょう(ほぼ Stack Overflow が元ネタ)
c# - How to display a dynamic object in property grid? - Stack Overflow

その回答の中で、

a custom TypeConverter, or (alternatively) ICustomTypeDescriptor/TypeDescriptionProvider.

とありましたので ICustomTypeDescriptor のほうでやってみたところ動いている気がします(あっているかは自信ないです^^;

参考
c# - Dynamic PropertyGrid properties - Stack Overflow
Customized Display of Collection Data in a PropertyGrid - CodeProject

TypeConverterでもConvertFromとかがあればいいのかもしれませんが、PropertyGridを使ったことがないのでぶっちゃけ何もわかってません^^;

xml

1<Window 2 x:Class="Questions353706.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Questions353706" 6 xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" 7 Width="800" 8 Height="450"> 9 <Window.DataContext> 10 <local:MainWindowViewModel /> 11 </Window.DataContext> 12 <Grid> 13 <Grid.ColumnDefinitions> 14 <ColumnDefinition Width="200" MinWidth="100" /> 15 <ColumnDefinition Width="5" /> 16 <ColumnDefinition Width="*" /> 17 </Grid.ColumnDefinitions> 18 19 <DockPanel> 20 <ListBox 21 DisplayMemberPath="Name" 22 ItemsSource="{Binding CommandList}" 23 SelectedItem="{Binding CommandProperty.Value}" /> 24 </DockPanel> 25 26 <GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" /> 27 28 <DockPanel Grid.Column="2"> 29 <Button 30 Command="{Binding TestCommand}" 31 Content="Test" 32 DockPanel.Dock="Bottom" /> 33 <xctk:PropertyGrid 34 SelectedObject="{Binding CommandProperty.Value, UpdateSourceTrigger=PropertyChanged}" 35 ShowSearchBox="False" 36 ShowSortOptions="False" /> 37 </DockPanel> 38 </Grid> 39</Window>

cs

1using Reactive.Bindings; 2using System; 3using System.Collections.Generic; 4using System.Collections.ObjectModel; 5using System.ComponentModel; 6using System.Diagnostics; 7using System.Linq; 8using System.Reactive.Linq; 9using System.Windows; 10 11namespace Questions353706 12{ 13 public class CustomObjectType : ICustomTypeDescriptor 14 { 15 public string Name { get; set; } 16 public List<CustomProperty> Properties { get; } = new List<CustomProperty>(); 17 18 public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(this, true); 19 public string GetClassName() => TypeDescriptor.GetClassName(this, true); 20 public string GetComponentName() => TypeDescriptor.GetComponentName(this, true); 21 public TypeConverter GetConverter() => TypeDescriptor.GetConverter(this, true); 22 public EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(this, true); 23 public PropertyDescriptor GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(this, true); 24 public object GetEditor(Type editorBaseType) => TypeDescriptor.GetEditor(this, editorBaseType, true); 25 public EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(this, true); 26 public EventDescriptorCollection GetEvents(Attribute[] attributes) => TypeDescriptor.GetEvents(this, attributes, true); 27 public PropertyDescriptorCollection GetProperties() => GetProperties(null); 28 public PropertyDescriptorCollection GetProperties(Attribute[] attributes) 29 => new PropertyDescriptorCollection(Properties.Select(x => new CustomPropertyDescriptor(x, null)).ToArray()); 30 public object GetPropertyOwner(PropertyDescriptor pd) => this; 31 32 33 private class CustomPropertyDescriptor : PropertyDescriptor 34 { 35 public override Type ComponentType => Property.Value.GetType(); 36 public override bool IsReadOnly => Property.IsReadOnly; 37 public override Type PropertyType => Property.Value.GetType(); 38 39 public override string Name => Property.Name; 40 public override string Description => Property.Description; 41 public override string Category => Property.Category; 42 public override string DisplayName => Property.Name; 43 public override bool IsBrowsable => Property.IsVisible; 44 45 private readonly CustomProperty Property; 46 47 public CustomPropertyDescriptor(CustomProperty Property, Attribute[] Attributes) 48 : base(Property.Name, Attributes) => this.Property = Property; 49 50 public override bool CanResetValue(object component) => true; 51 public override object GetValue(object component) => Property.Value; 52 public override void ResetValue(object component) { } 53 public override void SetValue(object component, object value) => Property.Value = value; 54 public override bool ShouldSerializeValue(object component) => true; 55 } 56 } 57 58 public class CustomProperty 59 { 60 public string Category { get; set; } 61 public string Name { get; set; } 62 public object Value { get; set; } 63 public string Description { get; set; } 64 public bool IsReadOnly { get; set; } 65 public bool IsVisible { get; set; } = true; 66 public Type Type { get; set; } 67 } 68 69 public class CommandInfo 70 { 71 public string CommandName { get; set; } 72 public string Summery { get; set; } 73 public string Detail { get; set; } 74 public int RequestParamNum { get; set; } 75 public int ResponseParamNum { get; set; } 76 } 77 78 public class MainWindowViewModel 79 { 80 public ReactiveProperty<CustomObjectType> CommandProperty { get; } = new ReactiveProperty<CustomObjectType>(); 81 public ObservableCollection<CustomObjectType> CommandList { get; } 82 public ReactiveCommand TestCommand { get; } 83 84 public MainWindowViewModel() 85 { 86 CommandList = new ObservableCollection<CustomObjectType>( 87 Enumerable.Range(1, 5).Select(x => GetCommandProperty(new CommandInfo 88 { 89 CommandName = $"CommandName{x}", 90 Summery = $"Summery{x}", 91 Detail = $"Detail{x}", 92 RequestParamNum = x, 93 ResponseParamNum = x, 94 }))); 95 96 TestCommand = new ReactiveCommand().WithSubscribe(() => 97 { 98 if (CommandProperty.Value == null) return; 99 Debug.WriteLine($"\n{CommandProperty.Value.Name}"); 100 foreach (var property in CommandProperty.Value.Properties) 101 Debug.WriteLine($"{property.Name}: {property.Value}"); 102 }); 103 } 104 105 private CustomObjectType GetCommandProperty(CommandInfo info) 106 { 107 var description = $"{info.Summery}\n{info.Detail}"; 108 var result = new CustomObjectType 109 { 110 Name = $"{info.CommandName}", 111 }; 112 result.Properties.Add(new CustomProperty 113 { 114 Category = "CommandInformation", 115 Name = "CommandName", 116 Type = typeof(string), 117 Description = description, 118 Value = info.CommandName, 119 }); 120 result.Properties.Add(new CustomProperty 121 { 122 Category = "CommandInformation", 123 Name = "AdjustBlock", 124 Type = typeof(string), 125 Description = description, 126 Value = info.CommandName, 127 }); 128 result.Properties.Add(new CustomProperty 129 { 130 Category = "CommandInformation", 131 Name = "AdjustCode", 132 Type = typeof(string), 133 Description = description, 134 Value = info.CommandName, 135 }); 136 137 for (var i = 0; i < info.RequestParamNum; i++) 138 { 139 result.Properties.Add(new CustomProperty 140 { 141 Category = "RequestParameter", 142 Name = $"S{i}", 143 Type = typeof(uint), 144 Description = $"S{i}", 145 Value = 0, 146 }); 147 } 148 149 for (var i = 0; i < info.ResponseParamNum; i++) 150 { 151 result.Properties.Add(new CustomProperty 152 { 153 Category = "ResponseParameter", 154 Name = $"R{i}", 155 Type = typeof(uint), 156 Description = $"R{i}", 157 Value = 0, 158 }); 159 } 160 161 return result; 162 } 163 } 164 165 public partial class MainWindow : Window 166 { 167 public MainWindow() => InitializeComponent(); 168 } 169}

投稿2021/08/11 11:10

編集2023/07/28 16:25
TN8001

総合スコア9862

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OlivePopeye.net

2021/08/11 12:44

動きました! 見ていたサイトにちゃんとヒントが書いてありましたね。 自分の英語能力の低さで、理解がちゃんと出来ておらず恥ずかしい。。。。 >参考URLが誤記とはどういうことでしょうか?   すみません。似たようなサイトが合って、どれだったかを確認していたときに、   誤ってURLを消したまま更新してしまっていました。 PropertyGridを使ったことがなくても出来てしまうのは、、、センスなんですよね。 英語も含めて頑張りたいと思います。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問