トップ回答者
WPF CheckBox.IsCheckedを双方向バインディングする方法

質問
-
下の様なテストプログラムを作成したのですが、動作しません。
不具合は3つあります。
1.Window_Loadedイベントの中でlib_1.P1 = Trueとしてもコントロールに反映されない。
2.CheckBox_1コントロールでチェックマークをON/OFFできるが、Debug.Print("### " & value)の行が実行されない。
3.XLS0518 インスタンス メンバーである型 'lib_1' の 'P1' が必要です。というコンパイルエラーが間歇的に発生する。リビルドするとエラーが無くなるが、Visual Studioを閉じて再起動するとまた発生する。XAMLの <CheckBox Content=" -----" Height="20" ~ の行で発生しているのだが、どこに原因があるのか分からない。
不具合の解消法を教えて下さい。
開発環境
Win7 64ビット
Microsoft Visual Studio Community 2017
Version 15.9.9
VisualStudio.15.Release/15.9.9+28307.518
Microsoft .NET Framework
Version 4.7.02558
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="xxx" Height="628.145" Width="619.398"
xmlns:local="clr-namespace:qqqq"
DataContext="{DynamicResource Custom01}">
<Window.Resources>
<local:lib_1 x:Key="Custom01" />
</Window.Resources>
<Grid Margin="0,0,0,0">
<CheckBox Content=" -----" Height="20" IsChecked="{Binding Custom01.P1, Mode=TwoWay}" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="CheckBox_1" VerticalAlignment="Top" Width="154" />
</Grid>
</Window>
ソースコード
Imports System.ComponentModel
Class MainWindow
Public Sub New()
' この呼び出しはデザイナーで必要です。
InitializeComponent()
' InitializeComponent() 呼び出しの後で初期化を追加します。
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
lib_1.P1 = True
Debug.Print(lib_1.P1)
End Sub
End Class 'MainWindow
Public Class lib_1
Private Shared Property _p1 As Boolean
Public Shared Property P1 As Boolean
Get
Return _p1
End Get
Set(value As Boolean)
_p1 = value
Debug.Print("### " & value) '←チェックボックスを切り替えてもここに飛ばない
NotifyPropertyChanged(New PropertyChangedEventArgs("1@"))
End Set
End Property
Private Shared Event PropertyChanged As PropertyChangedEventHandler
Private Shared Sub NotifyPropertyChanged(e As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Nothing, e)
End Sub
End Class
回答
-
普通に書いてるので、自分はやったことないけどまあできるんだろうな、と思ってスルーしていたのですが。
改めて確認したところ、Sharedプロパティでは双方向バインディングはできませんね。ソースからターゲットへの通知が行われない。SharedなPropertyChangedにはイベントハンドラが登録されない。
ということでSharedプロパティを使うのは諦めてください。アプリケーション全体で1つのインスタンスを参照したいのであれば、Sharedインスタンスのインスタンスプロパティを見る形にしましょう。例えば以下のような。
Public Class lib_1 Implements INotifyPropertyChanged Public Shared ReadOnly Property Instance As New lib_1 Private Property _p1 As Boolean Public Property P1 As Boolean Get Return _p1 End Get Set(value As Boolean) If _p1 <> value Then _p1 = value NotifyPropertyChanged(New PropertyChangedEventArgs(NameOf(P1))) End If End Set End Property ' INotifyPropertyChanged周りの実装は省略 End Class
<Window ... DataContext="{x:Static local:lib_1.Instance}"> <CheckBox IsChecked={Binding P1, Mode=TwoWay}"/> </Window>
- 回答としてマーク huahi11112 2019年5月23日 7:43
すべての返信
-
直接の回答ではありませんが、DynamicResourceを使っている意味は何でしょうか? もし、無いのであれば使わない方法もあります。
ただし、こうしても「インスタンス メンバーである型 'lib_1' の 'P1' が必要です」というエラーが無くなるわけではありません。<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="xxx" Height="628.145" Width="619.398" xmlns:local="clr-namespace:qqqq" > <Window.DataContext> <local:lib_1 /> </Window.DataContext> <Grid Margin="0,0,0,0"> <CheckBox Content=" -----" Height="20" IsChecked="{Binding P1, Mode=TwoWay}" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="CheckBox_1" VerticalAlignment="Top" Width="154" /> </Grid> </Window>
★良い回答には質問者は回答済みマークを、閲覧者は投票を!
-
普通に書いてるので、自分はやったことないけどまあできるんだろうな、と思ってスルーしていたのですが。
改めて確認したところ、Sharedプロパティでは双方向バインディングはできませんね。ソースからターゲットへの通知が行われない。SharedなPropertyChangedにはイベントハンドラが登録されない。
ということでSharedプロパティを使うのは諦めてください。アプリケーション全体で1つのインスタンスを参照したいのであれば、Sharedインスタンスのインスタンスプロパティを見る形にしましょう。例えば以下のような。
Public Class lib_1 Implements INotifyPropertyChanged Public Shared ReadOnly Property Instance As New lib_1 Private Property _p1 As Boolean Public Property P1 As Boolean Get Return _p1 End Get Set(value As Boolean) If _p1 <> value Then _p1 = value NotifyPropertyChanged(New PropertyChangedEventArgs(NameOf(P1))) End If End Set End Property ' INotifyPropertyChanged周りの実装は省略 End Class
<Window ... DataContext="{x:Static local:lib_1.Instance}"> <CheckBox IsChecked={Binding P1, Mode=TwoWay}"/> </Window>
- 回答としてマーク huahi11112 2019年5月23日 7:43
-
ありがとうございます。お二方の御回答を取り入れ、下記の様にして動作させました。
(1) Me.DataContext は仕方無くインスタンスを代入しました。
(2) CheckBox.IsCheckedをバインドしたプロパティー(P1)で操作したかったのですが、動かなかったのでCheckBox.IsCheckedを直接操作しています。
VB.netで回答して下さる方がいらっしゃるので本当にこのフォーラムは役に立ちます。御回答誠にありがとうございました。
<<MainWindowのプロパティーに双方向バインドしたCheckBoxの作り方>>
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="xxx" Height="228.145" Width="481.398" >
<Grid Margin="0,0,0,0" >
<CheckBox Content=" -----" Height="20" IsChecked="{Binding P1, Mode=TwoWay}" HorizontalAlignment="Left" Margin="38,53,0,0" Name="CheckBox_1" VerticalAlignment="Top" Width="154" />
<Button x:Name="Button1" Content="Check ON/OFF" HorizontalAlignment="Left" Height="47" Margin="227,53,0,0" VerticalAlignment="Top" Width="159" />
<Button x:Name="Button2" Content="Show P1" HorizontalAlignment="Left" Height="36" Margin="227,133,0,0" VerticalAlignment="Top" Width="108" />
</Grid>
</Window>
''''''''''''''''''''''''
Imports System.ComponentModel
Class MainWindow
Public Property li1 As New lib_1 'lib1を共有クラスにして使うことはできない ①の行でコンパイルできなくなる
Public Sub New()
' この呼び出しはデザイナーで必要です。
InitializeComponent()
' InitializeComponent() 呼び出しの後で初期化を追加します。
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Me.DataContext = Me.li1 '① 右辺はインスタンスでなければならない
End Sub
Private Sub B11_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
Dim b As Boolean = Not li1.P1
'li1.P1 = b ← こうすると動かない(CheckBoxコントロールに反映されない)
Me.CheckBox_1.IsChecked = b '← こうしないと動かない
End Sub
Private Sub Button2_Click(sender As Object, e As RoutedEventArgs) Handles Button2.Click
Debug.Print(li1.P1)
End Sub
End Class 'MainWindow
Public Class lib_1
Implements INotifyPropertyChanged
Private Property _p1 As Boolean = True
Public Property P1 As Boolean 'MainWindow.CheckBox1.IsCheckedとバインド
Get
Return _p1
End Get
Set(value As Boolean)
If _p1 <> value Then
_p1 = value
Debug.Print("##" & value)
NotifyPropertyChanged(New PropertyChangedEventArgs(NameOf(P1)))
End If
End Set
End Property
Private Event PropertyChanged As PropertyChangedEventHandler
Private Event INotifyPropertyChanged_PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(e As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Nothing, e)
End Sub
End Class