初めてのWPF 9日目

やりたいこと

起動時に押せるボタン:ファイルオープン、CSVインポート
起動時にアクティブなボタン


データ入力後に押せるボタン:保存、印刷プレビュー、印刷
データ入力後にアクティブなボタン


データを空にすると起動時と同じ状態
データを空にするとアクティブなボタン

MaterialDesignToolBarを使う

View側は楽勝。TextBoxやCheckBoxはスタイルの指定すら不要。
ToolBarのRegionをどうするかで迷いましたが、とりあえずModule側の基本画面に表示することに。
<ToolBarTray DockPanel.Dock="Top">
    <ToolBar Style="{DynamicResource MaterialDesignToolBar}" ClipToBounds="True">
        <Button ToolTip="ファイルを開く" Command="{Binding FileOpenCommand}">
            <materialDesign:PackIcon Kind="Folder" />
        </Button>
        <Button ToolTip="データを保存" Command="{Binding FileSaveCommand}">
            <materialDesign:PackIcon Kind="Floppy" />
        </Button>
        <Button ToolTip="CSVファイルをインポート" Command="{Binding CsvImportCommand}">
            <materialDesign:PackIcon Kind="FileDelimited" />
        </Button>
        <Separator />
        <Button ToolTip="印刷プレビュー" Command="{Binding PrintPreviewCommand}">
            <materialDesign:PackIcon Kind="FileFind" />
        </Button>
        <Button ToolTip="印刷" Command="{Binding PrintCommand}">
            <materialDesign:PackIcon Kind="Printer" />
        </Button>
    </ToolBar>
</ToolBarTray>

DelegateCommandを使う

NameSpace追加

using Prism.Commands;

アイコンの数だけDelegateCommand??

public DelegateCommand FileOpenCommand { get; private set; } 
public DelegateCommand FileSaveCommand { get; private set; }
public DelegateCommand CsvImportCommand { get; private set; }
public DelegateCommand PrintPreviewCommand { get; private set; }
public DelegateCommand PrintCommand { get; private set; }

ボタンのCanExecuteを制御

チェックボックスで制御するのはサンプル通りなので直ぐに出来ましたが、保存・印刷可能なコンテンツがあるかどうかで制御したいわけでTextBoxが空かどうかで制御しようとしたらどうしたらいいのか分からず。
テキストボックスの値が変わった時にRaiseCanExecuteChangedを呼んでも動きましたが、最終的にはObservesPropertyを使ってとりあえず動いたよ。
// bootをプロパティに変える
private bool HaveData()
{
    return !string.IsNullOrEmpty(InputData);
}
private bool NoData()
{
    // 微妙ですが…
    return string.IsNullOrEmpty(InputData);
}

public DelegateCommand FileOpenCommand { get; private set; } 
public DelegateCommand FileSaveCommand { get; private set; }
public DelegateCommand CsvImportCommand { get; private set; }
public DelegateCommand PrintPreviewCommand { get; private set; }
public DelegateCommand PrintCommand { get; private set; }

public CashbookViewModel()
{
    // ObservesPropertyでInputDataの変化を監視し、NoDataを再評価する感じ?
    FileOpenCommand = new DelegateCommand(FileOpen, NoData).ObservesProperty(() => InputData);
    FileSaveCommand = new DelegateCommand(FileSave, HaveData).ObservesProperty(() => InputData);
    CsvImportCommand = new DelegateCommand(CsvImport, NoData).ObservesProperty(() => InputData);
    PrintPreviewCommand = new DelegateCommand(PrintPreview, HaveData).ObservesProperty(() => InputData);
    PrintCommand = new DelegateCommand(Print, HaveData).ObservesProperty(() => InputData); 
}
監視対象のフィールドが複数ある場合はチェインでつなぐことも可能なようです。

初めてのWPF 7日目

モジュール側からMenuServiceを参照

またまたいてまえ系コーディング。やってみたら動いたよ…。
HomeModuleのViewAのViewModelで。
public ViewAViewModel(IMenuService menuService)
{
    MainMenuItems = menuService.GetMainMenuItems();
    Message = "Home Module";
}
デバッグしてここで待ち受けているとMainMenuItemsの中は空(HomeModuleのOnInitializedよりも先にこちらを通る)だったのですが、実行するとメニューが表示されました。
Prism MenuService

MaterialDesignのCardを使う

Cardで包んでボタンをAccentButtonに変えたらグッと良くなるはず、と思ってましたがいまいち垢抜けません…。
WrapPanelがWrapしてくれず苦労しました。MenuItemを拡張してグリッドの位置をモデルから渡そうかと思ったくらい。
MaterialDesignのCard(WPF)

<Grid>
    <ItemsControl ItemsSource="{Binding MainMenuItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
            <DataTemplate>
            <materialDesign:Card Margin="12" Padding="16" Width="280">
                    <StackPanel>
                    <TextBlock Style="{DynamicResource MaterialDesignTitleTextBlock}" Text="{Binding Title}"></TextBlock>
                    <TextBlock Text="{Binding Description}" Margin="10 8 0 0"></TextBlock>
                    <Button
                        Margin="12 16 12 0"   
                        Style="{StaticResource MaterialDesignRaisedAccentButton}"
                        Command="{Binding Path=DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding NavigatePath}">
                        <TextBlock Margin="6 0 0 0" Style="{StaticResource MaterialDesignBody1TextBlock}"
                           Text="OPEN" />
                    </Button>
                </StackPanel>
                 </materialDesign:Card>
             </DataTemplate>
        </ItemsControl.ItemTemplate>
     </ItemsControl>
</Grid>

画像を追加する前に発行後のフォルダ構成を確認

発行方法は3種類あるようですが、一番簡単そうなXCOPYでいくことに。
ところがその方法を書いてくれていない。
普通に「発行」するとexeが出来ずにインストーラーが出来てました。
ビルドをリリースに変えてbinフォルダの中にできたものをデスクトップに移動しexeをクリックしたらちゃんと動きました。
WPF XCOPY


現在は親からモジュールへ参照を張っているのでモジュールのdllも一緒にリリースビルドに含まれています。
dllの並び見ているとモジュールのプロジェクト名を変えたくなってきて一旦終了。

初めてのWPF 5日目

起動時はHomeモジュール表示
BasicRegionNavigation 1


ハンバーガートグルボタンでメインメニュー表示
BasicRegionNavigation 2


ドロワー上のボタンをクリックしてモジュール切り替え
BasicRegionNavigation 3

モジュール間の切り替えをBasicRegionNavigationに変更

Prism6版を確認したら、NavigateCommandを親プロジェクトの既存のViewModelに移動しても問題なく動いたとあった(記憶には無かった)のでモジュール側のメニューはViewだけを残して他はバッサリ削除。
「動かん!?」と思ったらNavigate先のRegion名を間違えてました(Typo)。
エラー出してよ、と思いました。

Viewもテンプレート化すれば重複減らせそうですが、それは次の課題。

メインメニューをMaterialDesignのDrawerに変更

デスクトップアプリでメインメニューにDrawer使うとか他人様にやられると「はぁ?」という感じですが、ものは試しで(^^;)。

MainWindowViewModelにメニュー開閉用のプロパティを追加

setの見慣れない書き方はよく分かってませんが、Titleの真似で。
private bool _mainMenuIsOpen = false;
public bool MainMenuIsOpen
{
    get { return _mainMenuIsOpen; }
    set { SetProperty(ref _mainMenuIsOpen, value); }
}

MainWindow.xamlのトグルボタンにバインド

2箇所あるので要注意
<materialDesign:DrawerHost IsLeftDrawerOpen="{Binding ElementName=MenuToggleButton, Path=IsChecked}">
    <materialDesign:DrawerHost.LeftDrawerContent>
        <DockPanel MinWidth="212">
            <ToggleButton Style="{StaticResource MaterialDesignHamburgerToggleButton}" 
                                DockPanel.Dock="Top"
                                HorizontalAlignment="Right" Margin="16"
                                IsChecked="{Binding MainMenuIsOpen}" />
            <StackPanel Margin="0">
                <ItemsControl x:Name="NavigationItemsControl" prism:RegionManager.RegionName="MainNavigationRegion" Margin="0" Padding="0" />
            </StackPanel>
        </DockPanel>
    </materialDesign:DrawerHost.LeftDrawerContent>
    <DockPanel>
        <materialDesign:ColorZone Padding="16" materialDesign:ShadowAssist.ShadowDepth="Depth2"
                                Mode="PrimaryMid" DockPanel.Dock="Top">
            <DockPanel>
                <ToggleButton Style="{StaticResource MaterialDesignHamburgerToggleButton}" IsChecked="{Binding MainMenuIsOpen}"
                                    x:Name="MenuToggleButton"/>
                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="22" Text="{Binding Title}"></TextBlock>
            </DockPanel>
        </materialDesign:ColorZone>
・・・・

NavigateCommandで遷移時にfalseに

private void Navigate(string navigatePath)
{
    MainMenuIsOpen = false;

    if (navigatePath != null)
      _regionManager.RequestNavigate("ContentRegion", navigatePath);
       
}

初めてのWPF 3日目

2018/12/21 WPF::Material Design

MaterialDesignを適用する

WPF+Prism+MaterialDesign

NuGet

MaterialDesignThemesとMaterialDesignColors

App.xaml

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />

        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

MainWindow.xaml

<Window x:Class="TreasurerHelper.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="400" Width="650"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        TextElement.Foreground="{DynamicResource MaterialDesignBody}"
        TextElement.FontWeight="Regular"
        TextElement.FontSize="13"
        TextOptions.TextFormattingMode="Ideal" 
        TextOptions.TextRenderingMode="Auto"        
        Background="{DynamicResource MaterialDesignPaper}"
        FontFamily="{DynamicResource MaterialDesignFont}">
    <DockPanel>
        <materialDesign:ColorZone Padding="16" materialDesign:ShadowAssist.ShadowDepth="Depth2"
                                    Mode="PrimaryMid" DockPanel.Dock="Top">
            <DockPanel>
                <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="22" Text="{Binding Title}"></TextBlock>
            </DockPanel>
        </materialDesign:ColorZone>
        <Grid>
            <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        </Grid>
    </DockPanel>
</Window>

Module側のView

…
         prism:ViewModelLocator.AutoWireViewModel="True"
         xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<Grid  ShowGridLines="True">
    <materialDesign:Card Padding="32" Margin="16">
        <TextBlock Text="{Binding Message}" Style="{DynamicResource MaterialDesignTitleTextBlock}"></TextBlock>
    </materialDesign:Card>
</Grid>

初めてのWPF

PrismとMaterialDesignを使って初めてのWPFアプリを作成中。
機能はエクセル使えよ!(実際に現在はエクセル使用中)なものですが、勉強のため。
独学でどこまでいけるでしょうか。
チュートリアル放置していた間に-Prismのバージョン上がってやる気が失せたので仕切りなおし。
1日目Prism Blank App(WPF)とPrism Module(WPF)を使ってプロジェクトを作成
2日目ModuleにViewを追加しApp側に表示
3日目MaterialDesign(MaterialDesignThemesとMaterialDesignColors)を適用する
4日目モジュール間の切り替えを行うメインメニュー追加
5日目モジュール間の切り替えをBasicRegionNavigationに変更
メインメニューをMaterialDesignのDrawerに変更
6日目モジュール間の切り替えをMenuServiceに変更
ItemTemplateを使ってViewの重複を除く
7日目トップページにMaterialDesignのCardを使ってモジュール一覧表示
8日目画像の表示
親ViewのViewModelのコマンドをモジュールのViewから呼び出す
9日目MaterialDesignToolBarでツールバーをつくる
DelegateCommandでボタンの活性/非活性を制御する
10日目PrismのCustomPopupを使う
CustomPopupの見た目をMaterialDesignのDialogっぽくする
11日目NavigationCallbackを使ってMainWindowにモジュールのタイトル追加
12日目MVVM1回目
DomainObjectを継承したModelのListをDataGridにバインド
13日目MVVM2回目
行合計(subtotal)の総合計(grandtotal)の取り方が分からない
結局決算に間に合わず中断中。GitHubに上げていますが初心者のためのチュートリアルではなく、WPF初心者が試行錯誤中のものですのでとんでもないバグが潜んでいるかも知れません。
OK キャンセル 確認 その他