【问题标题】:Dragging Adorner does not work when TextBox text is selected选择 TextBox 文本时,拖动装饰器不起作用
【发布时间】:2016-09-12 17:50:55
【问题描述】:

我有一个自定义装饰器,当它们被选中时会添加到控件中。问题是,我还注册了一个自定义全局事件,以在获得焦点时自动选择文本框中的所有文本。

我遇到的问题是,当我尝试在带有选定文本的文本框上使用它时,我的装饰器不起作用。由于某种原因,它会尝试拖动选定的文本。奇怪的是我可以拖动选定的文本,然后在完成拖放后我可以使用我的装饰器。我不确定为什么或如何解决它。

我尝试取消单个文本框上的拖动命令,但这也阻止了装饰器工作。

有没有办法让装饰器比所选文本具有更高的优先级?

这是我的定制装饰品

public class DraggingAdorner : Adorner
{

    Thumb draggingIcon = null;
    Thumb resizeThumb = null;
    Border border = null;

    VisualCollection visualChildren;

    public DraggingAdorner(UIElement adornedElement)
        : base(adornedElement)
    {
        visualChildren = new VisualCollection(this);

        BuildBorder();
        BuildDraggingIcon();
        BuildResizer();

        draggingIcon.DragDelta += DraggingIcon_DragDelta;
        resizeThumb.DragDelta += Resize;
        resizeThumb.MouseDoubleClick += ResizeThumb_MouseDoubleClick;
    }

    private void ResizeThumb_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
        Thumb hitThumb = sender as Thumb;

        if (adornedElement == null || hitThumb == null) return;
        FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

        var tb = adornedElement as TextBox;
        if (tb == null) return;

        tb.Height = Double.NaN;
        tb.Width = Double.NaN;

        e.Handled = true;
    }

    private void BuildBorder()
    {
        if (border != null) return;

        border = new Border();
        border.BorderBrush = new SolidColorBrush(Colors.Black);
        border.BorderThickness = new Thickness(1);
        border.Opacity = .5;

        visualChildren.Add(border);
    }

    private void BuildDraggingIcon()
    {
        if (draggingIcon != null) return;

        draggingIcon = new Thumb();

        draggingIcon.Cursor = Cursors.Hand;
        draggingIcon.Height = draggingIcon.Width = 12;
        draggingIcon.Opacity = 0.40;
        draggingIcon.Background = new SolidColorBrush(Colors.Black);

        visualChildren.Add(draggingIcon);
    }

    private void BuildResizer()
    {
        if (resizeThumb != null) return;

        resizeThumb = new Thumb();

        // Set some arbitrary visual characteristics.
        resizeThumb.Cursor = Cursors.ScrollAll;
        resizeThumb.Height = resizeThumb.Width = 10;
        resizeThumb.Opacity = 0.40;
        resizeThumb.Background = new SolidColorBrush(Colors.Red);

        visualChildren.Add(resizeThumb);
    }

    private void DraggingIcon_DragDelta(object sender, DragDeltaEventArgs e)
    {
        FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
        Thumb hitThumb = sender as Thumb;

        if (adornedElement == null || hitThumb == null) return;
        FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

        var left = e.HorizontalChange + Canvas.GetLeft(adornedElement);
        if (left <= 0)
        {
            left = 1;
        }
        else if (left + adornedElement.ActualWidth >= parentElement.ActualWidth)
        {
            left = parentElement.ActualWidth - adornedElement.ActualWidth - 1;
        }

        var top = e.VerticalChange + Canvas.GetTop(adornedElement);
        if (top <= 0)
        {
            top = 1;
        }
        else if (top + adornedElement.ActualHeight >= parentElement.ActualHeight)
        {
            top = parentElement.ActualHeight - adornedElement.ActualHeight - 1;
        }

        Canvas.SetLeft(adornedElement, left);
        Canvas.SetTop(adornedElement, top);
    }

    // Handler for resizing from the bottom-right.
    private void Resize(object sender, DragDeltaEventArgs e)
    {
        FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
        Thumb hitThumb = sender as Thumb;

        if (adornedElement == null || hitThumb == null) return;
        FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

        // Ensure that the Width and Height are properly initialized after the resize.
        EnforceSize(adornedElement);

        var left = Canvas.GetLeft(adornedElement);
        var top = Canvas.GetTop(adornedElement);

        var newWidth = Math.Max(adornedElement.Width + e.HorizontalChange, hitThumb.DesiredSize.Width);
        var newHeight = Math.Max(e.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);

        if (left + newWidth >= parentElement.ActualWidth)
        {
            newWidth = parentElement.ActualWidth - left;
        }
        if (top + newHeight >= parentElement.ActualHeight)
        {
            newHeight = parentElement.ActualHeight - top;
        }

        adornedElement.Width = newWidth;
        adornedElement.Height = newHeight;
    }

    private void EnforceSize(FrameworkElement adornedElement)
    {
        if (adornedElement.Width.Equals(Double.NaN))
            adornedElement.Width = adornedElement.DesiredSize.Width;
        if (adornedElement.Height.Equals(Double.NaN))
            adornedElement.Height = adornedElement.DesiredSize.Height;

        FrameworkElement parent = adornedElement.Parent as FrameworkElement;
        if (parent != null)
        {
            adornedElement.MaxHeight = parent.ActualHeight;
            adornedElement.MaxWidth = parent.ActualWidth;
        }
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        double desiredWidth = AdornedElement.DesiredSize.Width;
        double desiredHeight = AdornedElement.DesiredSize.Height;

        double adornerWidth = this.DesiredSize.Width;
        double adornerHeight = this.DesiredSize.Height;

        draggingIcon.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
        resizeThumb.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
        border.Arrange(new Rect(0, 0, adornerWidth, adornerHeight));

        return finalSize;
    }

    protected override int VisualChildrenCount { get { return visualChildren.Count; } }
    protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
}

这是我应用装饰器的代码

private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    e.Handled = AdornSelectedElement(sender as UIElement);
}

private bool AdornSelectedElement(UIElement element)
{
    if (element == null) return false;

    RemoveSelectedElement();
    if (Canvas.GetLeft(element) < 0)
        Canvas.SetLeft(element, 1);

    var aLayer = AdornerLayer.GetAdornerLayer(element);
    if ((aLayer.GetAdorners(element)?.Length ?? 0) > 0) return false;
    aLayer.Add(new DraggingAdorner(element));
    selectedElement = element;
    return true;
}

private void RemoveSelectedElement()
{
   if (selectedElement == null) return;

   var aLayer = AdornerLayer.GetAdornerLayer(selectedElement);

   if ((aLayer.GetAdorners(selectedElement)?.Length ?? 0) < 1) return;

   aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
   selectedElement = null;
}

这是我的全局事件处理程序

protected override void OnStartup(StartupEventArgs e)
{
   EventManager.RegisterClassHandler(typeof(TextBox), TextBox.GotKeyboardFocusEvent, new RoutedEventHandler(OnTextBoxFocus));
   EventManager.RegisterClassHandler(typeof(TextBox), TextBox.GotFocusEvent, new RoutedEventHandler(OnTextBoxFocus));

   base.OnStartup(e);
}

private void OnTextBoxFocus(object sender, RoutedEventArgs e)
{
    var tb = sender as System.Windows.Controls.TextBox;
    if (tb == null) return;

    if (!String.IsNullOrWhiteSpace(tb.Text))
        tb.SelectAll();
}

最后,这里是取消对所选文本的内部拖动不起作用的代码

DataObject.AddCopyingHandler(tb, (s, e) =>
{
    if (e.IsDragDrop)
    {
        e.CancelCommand();
    }
});

我很乐意添加更多可能需要的代码。

编辑:我不认为这仅仅是因为在 TextBox 中选择了文本。我认为我的装饰器和文本框有些奇怪,但我不确定是什么。我有一个可以正常工作的自定义图像控件。

第二次编辑:在选择文本框后,看起来我实际上什么也做不了(IE 关闭主窗口,单击其他控件等)。由于某种原因,发生了一些奇怪的事情,选择必须处理不当,并且不会退出这种奇怪的拖动文本模式。我需要拖放文本,然后我可以再次做任何我想做的事情。

【问题讨论】:

    标签: c# wpf xaml textbox drag-and-drop


    【解决方案1】:

    我有点想通了。原来是这个方法

    private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        e.Handled = AdornSelectedElement(sender as UIElement);
    }
    

    导致文本框出现问题。我有一个自定义图像控件,需要将 e.Handled 设置为 true 才能使该选择正确触发,但是在 TextBoxes 上,这会导致奇怪的选定文本拖动问题。我改了

    return true;
    

    在我的结尾

    AdornSelectedElement    
    

    阅读例程

    return element is ImageBox;
    

    然后解决了它。如果有人知道这是为什么,我可以提供自定义控件的 Xaml,我们可以讨论为什么会发生这种情况。

    谢谢!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-28
      • 1970-01-01
      相关资源
      最近更新 更多