附属物/活动

时间:2013-09-10 08:50:07

标签: c# wpf

我对自定义附加属性/事件有疑问。在我的场景中,我想将属性/事件附加到任何控件。此属性/事件的值应该是事件处理程序。简而言之,它应该看起来像:

<TextBox local:Dragging.OnDrag="OnDrag" />

首先,我尝试将OnDrag实现为附加属性。这适用于上述情况,但以下情况失败:

<Style TargetType="TextBox">
    <Setter Property="local:Dragging.OnDrag" Value="OnDrag" />
</Style>

因为XAML系统显然不能将“OnDrag”字符串转换为RoutedEventHandler(附加属性的类型)。

接下来我尝试使用附加事件,就像内置的Mouse.MouseEnter一样。

完整的代码显示在底部。这个版本发生了奇怪的事情:

  1. 如果你运行如图所示的代码(注册了RegisterRoutedEvent行),它将显示调用“添加处理程序”函数。然后xaml系统在应用样式时有一个内部异常(由于我猜想缺少注册事件)。

  2. 如果运行带有RegisterRoutedEvent行的代码,则一切都会运行,但永远不会调用“添加处理程序”功能。我希望它被调用,以便我可以在拖放管理器上注册。

  3. 奇怪的是,如果我将EventSetter中的事件从我自己更改为Mouse.MouseEnter,则由xaml设计器(在MainWindow.g [.i] .cs中)自动生成的代码是不同的。

  4. 我不确定为什么2)不会调用AddXYZHandler。 MSDN似乎表明这应该有效。

    最后我的问题:

    1. 我怎样才能做到这一点?有可能吗?

    2. 我最好在我的场景中使用附加事件或附加属性吗?

    3. 如果是属性:如何修复样式设置器,以便将OnDrag字符串转换为正确的RoutedEventHandler?

    4. 如果发生事件:这里出了什么问题?有任何解决这个问题的方法吗?我希望调用AddXYZHandler,但显然这不适用于样式。

    5. 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; }
              }
          }
      }
      

1 个答案:

答案 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}}}">

我必须说实话......这是很多的工作。但是,现在我可以通过设置一些属性来实现具有视觉反馈的拖放操作,这似乎是值得的。