none
配列型の添付プロパティの要素に継承クラスを指定するとデザイナでエラー RRS feed

  • 質問

  • 一つのViewModelに複数のViewを割り当てたかったので、以下のようなWindow[]型の添付プロパティを作成しました

        public static class AP
        {
            public static Window[] GetViewWindows(DependencyObject obj)
            {
                return (Window[])obj.GetValue(ViewWindowsProperty);
            }
            public static void SetViewWindows(DependencyObject obj, Window[] value)
            {
                obj.SetValue(ViewWindowsProperty, value);
            }
            // Using a DependencyProperty as the backing store for ViewWindows.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty ViewWindowsProperty =
                DependencyProperty.RegisterAttached("ViewWindows", typeof(Window[]), typeof(AP), new PropertyMetadata(null, OnViewWindowsChanged));
            private static void OnViewWindowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var ele = d as FrameworkElement;
                ele.DataContextChanged += Ele_DataContextChanged;
                SetDataContextViewWindows(ele);
            }
    
            private static void Ele_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
            {
                var ele = sender as FrameworkElement;
                SetDataContextViewWindows(ele);
            }
    
            private static void SetDataContextViewWindows(FrameworkElement ele)
            {
                var viewwindows = GetViewWindows(ele);
                foreach (var view in viewwindows)
                {
                    view.DataContext = ele.DataContext;
                }
            }
        }

    それを以下のようにWindowから派生したクラス(class SubWindow:)を要素にして使用しています

    <Window x:Class="WpfApplication.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:local="clr-namespace:WpfApplication10"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Window.Style>
            <Style>
                <Setter Property="local:AP.ViewWindows">
                    <Setter.Value>
                        <x:Array Type="{x:Type Window}">
                            <local:SubWindow/>
                        </x:Array>
                    </Setter.Value>
                </Setter>
            </Style>
        </Window.Style>
    </Window>

    コンパイルも通り動作も問題なさそうなのですが、デザイナで「オブジェクトをこの型の配列に格納できません。」というエラーが常に表示されてしまいます。

    何か問題のある状況なのでしょうか?問題がないのならば、このエラーは無視する以外に方法はありますか?

    OSはWindow10、Visual Studio 2015で開発しております。対象のフレームワークは.NET Framework4.5.2です

    2017年11月24日 3:40

回答

  • これはバグとかではなくて、デザイナが意図している状態が、開発者側からだと間違っているように見えるだけでしょうね。
    以下をデザイナで表示させると別の何かになっていることがわかります。

    <StackPanel>
        <StackPanel.Resources>
            <x:Array Type="{x:Type Window}" x:Key="a1">
                <Window /> <!-- Window配列であっても警告はでる-->
            </x:Array>
            <x:Array Type="{x:Type ContentControl}" x:Key="a2">
                <Window /> <!-- ContentControlの配列なら警告は出ない-->
            </x:Array>
            <x:Array Type="{x:Type TextBoxBase}" x:Key="a3">
                <TextBox /> <!-- TextBoxのベースクラスの配列であっても警告はでない-->
            </x:Array>
        </StackPanel.Resources>
    
        <!-- 普通のコントロールはそのままインスタンス化-->
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="TextBox" Width="100" />
            <TextBlock Text="{Binding}"> <TextBlock.DataContext> <TextBox /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="UserControl" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext> <UserControl /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <!-- 特定のコントロールは別の何か -->
        <StackPanel Orientation="Horizontal" Background="#F0F0F0">
            <TextBlock Text="SubWindow" Width="100" />
            <TextBlock Text="{Binding}">
                <TextBlock.DataContext> <local:SubWindow /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Window" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext>  <Window /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal" Background="#F0F0F0">
            <TextBlock Text="Page" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext>  <Page />  </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="WebBrowser" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext> <WebBrowser /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Popup" Width="100" />
            <TextBlock Text="{Binding}"> <TextBlock.DataContext> <Popup /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    </StackPanel>

    デザイン時には指定されたコントロールは生成せずに、たぶん軽量化やリアルタイムの反映をさせるために、デザイン専用に見た目のスタイルのみを適用するコントロールを表示しているんじゃないでしょうか。
    #正確に表示させようとすると、継承クラスの場合はコンパイル結果を参照しないといけないですし、エラーがあると表示できなくなりますし

    このためデザイン時にはArrayに格納しようとしても全く違う型のインスタンスなので、この警告が出てしまうのでしょう。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2017年11月24日 10:18
    • 回答としてマーク ikarimame 2017年11月24日 10:57
    2017年11月24日 10:09

すべての返信

  • コンパイルも動作も問題ないので、デザイナーが完璧でなくうまく解釈できないためにエラーを出している可能性が高いように思います。私も以前、似たような問題で悩んだことがあります。
    (参考)
    添付プロパティに配列を渡す際のVisual Studio 2010 WPFデザイナーのバグ?
    http://blogs.wankuma.com/trapemiya/archive/2010/07/16/191307.aspx
    http://d.hatena.ne.jp/trapemiya/20100716/1279244773

    コンパイルも動作も問題ないのに、デザイナーでエラーを出さないため記述を変えるという解決策がもし存在しており、そのように修正したとしても、アプリケーションの本質とは全く関係ない、デザイナーのための修正ということになります。
    確かにデザイナーでもエラーが消えれば気持ちの良いものでしょうが、ただそれは気持ちが良いだけで、本質的に必要でない修正です。その修正の必要性の良し悪しは、人それぞれでしょう。

    ちなみに、私の方でも試してみましたが、Visual Studio EnterPrise 2017、.NET Framework 4.7 でも同じエラーが発生しました。

    以下に、まだ生きているかどうかわかりませんが、Visual Studio 2015に関するフィードバックを送る方法が示されています。
    もしどうしても解決しない場合は、フィードバックされてみても良いかもしれません。

    Visual Studio で問題を報告する方法
    https://msdn.microsoft.com/ja-jp/library/mt632287.aspx


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2017年11月24日 5:23
    モデレータ
  • これはバグとかではなくて、デザイナが意図している状態が、開発者側からだと間違っているように見えるだけでしょうね。
    以下をデザイナで表示させると別の何かになっていることがわかります。

    <StackPanel>
        <StackPanel.Resources>
            <x:Array Type="{x:Type Window}" x:Key="a1">
                <Window /> <!-- Window配列であっても警告はでる-->
            </x:Array>
            <x:Array Type="{x:Type ContentControl}" x:Key="a2">
                <Window /> <!-- ContentControlの配列なら警告は出ない-->
            </x:Array>
            <x:Array Type="{x:Type TextBoxBase}" x:Key="a3">
                <TextBox /> <!-- TextBoxのベースクラスの配列であっても警告はでない-->
            </x:Array>
        </StackPanel.Resources>
    
        <!-- 普通のコントロールはそのままインスタンス化-->
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="TextBox" Width="100" />
            <TextBlock Text="{Binding}"> <TextBlock.DataContext> <TextBox /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="UserControl" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext> <UserControl /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <!-- 特定のコントロールは別の何か -->
        <StackPanel Orientation="Horizontal" Background="#F0F0F0">
            <TextBlock Text="SubWindow" Width="100" />
            <TextBlock Text="{Binding}">
                <TextBlock.DataContext> <local:SubWindow /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Window" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext>  <Window /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal" Background="#F0F0F0">
            <TextBlock Text="Page" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext>  <Page />  </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="WebBrowser" Width="100" />
            <TextBlock Text="{Binding}">  <TextBlock.DataContext> <WebBrowser /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Popup" Width="100" />
            <TextBlock Text="{Binding}"> <TextBlock.DataContext> <Popup /> </TextBlock.DataContext></TextBlock>
        </StackPanel>
    </StackPanel>

    デザイン時には指定されたコントロールは生成せずに、たぶん軽量化やリアルタイムの反映をさせるために、デザイン専用に見た目のスタイルのみを適用するコントロールを表示しているんじゃないでしょうか。
    #正確に表示させようとすると、継承クラスの場合はコンパイル結果を参照しないといけないですし、エラーがあると表示できなくなりますし

    このためデザイン時にはArrayに格納しようとしても全く違う型のインスタンスなので、この警告が出てしまうのでしょう。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2017年11月24日 10:18
    • 回答としてマーク ikarimame 2017年11月24日 10:57
    2017年11月24日 10:09
  • trapemiya様、gekka様

    返信ありがとうございます

    他の型でも同様の現象が起こっていること
    VisualStudio2017でも同様であること

    デザイナでは一部のコントロールは別の何かでインスタンス化されること
    (確かにWindow関連はデザイナーではMicrosoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.WindowInstanceとしてインスタンス化されていました)

    とてもよく理解できました。

    それらを踏まえとりあえず
    添付プロパティの型をWindow[]からFrameworkElement[]に変更することでデザイナー上のエラーが発生しなくなりました。
    (恐らくはWindowInstanceもFrameworkElementからの派生なのだと推測しております。)
    今度は何でもぼんぼん放り込めるようになってしまったので、実行時の型確任等で逃げようかと思っております。
    2017年11月24日 11:11