[WPF] DataTriggerが機能しない
Q&A
Closed
お世話になっております。
解決したいこと
C# + WPFでベクターグラフィックスドローイングツールを開発しています。
※下にソースコードへの案内を記載しております。よろしければそちらを参照ください。
発生している問題・エラー
現在、レイヤー機能を実装しているところなのですが、レイヤーの表示・非表示を切り替える目のアイコンのボタンを作ろうとしているところです。ここで、想定外の問題が発生しています。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:control="clr-namespace:boilersGraphics.Controls"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:helper="clr-namespace:boilersGraphics.Helpers"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase">
<Style TargetType="{x:Type control:Layers}">
<Style.Resources>
<Style x:Key="ToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="Border" Background="{StaticResource NormalBrush}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Expander">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<DockPanel>
<ToggleButton
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
DockPanel.Dock="Top"
IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ToggleButtonStyle}">
<ToggleButton.Content>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Path
Name="Arrow"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 0 8 L 5 4 Z"
Fill="{TemplateBinding Foreground}"
RenderTransformOrigin="0.5,0.5"
SnapsToDevicePixels="True"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="0.5">
<Path.RenderTransform>
<RotateTransform Angle="0" />
</Path.RenderTransform>
</Path>
<ContentPresenter
Name="HeaderContent"
Grid.Column="1"
ContentSource="Header" />
</Grid>
</ToggleButton.Content>
</ToggleButton>
<Border Name="Content">
<Border.LayoutTransform>
<ScaleTransform ScaleY="0" />
</Border.LayoutTransform>
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="Expander.IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Content"
Storyboard.TargetProperty="LayoutTransform.ScaleY"
To="1"
Duration="0:0:0.3" />
<DoubleAnimation
Storyboard.TargetName="Content"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0:0:0.3" />
<DoubleAnimation
DecelerationRatio="1"
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(FrameworkElement.RenderTransform).(RotateTransform.Angle)"
To="90"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Content"
Storyboard.TargetProperty="LayoutTransform.ScaleY"
To="0"
Duration="0:0:0.3" />
<DoubleAnimation
Storyboard.TargetName="Content"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0:0:0.3" />
<DoubleAnimation
AccelerationRatio="1"
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(FrameworkElement.RenderTransform).(RotateTransform.Angle)"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type control:Layers}">
<Border
Background="#EEE"
BorderBrush="DimGray"
BorderThickness="1"
CornerRadius="1">
<Expander Background="Transparent" IsExpanded="True">
<DockPanel>
<ItemsControl ItemsSource="{Binding Layers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding SwitchVisibilityCommand}">
<Button.ContentTemplate>
<DataTemplate>
<Image Name="Eye" Source="{StaticResource Icon_Eye}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsVisible.Value}"
Value="True">
<Setter TargetName="Eye" Property="Source" Value="{StaticResource Icon_Eye}" />
</DataTrigger>
<DataTrigger Binding="{Binding IsVisible.Value}"
Value="False">
<Setter TargetName="Eye" Property="Source" Value="{StaticResource Icon_EyeSlash}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<Expander.Header>
<Grid>
<Label Content="レイヤー" />
</Grid>
</Expander.Header>
</Expander>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
using boilersGraphics.ViewModels;
using Prism.Mvvm;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using System.Threading.Tasks;
namespace boilersGraphics.Models
{
public class Layer : BindableBase
{
private CompositeDisposable _disposable = new CompositeDisposable();
public static ObservableCollection<Layer> SelectedLayers { get; } = new ObservableCollection<Layer>();
public ReactivePropertySlim<bool> IsVisible { get; } = new ReactivePropertySlim<bool>();
public ReactivePropertySlim<Bitmap> Appearance { get; } = new ReactivePropertySlim<Bitmap>();
public ReactivePropertySlim<string> Name { get; } = new ReactivePropertySlim<string>();
public ReactiveCommand SwitchVisibilityCommand { get; } = new ReactiveCommand();
public ObservableCollection<SelectableDesignerItemViewModelBase> Items { get; } = new ObservableCollection<SelectableDesignerItemViewModelBase>();
public IObservable<PropertyPack<SelectableDesignerItemViewModelBase, bool>> Observable
{
get { return Items.ObserveElementProperty(x => x.IsSelected); }
}
public Layer()
{
SwitchVisibilityCommand.Subscribe(_ =>
{
IsVisible.Value = !IsVisible.Value;
})
.AddTo(_disposable);
}
}
}
目のアイコンのボタンを押すと、IsVisible.Valueプロパティのboolが反転し、それに伴って目のアイコンが切り替わる(目に赤の斜線が入る)はずなのですが、DataTriggerが正しく機能していないのか、アイコンが切り替わりません。Visual Studioでデバッグ中に表示される「XAMLバインドエラー」ウィンドウにも何も表示されていないので、Bindingでプロパティ名を間違っているとか、.Valueを付け忘れているとかではないようです。
また、この目のボタンにはSwitchVisibilityCommandをBindingしていますが、このCommandをSubscribe()している行にブレークポイントを設置して、ボタンを押すと、ブレークポイントにヒットします。
この問題の原因がわかる方いらっしゃいますでしょうか。教えていただけると幸いです。
ソースコード
boiler's Graphics
https://github.com/dhq-boiler/boiler-s-Graphics
gitリポジトリ
https://github.com/dhq-boiler/boiler-s-Graphics.git
ブランチ:feature/Layer
コミット:c45434c
自分で試したこと
上記のページでDataTriggerをデバッグする方法があったので、実装して動作確認してみました。
しかし、TriggerTraceStoryboardにキャスト(as)するところで、nullに変換されて、その後のif文のnullチェックで弾かれてしまうようです。
/// <summary>
/// A custom tracelistener.
/// </summary>
private class TriggerTraceListener : TraceListener
{
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
{
base.TraceEvent(eventCache, source, eventType, id, format, args);
if (format.StartsWith("Storyboard has begun;"))
{
TriggerTraceStoryboard storyboard = args[1] as TriggerTraceStoryboard; //ここで必ずnullが返る
if (storyboard != null)
{
// add a breakpoint here to see when your trigger has been
// entered or exited
// the element being acted upon
object targetElement = args[5];
// the namescope of the element being acted upon
INameScope namescope = (INameScope)args[7];
TriggerBase triggerBase = storyboard.TriggerBase;
string triggerName = GetTriggerName(storyboard.TriggerBase);
Debug.WriteLine(string.Format("Element: {0}, {1}: {2}: {3}",
targetElement,
triggerBase.GetType().Name,
triggerName,
storyboard.StoryboardType));
}
}
}
public override void Write(string message)
{
}
public override void WriteLine(string message)
{
}
}
Visual StudioのバージョンはMicrosoft Visual Studio Community 2019 Version 16.10.4です。
何か私の見落とし、致命的な勘違いなど気づいたところがあれば、回答していただけると助かります。よろしくお願いいたします。