繁体   English   中英

我如何识别对一行的第二次点击

[英]How can I recognize a second click on a line

我有一个带有一堆线条的 canvas,我设置了它,这样如果我点击一条线,颜色就会改变。 然后我希望能够再次单击此行并将颜色重置为黑色,但我遇到了一些麻烦。

我试过的:

     private void Line_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Color selectionColor = (Color)ColorConverter.ConvertFromString("#FF490AF6");
            SolidColorBrush selectionBrush = new SolidColorBrush(selectionColor);
            SolidColorBrush blackBrush = new SolidColorBrush();
            blackBrush.Color = Colors.Black;
            Line line = (Line)sender;

            if (line.Stroke == selectionBrush)
            {
                line.Stroke = blackBrush;
            }
            else
            {
                line.Stroke = selectionBrush;
            }    
        }

当你比较画笔时

 line.Stroke == selectionBrush

这将始终是与您期望的不同的画笔,因为您刚刚实例化了一个新画笔。 用线

 SolidColorBrush selectionBrush = new SolidColorBrush(selectionColor);

你应该把那个刷子声明在拥有的class中,声明的时候设置它。

 private SolidColorBrush selectionBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF490AF6"));

 

然后将其用于设置和比较。

 private void Line_MouseDown(object sender, MouseButtonEventArgs e)
    {
        Line line = (Line)sender;

        if (line.Stroke == selectionBrush)
        {
            line.Stroke = Brushes.Black;
        }
        else
        {
            line.Stroke = selectionBrush;
        }    
    }

你也可以只使用 Brushes.Black。

使用列表框进行选择处理的替代方法

<ListBox ItemsSource="{Binding Lines}" b:SelectionBehavior.SelectedItems="{Binding SelectedLines}" Background="Beige" SelectionMode="Multiple">
    <ListBox.Resources>
        <local:SubstractingConverter x:Key="SubstractingConverter" LowerBound="0"/>
        <local:MinConverter x:Key="MinConverter"/>
    </ListBox.Resources>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Canvas.Left">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource MinConverter}">
                        <Binding Path="X1"/>
                        <Binding Path="X2"/>
                    </MultiBinding>
                </Setter.Value>
            </Setter>
            <Setter Property="Canvas.Top">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource MinConverter}">
                        <Binding Path="Y1"/>
                        <Binding Path="Y2"/>
                    </MultiBinding>
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid>
                            <Line x:Name="LineElement" StrokeThickness="2" Stroke="Black">
                                <Line.X1>
                                    <MultiBinding Converter="{StaticResource SubstractingConverter}">
                                        <Binding Path="X1"/>
                                        <Binding Path="X2"/>
                                    </MultiBinding>
                                </Line.X1>
                                <Line.Y1>
                                    <MultiBinding Converter="{StaticResource SubstractingConverter}">
                                        <Binding Path="Y1"/>
                                        <Binding Path="Y2"/>
                                    </MultiBinding>
                                </Line.Y1>
                                <Line.X2>
                                    <MultiBinding Converter="{StaticResource SubstractingConverter}">
                                        <Binding Path="X2"/>
                                        <Binding Path="X1"/>
                                    </MultiBinding>
                                </Line.X2>
                                <Line.Y2>
                                    <MultiBinding Converter="{StaticResource SubstractingConverter}">
                                        <Binding Path="Y2"/>
                                        <Binding Path="Y1"/>
                                    </MultiBinding>
                                </Line.Y2>
                            </Line>
                            <Line x:Name="HighlightElement" StrokeThickness="4" Stroke="Transparent"
                                  X1="{Binding X1, ElementName=LineElement}" Y1="{Binding Y1, ElementName=LineElement}"
                                  X2="{Binding X2, ElementName=LineElement}" Y2="{Binding Y2, ElementName=LineElement}">
                            </Line>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="LineElement" Property="Stroke" Value="HotPink"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="HighlightElement" Property="Stroke" Value="LightBlue"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

和视图模型代码

    public ObservableCollection<LineViewModel> Lines { get; } = new ObservableCollection<LineViewModel>();
    
    public ObservableCollection<LineViewModel> SelectedLines { get; } = new ObservableCollection<LineViewModel>();

专线 class

public class LineViewModel : ObservableObject
{
    private double _x1;
    private double _y1;
    private double _x2;
    private double _y2;

    public double X1 { get => _x1; set => SetValue(ref _x1, value); }
    public double Y1 { get => _y1; set => SetValue(ref _y1, value); }
    public double X2 { get => _x2; set => SetValue(ref _x2, value); }
    public double Y2 { get => _y2; set => SetValue(ref _y2, value); }
}

转换器

public class SubstractingConverter : IMultiValueConverter
{
    public double LowerBound { get; set; } = double.MinValue;

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Contains(DependencyProperty.UnsetValue))
            return Binding.DoNothing;
        return Math.Max(LowerBound, (double)values[0] - (double)values[1]);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

public class MinConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Contains(DependencyProperty.UnsetValue))
            return Binding.DoNothing;
        return Math.Min((double)values[0], (double)values[1]);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

行为

public class SelectionBehavior
{
    private static readonly DependencyProperty BehaviorProperty = DependencyProperty.Register("BehaviorItems", typeof(SelectionBehavior), typeof(SelectionBehavior));

    public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached("SelectedItems", typeof(INotifyCollectionChanged), typeof(SelectionBehavior), new PropertyMetadata(SelectedItems_Changed));
    public static INotifyCollectionChanged GetSelectedItems(DependencyObject obj)
    {
        return (INotifyCollectionChanged)obj.GetValue(SelectedItemsProperty);
    }

    public static void SetSelectedItems(DependencyObject obj, INotifyCollectionChanged value)
    {
        obj.SetValue(SelectedItemsProperty, value);
    }

    private static void SelectedItems_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        INotifyCollectionChanged selectedItems;
        if (d is ListBox listBox)
            selectedItems = (INotifyCollectionChanged)listBox.SelectedItems;
        else if (d is MultiSelector ms)
            selectedItems = (INotifyCollectionChanged)ms.SelectedItems;
        else
            selectedItems = (INotifyCollectionChanged)d.GetType().GetProperty("SelectedItems").GetValue(d);

        var behavior = (SelectionBehavior)d.GetValue(BehaviorProperty);
        if (behavior != null)
            behavior.Detach(d);

        if (e.NewValue != null)
        {
            behavior = new SelectionBehavior(selectedItems, (INotifyCollectionChanged)e.NewValue);
            behavior._sourceItems.CollectionChanged += behavior.OnCollectionChanged;
            behavior._targetItems.CollectionChanged += behavior.OnCollectionChanged;
            d.SetValue(BehaviorProperty, behavior);
            if (d is FrameworkElement fe)
                fe.Unloaded += behavior.Unloaded;
        }
    }

    private readonly INotifyCollectionChanged _sourceItems;
    private readonly INotifyCollectionChanged _targetItems;
    private bool _isSyncing;

    public SelectionBehavior(INotifyCollectionChanged sourceItems, INotifyCollectionChanged targetItems)
    {
        _sourceItems = sourceItems;
        _targetItems = targetItems;
    }

    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (_isSyncing)
            return;
        try
        {
            _isSyncing = true;
            if (e.Action == NotifyCollectionChangedAction.Move)
                throw new NotImplementedException();    // not sure if it has old and new items like with replace
            var syncTo = sender == _sourceItems ? (IList)_targetItems : (IList)_sourceItems;
            if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace)
            {
                for (int i = 0; i < e.OldItems.Count; i++)
                    syncTo.RemoveAt(e.OldStartingIndex + i);
            }
            if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Replace)
            {
                for (int i = 0; i < e.NewItems.Count; i++)
                    syncTo.Insert(e.NewStartingIndex + i, e.NewItems[i]);
            }
            if (e.Action == NotifyCollectionChangedAction.Reset)
                syncTo.Clear();
        }
        finally
        {
            _isSyncing = false;
        }
    }

    private void Unloaded(object sender, RoutedEventArgs e)
    {
        ((FrameworkElement)sender).Unloaded -= Unloaded;
        Detach((DependencyObject)sender);
    }

    public void Detach(DependencyObject d)
    {
        _sourceItems.CollectionChanged -= OnCollectionChanged;
        _targetItems.CollectionChanged -= OnCollectionChanged;
        d.ClearValue(BehaviorProperty);
    }
}

一个非常简单的解决方案涉及一个自定义附加属性IsChecked来保存元素的 state 和一个Style来定义一个Trigger来切换Shape.Stroke值。

以下示例使用以所有Line元素为目标的隐式Style
如果您需要每条Line都有不同的颜色,则必须创建单独的 styles。

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public static bool GetIsChecked(DependencyObject attachingElement) 
    => (bool)attachingElement.GetValue(IsCheckedProperty);

  public static void SetIsChecked(DependencyObject attachingElement, bool value)
    => attachingElement.SetValue(IsCheckedProperty, value);

  public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached(
    "IsChecked", 
    typeof(bool), 
    typeof(MainWindow), 
    new PropertyMetadata(default));

  private void OnShapePreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  {
    var shape = sender as DependencyObject;

    // Toggle the attached IsChecked value
    SetIsChecked(shape, GetIsChecked(shape) ^ true);
  }
}

主窗口.xaml
以下Style将应用于作为MainWindow可视化树子级的每一Line

<Window>
  <Window.Resources>

    <Style TargetType="Line">
      <Setter Property="Stroke"
              Value="Black" />
      <Setter Property="StrokeThickness"
              Value="4" />
      <EventSetter Event="PreviewMouseLeftButtonUp"
                   Handler="OnShapePreviewMouseLeftButtonUp" />

      <Style.Triggers>
        <Trigger Property="local:MainWindow.IsChecked" 
                 Value="True">
          <Setter Property="Stroke"
                  Value="#FF490AF6" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>

  <Canvas>
    <Line Canvas.Left="10"
          Canvas.Top="10"
          X1="0"
          X2="50"
          Y1="10"
          Y2="10" />
    <Line Canvas.Left="20"
          Canvas.Top="20"
          X1="0"
          X2="50"
          Y1="10"
          Y2="10" />
  </Canvas>
</Window>

或者将上面的代码移动到一个简单的附加行为中:

ShapeToggleBehavior.cs

public class ShapeToggleBehavior : DependencyObject
{
  public static bool GetIsChecked(DependencyObject attachingElement)
    => (bool)attachingElement.GetValue(IsCheckedProperty);

  public static void SetIsChecked(DependencyObject attachingElement, bool value)
    => attachingElement.SetValue(IsCheckedProperty, value);

  public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached(
    "IsChecked",
    typeof(bool),
    typeof(ShapeToggleBehavior),
    new PropertyMetadata(default(bool), OnIsCheckedChanged));

  public static Brush GetCheckedStroke(DependencyObject attachingElement)
    => (Brush)attachingElement.GetValue(CheckedStrokeProperty);

  public static void SetCheckedStroke(DependencyObject attachingElement, Brush value)
    => attachingElement.SetValue(CheckedStrokeProperty, value);

  public static readonly DependencyProperty CheckedStrokeProperty = DependencyProperty.RegisterAttached(
    "CheckedStroke",
    typeof(Brush),
    typeof(ShapeToggleBehavior),
    new PropertyMetadata(Brushes.LightBlue));

  public static Brush GetUncheckedStroke(DependencyObject attachingElement)
    => (Brush)attachingElement.GetValue(UncheckedStrokeProperty);

  public static void SetUncheckedStroke(DependencyObject attachingElement, Brush value)
    => attachingElement.SetValue(UncheckedStrokeProperty, value);

  public static readonly DependencyProperty UncheckedStrokeProperty = DependencyProperty.RegisterAttached(
    "UncheckedStroke",
    typeof(Brush),
    typeof(ShapeToggleBehavior),
    new PropertyMetadata(Brushes.Black));

  public static bool GetIsEnabled(DependencyObject attachingElement)
    => (bool)attachingElement.GetValue(IsEnabledProperty);

  public static void SetIsEnabled(DependencyObject attachingElement, bool value)
    => attachingElement.SetValue(IsEnabledProperty, value);

  public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
    "IsEnabled",
    typeof(bool),
    typeof(ShapeToggleBehavior),
    new PropertyMetadata(default(bool), OnIsEnabledChanged));

  private static void OnIsEnabledChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (attachingElement is not Shape shape)
    {
      return;
    }

    if ((bool)e.NewValue)
    {
      WeakEventManager<Shape, MouseButtonEventArgs>.AddHandler(shape, nameof(shape.PreviewMouseLeftButtonUp), OnShapePreviewMouseLeftButtonUp);
      shape.Stroke ??= GetUncheckedStroke(shape);
    }
    else
    {
      WeakEventManager<Shape, MouseButtonEventArgs>.RemoveHandler(shape, nameof(shape.PreviewMouseLeftButtonUp), OnShapePreviewMouseLeftButtonUp);
    }
  }

  private static void OnIsCheckedChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    var shape = attachingElement as Shape;
    bool isChecked = GetIsChecked(shape);
    shape.Stroke = isChecked
      ? GetCheckedStroke(shape)
      : GetUncheckedStroke(shape);
  }

  private static void OnShapePreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  {
    var shape = sender as Shape;

    // Toggle IsChecked
    bool isChecked = GetIsChecked(shape) ^ true;
    SetIsChecked(shape, isChecked);
  }
}

主窗口.xaml

<Window>
  <Canvas>
    <Line local:ShapeToggleBehavior.IsEnabled="True"
          StrokeThickness="4"
          Canvas.Left="10"
          Canvas.Top="10"
          X1="0"
          X2="50"
          Y1="10"
          Y2="10" />
    <Line local:ShapeToggleBehavior.IsEnabled="True"
          local:ShapeToggleBehavior.CheckedStroke="Red"
          local:ShapeToggleBehavior.UncheckedStroke="Green"
          StrokeThickness="4"
          Canvas.Left="20"
          Canvas.Top="20"
          X1="0"
          X2="50"
          Y1="10"
          Y2="10" />
  </Canvas>
</Window>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM