WPFのXAMLを書いていた時にハマったのでメモ。
(2020.8.12追記)当方.NET Framework 4.8を使用しています。
Visibilityをバインドしたかった
自作のUserControlのVisibilityをバインドするコードを書きました。
MyUserControlVisibilityを通じて、MyUserControlのVisibilityを変更出来るようにしようとしています。
<Window x:Name="Test" x:Class="MyNamespace.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:MyNamespace"
mc:Ignorable="d"
Title="テスト">
<Window.DataContext>
<local:MainWindowUserControl/>
</Window.DataContext>
<local:MyUserControl x:Name="MyControl1" Visibility="{Binding MyUserControlVisibility, Mode=TwoWay}"/>
</Window>
// usingは省略。Prismを使用しています
namespace MyNamespace
{
public class MainWindowViewModel : BindableBase
{
private Visibility _MyUserControlVisibility = Visibility.Hidden;
public Visibility MyUserControlVisibility
{
get { return _MyUserControlVisibility; }
set { SetProperty(ref _MyUserControlVisibility, value); }
}
}
public class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
しかし、これでは動作せず、無視されまてしまいました。
DataTriggerに書き換えてみる
DataTriggerで発火させれば動くのではと思い、書き換えてみます。
<Window x:Name="Test" x:Class="MyNamespace.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:MyNamespace"
mc:Ignorable="d"
Title="テスト">
<Window.DataContext>
<local:MainWindowUserControl/>
</Window.DataContext>
<local:MyUserControl x:Name="MyControl1">
<local:MyUserControl.Style>
<Style TargetType="local:MyUserControl"> <!--TargetTypeをUserControlにしても変わらない-->
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyUserControlVisibility}" Value="Hidden">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</local:MyUserControl.Style>
</local:MyUserControl>
</Window>
が、あえなく撃沈。またもや無視されてしまいます。
(一応解決)Gridに入れる
目的のUserControlをGridに入れて、GridのVisibilityをバインドしてみます。
<Window x:Name="Test" x:Class="MyNamespace.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:MyNamespace"
mc:Ignorable="d"
Title="テスト">
<Window.DataContext>
<local:MainWindowUserControl/>
</Window.DataContext>
<Grid Visibility="{Binding MyUserControlVisibility, Mode=TwoWay}">
<local:MyUserControl x:Name="MyControl1"/>
</Grid>
</Window>
これだと希望通りの動作をしてくれました。が、Gridで挟むって……メモリの無駄遣い感があって非常に嫌です。
2020.8.12追記……(解決)UserControlを定義する側でバインドする
その後、MyUserControlを定義するXAMLの方でVisibilityをバインドしてみました。
<UserControl x:Class="MyNamespace.MyUserControl"
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"
xmlns:local="clr-namespace:MyNamespace"
mc:Ignorable="d"
Visibility="{Binding Visibility}"/>
<UserControl.DataContext>
<local:MyUserControlViewModel/>
</UserControl.DataContext>
</UserControl>
// usingは省略
namespace MyNamespace
{
public class MyUserControlViewModel : BindableBase
{
private Visibility _Visibility = Visibility.Hidden;
public Visibility Visibility
{
get { return _Visibility; }
set { SetProperty(ref _Visibility, value); }
}
}
public class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
}
}
// usingは省略
namespace MyNamespace
{
public class MainWindowViewModel
{
private MyUserControlViewModel _MyControl1VM;
public MyUserControlViewModel MyControl1VM
{
get { return _MyControl1VM; }
set { SetProperty(ref _MyControl1VM, value); }
}
public class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainWindowViewModel VM = DataContext;
VM.MyControl1VM = MyControl1.DataContext;
}
}
}
これだとちゃんと動いてくれました。
MyUserControlのViewModelの方にVisibilityプロパティを定義出来るのは、オブジェクト指向的にも美味しいですね。多分これが一番綺麗だと思います。ウソですごめんなさい(泣)
DataContextまわりをXAMLで設定するように変更し、少しだけスリムにしました。(2020.8.13追記)
終わりに
自作UserControlに限ってバインドがうまくできないのですが、バグなのでしょうか。それとも仕様?
**WPFのXAMLはBinding関連でエラーが起こっても基本的に例外を吐かず、デバッグ実行時に出力ログを残すのみのほんわか仕様となっています。**立派なエラーなのですから、もう少し主張して欲しい所なのですが。MSさん、いかがでしょう?
(2020.8.12修正)