私がthis articleを読んだ後、私はVirtualToggleButtonで遊ぶことにし、2つのブール型プロパティSuppressKeyboardとSuppressMouseを追加して、e.HandledをTrueに設定するかどうかを定義しました。TreeView with VirtualToggleButtonaおよびPreviewMouseDown。奇妙な振る舞い
ここで変更VTBのためのコードがあります:
public static class VirtualToggleButton
{
#region attached properties
#region IsChecked
/// <summary>
/// IsChecked Attached Dependency Property
/// </summary>
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.RegisterAttached("IsChecked", typeof(Nullable<bool>), typeof(VirtualToggleButton),
new FrameworkPropertyMetadata((Nullable<bool>)false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(OnIsCheckedChanged)));
/// <summary>
/// Gets the IsChecked property. This dependency property
/// indicates whether the toggle button is checked.
/// </summary>
public static Nullable<bool> GetIsChecked(DependencyObject d)
{
return (Nullable<bool>)d.GetValue(IsCheckedProperty);
}
/// <summary>
/// Sets the IsChecked property. This dependency property
/// indicates whether the toggle button is checked.
/// </summary>
public static void SetIsChecked(DependencyObject d, Nullable<bool> value)
{
d.SetValue(IsCheckedProperty, value);
}
/// <summary>
/// Handles changes to the IsChecked property.
/// </summary>
private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var pseudobutton = d as UIElement;
if (pseudobutton != null)
{
var newValue = (Nullable<bool>)e.NewValue;
if (newValue == true)
{
RaiseCheckedEvent(pseudobutton);
}
else if (newValue == false)
{
RaiseUncheckedEvent(pseudobutton);
}
else
{
RaiseIndeterminateEvent(pseudobutton);
}
}
}
#endregion
#region IsThreeState
/// <summary>
/// IsThreeState Attached Dependency Property
/// </summary>
public static readonly DependencyProperty IsThreeStateProperty =
DependencyProperty.RegisterAttached("IsThreeState", typeof(bool), typeof(VirtualToggleButton),
new FrameworkPropertyMetadata((bool)false));
/// <summary>
/// Gets the IsThreeState property. This dependency property
/// indicates whether the control supports two or three states.
/// IsChecked can be set to null as a third state when IsThreeState is true.
/// </summary>
public static bool GetIsThreeState(DependencyObject d)
{
return (bool)d.GetValue(IsThreeStateProperty);
}
/// <summary>
/// Sets the IsThreeState property. This dependency property
/// indicates whether the control supports two or three states.
/// IsChecked can be set to null as a third state when IsThreeState is true.
/// </summary>
public static void SetIsThreeState(DependencyObject d, bool value)
{
d.SetValue(IsThreeStateProperty, value);
}
#endregion
#region IsVirtualToggleButton
/// <summary>
/// IsVirtualToggleButton Attached Dependency Property
/// </summary>
public static readonly DependencyProperty IsVirtualToggleButtonProperty =
DependencyProperty.RegisterAttached("IsVirtualToggleButton", typeof(bool), typeof(VirtualToggleButton),
new FrameworkPropertyMetadata((bool)false,
new PropertyChangedCallback(OnIsVirtualToggleButtonChanged)));
/// <summary>
/// Gets the IsVirtualToggleButton property. This dependency property
/// indicates whether the object to which the property is attached is treated as a VirtualToggleButton.
/// If true, the object will respond to keyboard and mouse input the same way a ToggleButton would.
/// </summary>
public static bool GetIsVirtualToggleButton(DependencyObject d)
{
return (bool)d.GetValue(IsVirtualToggleButtonProperty);
}
/// <summary>
/// Sets the IsVirtualToggleButton property. This dependency property
/// indicates whether the object to which the property is attached is treated as a VirtualToggleButton.
/// If true, the object will respond to keyboard and mouse input the same way a ToggleButton would.
/// </summary>
public static void SetIsVirtualToggleButton(DependencyObject d, bool value)
{
d.SetValue(IsVirtualToggleButtonProperty, value);
}
/// <summary>
/// Handles changes to the IsVirtualToggleButton property.
/// </summary>
private static void OnIsVirtualToggleButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as IInputElement;
if (element != null)
{
if ((bool)e.NewValue)
{
element.AddHandler(Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler(OnPreviewMouseLeftButtonDown));
element.PreviewKeyDown += OnPreviewKeyDown;
}
else
{
element.RemoveHandler(Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler(OnPreviewMouseLeftButtonDown));
element.PreviewKeyDown -= OnPreviewKeyDown;
}
}
}
#endregion
#region SuppressKeyboard
public static readonly DependencyProperty SuppressKeyboardProperty =
DependencyProperty.RegisterAttached("SuppressKeyboard", typeof (bool), typeof (VirtualToggleButton),
new FrameworkPropertyMetadata(false));
public static bool GetSuppressKeyboard(DependencyObject d)
{
return (bool)d.GetValue(SuppressKeyboardProperty);
}
public static void SetSuppressKeyboard(DependencyObject d, bool value)
{
d.SetValue(SuppressKeyboardProperty, value);
}
#endregion
#region SuppressMouse
public static readonly DependencyProperty SuppressMouseProperty =
DependencyProperty.RegisterAttached("SuppressMouse", typeof(bool), typeof(VirtualToggleButton),
new FrameworkPropertyMetadata(false));
public static bool GetSuppressMouse(DependencyObject d)
{
return (bool)d.GetValue(SuppressMouseProperty);
}
public static void SetSuppressMouse(DependencyObject d, bool value)
{
d.SetValue(SuppressKeyboardProperty, value);
}
#endregion
#endregion
#region routed events
#region Checked
/// <summary>
/// A static helper method to raise the Checked event on a target element.
/// </summary>
/// <param name="target">UIElement or ContentElement on which to raise the event</param>
internal static RoutedEventArgs RaiseCheckedEvent(UIElement target)
{
if (target == null)
return null;
var args = new RoutedEventArgs {RoutedEvent = ToggleButton.CheckedEvent};
RaiseEvent(target, args);
return args;
}
#endregion
#region Unchecked
/// <summary>
/// A static helper method to raise the Unchecked event on a target element.
/// </summary>
/// <param name="target">UIElement or ContentElement on which to raise the event</param>
internal static RoutedEventArgs RaiseUncheckedEvent(UIElement target)
{
if (target == null) return null;
RoutedEventArgs args = new RoutedEventArgs();
args.RoutedEvent = ToggleButton.UncheckedEvent;
RaiseEvent(target, args);
return args;
}
#endregion
#region Indeterminate
/// <summary>
/// A static helper method to raise the Indeterminate event on a target element.
/// </summary>
/// <param name="target">UIElement or ContentElement on which to raise the event</param>
internal static RoutedEventArgs RaiseIndeterminateEvent(UIElement target)
{
if (target == null) return null;
RoutedEventArgs args = new RoutedEventArgs();
args.RoutedEvent = ToggleButton.IndeterminateEvent;
RaiseEvent(target, args);
return args;
}
#endregion
#endregion
#region private methods
private static void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left && e.LeftButton == MouseButtonState.Pressed)
{
UpdateIsChecked(sender as DependencyObject);
var dob = sender as DependencyObject;
var suppress = dob != null && GetSuppressMouse(dob);
e.Handled = suppress;
}
}
private static void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
var dob = sender as DependencyObject;
var suppress = dob != null && GetSuppressKeyboard(dob);
if (e.OriginalSource == sender)
{
if (e.Key == Key.Space)
{
// ignore alt+space which invokes the system menu
if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
return;
UpdateIsChecked(sender as DependencyObject);
e.Handled = suppress;
}
else if (e.Key == Key.Enter && (bool)(sender as DependencyObject).GetValue(KeyboardNavigation.AcceptsReturnProperty))
{
UpdateIsChecked(sender as DependencyObject);
e.Handled = suppress;
}
}
}
private static void UpdateIsChecked(DependencyObject d)
{
Nullable<bool> isChecked = GetIsChecked(d);
if (isChecked == true)
{
SetIsChecked(d, GetIsThreeState(d) ? (Nullable<bool>)null : (Nullable<bool>)false);
}
else
{
SetIsChecked(d, isChecked.HasValue);
}
}
private static void RaiseEvent(DependencyObject target, RoutedEventArgs args)
{
if (target is UIElement)
{
(target as UIElement).RaiseEvent(args);
}
else if (target is ContentElement)
{
(target as ContentElement).RaiseEvent(args);
}
}
#endregion
}
私も少しバインディングを変更:
<TreeView>
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="cl:VirtualToggleButton.IsVirtualToggleButton" Value="True"/>
<Setter Property="cl:VirtualToggleButton.SuppressKeyboard" Value="True"/>
<!--<Setter Property="cl:VirtualToggleButton.SuppressMouse" Value="True"/>-->
</Style>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Softwares}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Focusable="False"
IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}
, Path=(cl:VirtualToggleButton.IsChecked), Mode=OneWay}"/>
<TextBlock Text="{Binding Name}" Height="Auto"/>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
ここでは非常に奇妙なことが来ます。 SuppressMouseが設定されていて、TreeViewItemsがクリックされていない場合(これは一貫した動作です)、IsCheckedはTrueに設定されていますが、CheckBoxはチェックされません。 キーボードが使用されているとすべてがうまく動作するので、なぜそうなのかわかりません。 SuppressMouse = Falseの場合CheckBoxはその状態を変更しますが、クリックしたTreeViewItemに対してOnPreviewMouseLeftButtonDownが2回呼び出されます。そして、これも非常に混乱しています。
質問:説明動作
質問は何ですか? –
質問:説明された動作の原因は何ですか –
idk、デバッグを行う –