我对自定义附加属性/事件有疑问。在我的场景中,我想将属性/事件附加到任何控件。此属性/事件的值应该是事件处理程序。简而言之,它应该看起来像:
<TextBox local:Dragging.OnDrag="OnDrag" />
首先,我尝试将OnDrag实现为附加属性。这适用于上述情况,但以下情况失败:
<Style TargetType="TextBox">
<Setter Property="local:Dragging.OnDrag" Value="OnDrag" />
</Style>
因为XAML系统显然不能将“OnDrag”字符串转换为RoutedEventHandler(附加属性的类型)。
接下来我尝试使用附加事件,就像内置的Mouse.MouseEnter一样。
完整的代码显示在底部。这个版本发生了奇怪的事情:
如果你运行如图所示的代码(注册了RegisterRoutedEvent行),它将显示调用“添加处理程序”函数。然后xaml系统在应用样式时有一个内部异常(由于我猜想缺少注册事件)。
如果运行带有RegisterRoutedEvent行的代码,则一切都会运行,但永远不会调用“添加处理程序”功能。我希望它被调用,以便我可以在拖放管理器上注册。
奇怪的是,如果我将EventSetter中的事件从我自己更改为Mouse.MouseEnter,则由xaml设计器(在MainWindow.g [.i] .cs中)自动生成的代码是不同的。
我不确定为什么2)不会调用AddXYZHandler。 MSDN似乎表明这应该有效。
最后我的问题:
我怎样才能做到这一点?有可能吗?
我最好在我的场景中使用附加事件或附加属性吗?
如果是属性:如何修复样式设置器,以便将OnDrag字符串转换为正确的RoutedEventHandler?
如果发生事件:这里出了什么问题?有任何解决这个问题的方法吗?我希望调用AddXYZHandler,但显然这不适用于样式。
MainWindow.xaml:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="GridTest.MainWindow"
xmlns:local="clr-namespace:GridTest"
Title="MainWindow" Height="350" Width="525"
local:XYZTest.XYZ="OnXYZAttached">
<Window.Style>
<Style TargetType="Window">
<EventSetter Event="local:XYZTest.XYZ" Handler="OnXYZStyle" />
</Style>
</Window.Style>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace GridTest
{
public class XYZTest
{
//public static readonly RoutedEvent XYZEvent = EventManager.RegisterRoutedEvent("XYZ", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(XYZTest));
public static void AddXYZHandler(DependencyObject element, RoutedEventHandler handler)
{
MessageBox.Show("add handler");
}
public static void RemoveXYZHandler(DependencyObject element, RoutedEventHandler handler)
{
MessageBox.Show("remove handler");
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void OnXYZAttached(object sender, RoutedEventArgs e)
{
MessageBox.Show("attached");
}
public void OnXYZStyle(object sender, RoutedEventArgs e)
{
MessageBox.Show("style");
}
}
}
}
新代码:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="GridTest.MainWindow"
x:Name="root"
xmlns:local="clr-namespace:GridTest"
local:XYZTest.ABC="OnXYZTopLevel"
Title="MainWindow" Height="350" Width="525">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Background" Value="Red" />
<Setter Property="local:XYZTest.ABC" Value="OnXYZStyle" />
<!-- <Setter Property="local:XYZTest.ABC" Value="{Binding OnXYZStyleProperty, ElementName=root}" /> -->
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Window>
using System.Windows;
namespace GridTest
{
public class XYZTest
{
public static readonly DependencyProperty ABCProperty = DependencyProperty.RegisterAttached("ABC", typeof(RoutedEventHandler), typeof(XYZTest), new UIPropertyMetadata(null, OnABCChanged));
public static void SetABC(UIElement element, RoutedEventHandler value)
{
System.Diagnostics.Debug.WriteLine("ABC set to " + value.Method.Name);
}
static void OnABCChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ABC changed to " + ((RoutedEventHandler)e.NewValue).Method.Name);
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new[] { "A", "B", "C" };
}
public void OnXYZTopLevel(object sender, RoutedEventArgs e)
{
MessageBox.Show("handler top level");
}
public void OnXYZStyle(object sender, RoutedEventArgs e)
{
MessageBox.Show("handler style");
}
public RoutedEventHandler OnXYZStyleProperty
{
get { return OnXYZStyle; }
}
}
}
答案 0 :(得分:2)
我使用Attached Properties
完全成功实现了拖放功能。如果我是你,我会避免使用自定义事件,因为你坚持他们的参数。就个人而言,我改为ICommand
,但您也可以使用delegate
s。
请查看我在拖放基类实现中使用的属性列表和Command
:
/// <summary>
/// Gets or sets the type of the drag and drop object required by the Control that the property is set on.
/// </summary>
public Type DragDropType { get; set; }
/// <summary>
/// Gets or sets the allowable types of objects that can be used in drag and drop operations.
/// </summary>
public List<Type> DragDropTypes { get; set; }
/// <summary>
/// Gets or sets the ICommand instance that will be executed when the user attempts to drop a dragged item onto a valid drop target Control.
/// </summary>
public ICommand DropCommand { get; set; }
/// <summary>
/// Gets or sets the DragDropEffects object that specifies the type of the drag and drop operations allowable on the Control that the property is set on.
/// </summary>
public DragDropEffects DragDropEffects { get; set; }
/// <summary>
/// The Point struct that represents the position on screen that the user initiated the drag and drop procedure.
/// </summary>
protected Point DragStartPosition
{
get { return dragStartPosition; }
set { if (dragStartPosition != value) { dragStartPosition = value; } }
}
/// <summary>
/// The UIElement object that represents the UI element that has the attached Adorner control... usually the top level view.
/// </summary>
protected UIElement AdornedUIElement
{
get { return adornedUIElement; }
set { if (adornedUIElement != value) { adornedUIElement = value; } }
}
AdornedUIElement
属性包含Adorner
,可在拖动项目时显示这些项目,但您可以选择实施。在这个基类中,我实现了大多数拖放功能和派生类必须实现的公开protected abstract
方法。例如,此方法调用OnAdornedUIElementPreviewDragOver
方法为派生类提供更改基类行为的机会:
private void AdornedUIElementPreviewDragOver(object sender, DragEventArgs e)
{
PositionAdorner(e.GetPosition(adornedUIElement));
OnAdornedUIElementPreviewDragOver(sender, e); // Call derived classes here <<<
if (e.Handled) return; // to bypass base class behaviour
HitTestResult hitTestResult = VisualTreeHelper.HitTest(adornedUIElement, e.GetPosition(adornedUIElement));
Control controlUnderMouse = hitTestResult.VisualHit.GetParentOfType<Control>();
UpdateDragDropEffects(controlUnderMouse, e);
e.Handled = true;
}
/// <summary>
/// Must be overidden in derived classes to call both the UpdateDropProperties and UpdateDragDropEffects methods to provide feedback for the current drag and drop operation.
/// </summary>
/// <param name="sender">The Control that the user dragged the mouse pointer over.</param>
/// <param name="e">The DragEventArgs object that contains arguments relevant to all drag and drop events.</param>
protected abstract void OnAdornedUIElementPreviewDragOver(object sender, DragEventArgs e);
然后在我的扩展ListBoxDragDropManager
课程中:
protected override void OnAdornedUIElementPreviewDragOver(object sender, DragEventArgs e)
{
HitTestResult hitTestResult = VisualTreeHelper.HitTest(AdornedUIElement, e.GetPosition(AdornedUIElement));
ListBox listBoxUnderMouse = hitTestResult.VisualHit.GetParentOfType<ListBox>();
if (listBoxUnderMouse != null && listBoxUnderMouse.AllowDrop)
{
UpdateDropProperties(ListBoxProperties.GetDragDropType(listBoxUnderMouse), ListBoxProperties.GetDropCommand(listBoxUnderMouse));
}
UpdateDragDropEffects(listBoxUnderMouse, e);
e.Handled = true; // This bypasses base class behaviour
}
最后,它只是在UI中使用(RelativeSource
声明和窄宽度使它看起来比它更糟糕):
<ListBox ItemsSource="{Binding Disc.Tracks, IsAsync=True}" SelectedItem="{Binding
Disc.Tracks.CurrentItem}" AllowDrop="True" Attached:ListBoxProperties.
IsDragTarget="True" Attached:ListBoxProperties.DropCommand="{Binding
DataContext.DropTracks, RelativeSource={RelativeSource AncestorType={x:Type
Views:ReleaseTracksView}}}" Attached:ListBoxProperties.DragDropTypes="{Binding
DataContext.DragDropTypes, RelativeSource={RelativeSource AncestorType={x:Type
Views:ReleaseTracksView}}}" Attached:ListBoxProperties.DragEffects="{Binding
DataContext.DragEffects, RelativeSource={RelativeSource AncestorType={x:Type
Views:ReleaseTracksView}}}">
我必须说实话......这是很多的工作。但是,现在我可以通过设置一些属性来实现具有视觉反馈的拖放操作,这似乎是值得的。