WPF イベントのバインディングについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
kakashi

WPF イベントのバインディングについて

#1

投稿記事 by kakashi » 4年前

はじめまして

下記サイトで紹介されているプログラムをもとに、改造してみてうまく動かなかったプログラムを添付いたしました。

サイト:http://blog.basyura.org/entry/2016/03/19/235427
改造後プロジェクト一式:http://whitecats.dip.jp/up/download/156 ... 516552.rar
DLパスワード:1234

[1] 質問文
 [1.1] 自分が今行いたい事は何か
サイトのプロジェクトをMVVM化したいので、まずは、UserControl(SampleControl)のコマンドをVMへ移動を計画
そこで、「RaiseSampleEvent」ボタンのClickイベントをEventToCommandを使ってVMへ記述したい

 [1.2] どのように取り組んだか(プログラムコードがある場合記載)
詳しくはプロジェクト参照ください。
簡単に抜粋しました
SampleControl.xaml

コード:

        <Button Grid.Row="1" Content="RaiseSampleEvent" Margin="0 10 0 0">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <mvvm:EventToCommand Command="{Binding SomeCommand, Mode=TwoWay}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
MainWindowsViewModel.cs

コード:

    public class MainWindowsViewModel {
        public RelayCommand<RoutedEventArgs> SomeCommand { get; private set; }
        public MainWindowsViewModel() {
            SomeCommand = new RelayCommand<RoutedEventArgs>((param) => {
                int aaa;
                aaa = 1; // ここにブレイクポイントを張るが、イベントが呼ばれない
            });
        }
    }
MainWindow.xaml

コード:

<Window x:Class="WPFSample_SwitchEventArgs.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:WPFSample_SwitchEventArgs"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <my:MainWindowsViewModel/>
    </Window.DataContext>
    <Grid>
        <my:SampleControl 
                          MouseLeftButtonDown="SampleControl_MouseLeftButtonDown"
                          SampleChanged="SampleControl_SampleChanged"
                          />
        
    </Grid>
</Window>
 [1.3] どのようなエラーやトラブルで困っているか(エラーメッセージが解る場合は記載)
 [1.4] 今何がわからないのか、知りたいのか
UserControl(SampleControl)のRaiseイベントより、MainWindowViewModelのSomeCommandが動作してほしい
(現在は動作しない)
どのように実装すればよいのか?

以上になります。
お手数をおかけしますが、よろしくお願いいたします。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: WPF イベントのバインディングについて

#2

投稿記事 by YuO » 4年前

今回の問題の解決方法としては、
  • SampleControlの中ではTriggerを記述しない
  • SampleControlのTriggerを書いていたボタンのClickイベントをRaiseSampleEventメソッドに紐付ける
  • MainWindow側で、SampleChangedに対してTriggerを設定する
の3点ことによって、MainWindowsViewModelのSomeCommandを呼び出すことが出来ます。

RaiseSampleEventメソッドが呼ばれない、というのを希望しているようですが、
メソッドを経由してイベントを発火させてした方が綺麗に作れます。

メソッドを呼ばない場合だと、ICommandを受け付ける依存関係プロパティを用意して、
PropertyMetaDataのPropertyCallbackで[RaiseSampleEvent]イベントのCommandに紐付ける、といった作りが必要になります。

-- 以下は理由等 --
kakashi さんが書きました:
4年前
SampleControl.xaml

コード:

        <Button Grid.Row="1" Content="RaiseSampleEvent" Margin="0 10 0 0">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <mvvm:EventToCommand Command="{Binding SomeCommand, Mode=TwoWay}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
MainWindow.xaml

コード:

        <my:SampleControl 
                          MouseLeftButtonDown="SampleControl_MouseLeftButtonDown"
                          SampleChanged="SampleControl_SampleChanged"
                          />
※引用者によって一部を抜粋。

UserControlはUserControl内で参照関係を閉じる必要があります。
今回の場合は、UserControl (とそれに付属するViewModel) の外にある、
MainWindowsViewModelのSomeCommandをバインドしようとしているために問題が生じています。
オフトピック
一応、DataContextの設定がない場合は親のFrameworkElementを遡ってDataContextを使いますが、
今回はMvvmLightの機能によって、自動的にSampleControlにはSampleControlViewModelがバインドされます。
SampleControlのボタンに割り当てられたEventToCommandが参照するSomeCommandは、SampleControlViewModelのSomeCommandになります。
そして、このプロパティは見つからないため、SomeCommandのバインドは行われません。
[出力] - [デバッグ]には、

コード:

System.Windows.Data Error: 40 : BindingExpression path error: 'SomeCommand' property not found on 'object' ''String' (HashCode=-899346247)'. BindingExpression:Path=SomeCommand; DataItem='String' (HashCode=-899346247); target element is 'EventToCommand' (HashCode=11903911); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'SomeCommand' property not found on 'object' ''SampleControlViewModel' (HashCode=21520579)'. BindingExpression:Path=SomeCommand; DataItem='SampleControlViewModel' (HashCode=21520579); target element is 'EventToCommand' (HashCode=11903911); target property is 'Command' (type 'ICommand')
というエラーが報告されています。
オフトピック
前者は、SampleControl.xamlにDataContextがテキストで書かれている為に発生していると思われます。
SampleControlの参照関係を閉じるには、SomeCommandをSampelControlで使わないという方法しかありません。
幸い、[RaiseSampleEvent]ボタンのクリックイベントは残骸がコードビハインドにあるため、これに紐付けることが出来ます。
これにより、[RaiseSampleEvent]ボタンを押すとコントロールが[SampleChanged]イベントを発生させます。

あとは、Clickなどのイベントと同じく、親側 (=MainWindow側) で、SampleChangedイベントを購読すればよくなります。
Clickにやったのと同じようにTriggerを設定してやれば、SampleChangedイベントの発生を拾ってSomeCommandを呼び出してくれます。

kakashi

Re: WPF イベントのバインディングについて

#3

投稿記事 by kakashi » 4年前

Yuo様

ご教授いただき誠にありがとうございました。
アドバイスいただいた通り、下記のように行うことで、無事イベントが発生しました。
MainWindow.xaml

コード:

...
    <Grid>
        <my:SampleControl>
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SampleChanged">
                    <mvvm:EventToCommand Command="{Binding SomeCommand}" PassEventArgsToCommand="True"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </my:SampleControl>
    </Grid>
...
SampleControl.xaml

コード:

<UserControl x:Class="WPFSample_SwitchEventArgs.SampleControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid VerticalAlignment="Center" HorizontalAlignment="Center">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button Grid.Row="0" Content="SwitchEvent"      Click="SwitchEvent" />
        <!--これはうまくいく-->
        <Button Grid.Row="1" Content="RaiseSampleEvent" Click="RaiseSampleEvent" Margin="0 10 0 0"/>
    </Grid>
</UserControl>
現在、formからwpfへ(やっと)移行する決心で勉強しております。
MVVMの考え方が正直、理解できていないことが問題ですが、とにかく
イベント(RaiseSampleEvent)やプロパティ(SampleChanged)はMVに定義するものだと決めつけ、
何もかもバインドして実態をコードビハインドから抹消することが目的だと勘違いしておりました(今でもしれいるかもしれません)

何をどこに書くことがよいのかこれから少しずつ勉強(実践)していきます。
ありがとうございました。

kakashi

Re: WPF イベントのバインディングについて

#4

投稿記事 by kakashi » 4年前

連投すみません。送信してしまいました。
最後に一点だけ教えてください。

コード:

<Button Grid.Row="1" Content="RaiseSampleEvent" Click="RaiseSampleEvent" Margin="0 10 0 0"/>
ContentとClickはなぜ同じものを指定しているのでしょうか?
もし以下のように、多数のイベントをRaiseする場合、どのようにするのでしょうか?

コード:

<Button Grid.Row="1" Content="RaiseSampleEvent(?)" Click="RaiseSampleEvent" DoubleClick="こんなイベントはありませんが" Margin="0 10 0 0"/>

Math

Re: WPF イベントのバインディングについて

#5

投稿記事 by Math » 4年前

あんたひどい勘違いしてるよ。
Formでも xaml は使えるし データバインディングできるよ。
MVVMhttps://ja.wikipedia.org/wiki/Model_View_ViewModelってこう言うことだから全然違うよ。

だいいち基本技術がちがうぜ。ひどいサイトにあたったのが不運ですね。
明日夕方までは忙しいからそれからで良ければ教えてあげます。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: WPF イベントのバインディングについて

#6

投稿記事 by YuO » 4年前

kakashi さんが書きました:
4年前

コード:

<Button Grid.Row="1" Content="RaiseSampleEvent" Click="RaiseSampleEvent" Margin="0 10 0 0"/>
ContentとClickはなぜ同じものを指定しているのでしょうか?
  • Content : コントロールの内部に描画される内容を設定します。普通は属性としてではなく、要素の内容として記述します。

    コード:

    <Button Grid.Row="1" Click="RaiseSampleEvent" Margin="0 10 0 0">RaiseSampleEvent Content</Button>
    こちらの書き方の場合、内容であるRaiseSampleEvent Contentの部分は別のコントロールを指定することも出来ます (ImageとTextBlockを載せたStackPanelなど)。
  • Click : コントロールのClickイベントのハンドラを指定します。Buttonでは、Command依存関係プロパティにICommandをバインドしてClickイベントは放置する方が多いと思います。
    今回のように、読み替えるのであればClickイベントを使うと思いますが。
kakashi さんが書きました:
4年前
もし以下のように、多数のイベントをRaiseする場合、どのようにするのでしょうか?

コード:

<Button Grid.Row="1" Content="RaiseSampleEvent(?)" Click="RaiseSampleEvent" DoubleClick="こんなイベントはありませんが" Margin="0 10 0 0"/>
  • Contentには、自由に値を設定できます。
  • Clickには、Clickイベントに対応するイベントハンドラの名前を記述します。
  • DoubleClickがイベントだとすると、DoubleClickには、DoubleClickイベントに対応するイベントハンドラの名前を記述します。

Math

Re: WPF イベントのバインディングについて

#7

投稿記事 by Math » 4年前

Hey!You
viewtopic.php?f=3&t=18676&p=141885&hilit=wpf#p141992
ここに述べているようにWPFを理解するには遠いひとたちです。
MVVMの基本技術をあなたはどういう風に理解されてますか。
(それとPowershellは使われていますか?)

返信

“C言語何でも質問掲示板” へ戻る