質問するログイン新規登録
C#

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

Q&A

解決済

1回答

970閲覧

【WPF】(int)Enum.Value をIndexとする配列へのバインド

add-1914

総合スコア16

C#

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

2グッド

0クリップ

投稿2023/09/19 15:53

2

0

お世話になっております。
WPFを利用したアプリケーションの開発について勉強しています。
現在、xamlファイル内で元々. 配列[0]の形式でバインドしていた箇所に対して、
添え字の値を列挙型から取得できるような方法を現在模索していますが、少々行き詰っておりまして、
お力添え頂けないでしょうか。

Web等で検索してみると、配列[(int)列挙型.値]のような形式で実装はできないような旨もあり、
そもそも実現が可能なのかという点も踏まえてご相談させて頂ければと思います。

実現したいこと

xamlファイル内で、『{Binding Path=MyArray[(int)EnumType.Normal]}』のような形式で
配列の添え字にアクセスしたいです。

恐らく、配列ではなく、Dictionary型にすることで、Dictionary[(local:定義したEnum) 値]のような形式であれば
列挙型の値を利用してのアクセスも可能だと考えています。

現行が配列[添え字]の形式でバインドされているので、影響範囲を考えるとできる限り修正が少なく
(可能であればConverterをかませる等)で出来たらうれしいです。

前提・試したこと

【実行結果】
イメージ説明

下記コードにて、xamlファイル内でバインドした結果①~③の通りとなりました。
①配列(添え字)
MyArray[0] → アクセス可能
②配列[(int)列挙型]
MyArray[(int)EnumType.Normal] →アクセス不可(※エラー発生)
③Dictionary型(キー値)
MyTypes[(local:EnumType)Normal] → アクセス可能

【実装時仕様】
※列挙型は本来値が50ほどあるので、各Enumの値それぞれでプロパティを作成するようなやり方は避けたいと考えています。

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

※②の場合のエラー内容は下記の通りです。

System.Windows.Markup.XamlParseException HResult=0x80131501 Message=''System.Windows.Baml2006.TypeConverterMarkupExtension' の値の指定時に例外がスローされました。' 行番号 '12'、行位置 '20'。 Source=PresentationFramework スタック トレース: 場所 System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri) 場所 System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri) 場所 System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri) 場所 System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream) 場所 System.Windows.Application.LoadComponent(Object component, Uri resourceLocator) 場所 WpfBinding.MainWindow.InitializeComponent() (D:\WpfBinding\WpfBinding\MainWindow.xaml):行 1 この例外は、最初にこの呼び出し履歴 [外部コード] でスローされました 内部例外 1: XamlParseException: 型参照は '{http://schemas.microsoft.com/winfx/2006/xaml/presentation}int' という名前の型を見つけることができません。

該当のソースコード

C#

1//MainViewModel.cs 2using System.Collections.Generic; 3 4namespace WpfBinding 5{ 6 public enum EnumType 7 { 8 Normal, 9 All, 10 None, 11 } 12 public class MainViewModel 13 { 14 public string[] MyArray { get; private set; } 15 16 public Dictionary<EnumType, string> MyTypes { get; private set; } 17 18 19 public MainViewModel() 20 { 21 MyArray = new string[3]; 22 MyArray[(int)EnumType.Normal] = "Normal:配列"; 23 MyArray[(int)EnumType.All] = "All:配列"; 24 MyArray[(int)EnumType.None] = "None:配列"; 25 26 MyTypes = new Dictionary<EnumType, string> 27 { 28 {EnumType.Normal, "Normal:Dicionary"}, 29 {EnumType.All, "All:Dicionary"}, 30 {EnumType.None, "None:Dicionary"}, 31 }; 32 } 33 } 34} 35

xaml

1<!--MainWindow.xaml--> 2<Window x:Class="WpfBinding.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 7 xmlns:local="clr-namespace:WpfBinding" 8 mc:Ignorable="d" 9 Title="MainWindow" Height="450" Width="600"> 10 <StackPanel> 11 <TextBlock Text="{Binding Path=MyArray[0]}"/> 12 <!--本来書きたい書き方だとエラーになるのでコメントアウト--> 13 <!--<TextBlock Text="{Binding Path=MyArray[(int)EnumType.Normal]}"/>--> 14 <TextBlock Text="{Binding Path=MyTypes[(local:EnumType)Normal]}"/> 15 16 </StackPanel> 17</Window>

C#

1//MaonWindow.xaml.cs 2namespace WpfBinding 3{ 4 /// <summary> 5 /// Interaction logic for MainWindow.xaml 6 /// </summary> 7 public partial class MainWindow : Window 8 { 9 public MainWindow() 10 { 11 InitializeComponent(); 12 DataContext = new MainViewModel(); 13 } 14 } 15} 16

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

Microsoft Visual Studio Community 2022
.NET 6

TN8001😄を押しています
TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

xamlファイル内で元々. 配列[0]の形式でバインドしていた箇所に対して、
添え字の値を列挙型から取得できるような方法を現在模索しています

「マジックナンバーをやめたい(というかわかりやすくしたい)」という動機ですよね?

だったらDictionaryのほうがより望ましいというか、xamlで書きたい内容も大差ないですし、C#側もマジックナンバーが消えると思うのですが...

現行が配列[添え字]の形式でバインドされているので、影響範囲を考えるとできる限り修正が少なく
(可能であればConverterをかませる等)で出来たらうれしいです。

単純なEnumintなら簡単ですが、インデクサだとIMultiValueConverter(xamlがごちゃつく)になっちゃうような??(MarkupExtensionでどうにかなるとは思いますが、特に考えてはいません^^;

MyArray部分が消えていいなら、 ViewModel自体にインデクサを作ってラッパーにすることは簡単です。

cs

1public class MainViewModel 2{ 3 [IndexerName("MyArray2")] 4 public string this[EnumType enumType] => MyArray[(int)enumType];

xml

1<TextBlock Text="{Binding Path=MyArray2[(local:EnumType)Normal]}" />

IndexerNameAttribute クラス (System.Runtime.CompilerServices) | Microsoft Learn

投稿2023/09/19 22:22

編集2025/07/18 15:27
TN8001

総合スコア10166

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

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

add-1914

2023/09/19 23:01

ご回答いただきありがとうございます。 ご認識いただいている通り、マジックナンバーをなくすことでコードを分かりやすくしたいという理由もありますが、動機としては、Enumで定義している列挙型への自由な値追加を行った際に拡張性を持たせておきたいというのがあります。 現在のEnumType の上から1番目に 'Only'のような項目を追加したい場合 public enum EnumType { Normal,    Only, All, None, } のような形で追加します。その際 enum側の追加順がxaml側に影響しないようにしたいと考えています。 ※この要件だとDictionary型に変えてしまった方が良いような気もするのですが、現状こちらのプロパティを 利用している箇所が多く、型を変更した際の影響範囲を加味しつつ、Dictionary型にするのか別方法をとるのかで迷っています。 xamlファイル自体が、現時点で結構構造上複雑といいますかコードが長いのでxamlファイルがごちゃついてしまうのは、保守的な面でも出来れば避けたいなと考えています。 ViewModel自体にインデクサを作ってラッパーにするというのは思いつかなかったので、 一旦現状の構造で実装できるか試してみようと思います。 ありがとうございます。
add-1914

2023/09/19 23:33

連投失礼します。 早速、投稿したコードにてインデクサを実装し色々と試していますが こちらの方法であれば、実現したかったことが少ない修正範囲で実装できそうです。 一度こちらの方法を利用して現行のプログラム側でも実装してみます。 大変助かりました。 ありがとうございます。 また、後学のためにお伺いしたいのですが 上記ような要件があった場合、TN8001さんでしたら 下記どちらの方法を利用されることが多いでしょうか。 ①Dictionary型 ②インデクサの利用 実装手段として思いつくものはあるのですが、まだまだ実装経験が浅く、 どの方法で実装するのかの取捨選択や選択肢の狭さで適切な方法について悩むことがあるので、対応方法選択時に指針とされていることがあれば合わせてお聞きできたら嬉しいです。
TN8001

2023/09/20 00:12

なるほど。順不同も目的の一つと。 > 現状こちらのプロパティを利用している箇所が多く、型を変更した際の影響範囲を加味しつつ、Dictionary型にするのか別方法をとるのかで迷っています。 string[]とDictionary<EnumType, string>でインデクサを使ってる限りは変更箇所ってあります? 初期化コードは変更せざるをえないでしょうが^^; > また、後学のためにお伺いしたいのですが > 上記ような要件があった場合、TN8001さんでしたら 用途にもよるとは思いますが、Dictionaryのほうが素直な感じはします。 あるいは「enumの属性から引いてくる」みたいのもありますね。 [【WPF】Enum を Attribute に設定した値に変換する汎用的な Converter を作った - Qiita](https://qiita.com/39ra8/items/bc736f04df9431f85584) 今回は変更を最小限にしたいということなので、インデクサがお手軽かなと提案した次第です(50個も行く前に検討したかったですが^^; > 単純なEnum→intなら簡単ですが、インデクサだとIMultiValueConverter(xamlがごちゃつく)になっちゃうような?? これは勘違いでした。string[]をもらってstringを返すコンバータ(パラメータにenum)でいいんすね。 でもxamlがごちゃつくのはごちゃつきますw <TextBlock Text="{Binding Path=MyArray, Converter={StaticResource EnumTypeConverter}, ConverterParameter={x:Static local:EnumType.Normal}}" />
add-1914

2023/09/20 09:56

ご回答いただきありがとうございます。 >string[]とDictionary<EnumType, string>でインデクサを使ってる限りは変更箇所ってあります? >初期化コードは変更せざるをえない そうですね、今回のコードであれば初期化箇所以外の変更箇所はないと思います。 INotifyPropertyChangedをを実装するとして、配列に格納されている値と各設定によって動的に切り替えたい等だと変更箇所もあるのかなと考えていました。 >用途にもよるとは思いますが、Dictionaryのほうが素直な感じはします。 ありがとうございます。 確かにアクセスとしても、xamlをよく知らない人が見た場合でもDictionary型の方が一目でわかりやすいですね。 enumの属性から引いてくる というのは初めて知ったので、記載いただいたURLを見て動作を確認してみようと思います。 >今回は変更を最小限にしたいということなので ※今回ですが、コードを改めて確認したところDictionary型に変えた場合の影響範囲が数百行程ありましたのでインデクサでの方法で対応しました。 >単純なEnum→intなら簡単ですが、インデクサだとIMultiValueConverter(xamlがごちゃつく) <TextBlock Text="{Binding Path=MyArray, Converter={StaticResource EnumTypeConverter}, ConverterParameter={x:Static local:EnumType.Normal}}" /> Pathで配列をもらって、stringまたは任意の値を返すコンバータを作成する形でできるのですね、 一度コンバータでのやり方も考えていたのですが配列をどう渡したらいいのかがわからず頓挫していたので こちらも非常に勉強になります。ありがとうございます。 Converterは、バインドされた値を任意の型や形式で受け取ることができるとともに、 本来であればコードをスッキリさせることができるものでもあるのかなと考えているので、 もう少しごちゃついてないソースで試してみます。 教えていただいたインデクサを利用して対応し、無事実装できました。 本当にありがとうございます。 また、バインド先のデータがINotifyPropertyChangedを実装していたので、 タイミングに合わせて、表示を切り替えできるよう、更新通知のタイミングで以下のコードだけ追加しました。 //インデクサ変更通知 RaisePropertyChanged(Binding.IndexerName);
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問