nakamurakko’s diary

たまに書く

WPFアプリケーションからMainWindow.xaml.csを削除したい

WPFアプリケーションを作成すると、メインウィンドウの下記ファイルが作成される。

MainWindow.xaml.csは下記のとおり、コンストラクタでInitializeComponentが呼ばれているだけの簡素な状態。

using System.Windows;

namespace WpfNoXamlSample
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

「MVVMで開発する時でウィンドウ側に処理を書かない場合、これは要らないのでは?」と思い、MainWindow.xaml.csのみを削除してビルドしてみたら、正常にビルドできて、実行もできた。

ちなみに、Visual Basic(VB.NET)でプロジェクトを作成した場合のMainWindow.xaml.vbは下記のとおり。

Class MainWindow

End Class

InitializeComponentが無い…。ということは要らないのでは…。

MVVMを考えたら処理をウィンドウクラス.xaml.csに書きたくないし、そもそもファイルが無ければ余計な事を考えなくて済むので、問題ないのであれば削除したい。

誰か教えてください。

DocumentCompositeNodeにキャストできません

実行環境:Visual Studio 2017 Professional

困った事象

WPFで、Styleなどを定義した外部ResourceDictionaryをExpander.HeaderTemplate内に適用したくて、次のように書いた。

<Window x:Class="WpfDataTemplateStyleNG.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:WpfDataTemplateStyleNG"
        mc:Ignorable="d"
        Title="WpfDataTemplateStyleNG"
        Height="200"
        Width="300">

    <StackPanel>
        <Expander>
            <Expander.HeaderTemplate>
                <DataTemplate>
                    <DataTemplate.Resources>
                        <!--DataTemplateで外部リソースを設定。-->
                        <ResourceDictionary Source="Styles.xaml" />
                    </DataTemplate.Resources>

                    <TextBlock Text="Expander.HeaderTemplate上のTextBlock" />
                </DataTemplate>
            </Expander.HeaderTemplate>

        </Expander>

        <TextBlock Text="StackPanel上のTextBlock" />
    </StackPanel>

</Window>

コードは問題なさそうだけど、こんなエラーが出た。(ただ、エラーにはなるけど、ビルドは正常に完了するし、実行も問題なくできる。)

'OnDemandResourceDictionary' のオブジェクトを型 'Microsoft.VisualStudio.DesignTools.Markup.DocumentModel.DocumentCompositeNode' にキャストできません。

調べてみると、Microsoftに報告されていて、対応しないことになっている。

XAML designer broken when adding resource dictionaries to data or control templates


回避策

エラーになるのは困るので、回避策としては、

  • StaticResourceを使う。
  • Template内で使用する前にカスタムコントロール化して、カスタムコントロールの方で外部リソースを読み込む。

という感じですかね。

ちなみに、上記Expanderの場合は次のようにHeaderTemplateではなく、Headerに直接書けば回避できた。

<Window x:Class="WpfDataTemplateStyle.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:WpfDataTemplateStyle"
        mc:Ignorable="d"
        Title="WpfDataTemplateStyle"
        Height="200"
        Width="300"
        FontSize="15">

    <StackPanel>
        <Expander>
            <Expander.Header>
                <HeaderedContentControl>
                    <HeaderedContentControl.Resources>
                        <!--これはエラーにならない。-->
                        <ResourceDictionary Source="Styles.xaml" />
                    </HeaderedContentControl.Resources>

                    <TextBlock Text="Expander.HeaderTemplate上のTextBlock" />
                </HeaderedContentControl>
            </Expander.Header>

        </Expander>

        <TextBlock Text="StackPanel上のTextBlock" />
    </StackPanel>

</Window>

ControlTemplate、DataTemplateで外部リソースを書いてもエラーにならない場合があるけど、条件は分からない。

Visual Studio 2017のキーマップを変更して、CTRL + PageUp、CTRL + PageDownでタブ切り替えしたい。(自分用)

キーボードマップ スキームが「既定」の場合に、オプション - 環境 - キーボードで設定する。

  • CTRL + PageUp
    • 「編集.上端まで移動」を選択して、ショートカットを削除。
    • 「ウィンドウ.前のタブ」にCTRL + PageUpが割り当てられているか確認。
  • CTRL + PageDown
    • 「編集.下端に移動」を選択して、ショートカットを削除
    • 「ウィンドウ.次のタブ」にCTRL + PageDownが割り当てられているか確認。

Visual Studio 2012以降なら同じように設定できる気がする。

WPF StatusBarの項目を右寄せ

StatusBarの項目を右寄せしたいと思っていたら、StatusBarItemに「DockPanel.Dock="Right"」を指定すればいいらしい。
(ただ、DockPanelを使っていないのにDockPanel.Dockが使える理由が分からない。)

ソース

<Window x:Class="WpfStatusBarSample.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:WpfStatusBarSample"
        mc:Ignorable="d"
        Title="WpfStatusBarSample"
        Height="200"
        Width="300">
    <Grid>

        <!--ステータスバー。-->
        <StatusBar VerticalAlignment="Bottom">

            <!--「DockPanel.Dock="Right"」で右に寄せている。-->
            <StatusBarItem DockPanel.Dock="Right">
                Right
            </StatusBarItem>

            <!--左寄せにしたい項目は後で定義する。-->
            <StatusBarItem>
                Left
            </StatusBarItem>

        </StatusBar>

    </Grid>
</Window>

実行結果
f:id:nakamurakko:20170316223543p:plain

C# Lambdaでループを書き換えてみる

「yyyyMMdd」形式の文字列を用意する。 

  1. // 「yyyyMMdd」形式で日付を表す文字列のリスト。
  2. List<string> stringDateTimes = new List<string>();
  3. stringDateTimes.Add("20161011");
  4. stringDateTimes.Add("20150303");
  5. stringDateTimes.Add("20161215");

foreachでループさせて出力

  1. foreach (string stringDateTime in stringDateTimes)
  2. {
  3.     Console.WriteLine(DateTime.ParseExact(stringDateTime, "yyyyMMdd", null));
  4. }

Selectメソッドで取り出した結果をLambdaでDateTimeのコレクションに変換してから出力

  1. IEnumerable<DateTime> dateTimes = stringDateTimes.Select(
  2.     (string stringDateTime) =>
  3.     {
  4.         return DateTime.ParseExact(stringDateTime, "yyyyMMdd", null);
  5.     }
  6. );
  7. foreach (DateTime d in dateTimes)
  8. {
  9.     Console.WriteLine(d.ToString());
  10. }

Selectメソッドで取り出した結果をLambdaでDateTimeのコレクションに変換してから出力(varを使ったり、式形式のLambdaに変えたりして記述)

  1. var dateTimes2 = stringDateTimes.Select(
  2.     (string stringDateTime) => DateTime.ParseExact(stringDateTime, "yyyyMMdd", null)
  3. );
  4. foreach (DateTime d in dateTimes)
  5. {
  6.     Console.WriteLine(d.ToString());
  7. }

ForEachメソッドで繰り返し、LambdaでDateTimeに変換と出力

  1. stringDateTimes.ForEach(
  2.     (string stringDateTime) =>
  3.     {
  4.         DateTime d = DateTime.ParseExact(stringDateTime, "yyyyMMdd", null);
  5.         Console.WriteLine(d.ToString());
  6.     }
  7. );

Lambdaはいつも混乱するからメモ代わりに。