1

カスタム添付プロパティ/イベントについて質問があります。私のシナリオでは、プロパティ/イベントを任意のコントロールにアタッチしたいと考えています。このプロパティ/イベントの値は、イベント ハンドラーである必要があります。つまり、次のようになります。

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

まず、添付プロパティとして OnDrag を実装しようとしました。これは上記のケースでは機能しますが、次のケースでは失敗します。

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

"OnDrag" 文字列は、XAML システムによって RoutedEventHandler (添付プロパティの型) にできないようです。

次に試したのは、たとえば組み込みの Mouse.MouseEnter と非常によく似た、添付イベントを試して使用することでした。

この完全なコードは、下部に示されています。このバージョンでは、奇妙なことが起こっています。

  1. 示されているように (RegisterRoutedEvent 行をコメント化して) コードを実行すると、「ハンドラーの追加」関数が呼び出されたことが示されます。次に、スタイルを適用するときにxamlシステムに内部例外があります(登録されたイベントがないためだと思います)。

  2. RegisterRoutedEvent 行を有効にしてコードを実行すると、すべてが実行されますが、「ハンドラーの追加」関数は呼び出されません。ドラッグアンドドロップマネージャーに登録できるように、それを呼び出したいのですが。

  3. 不思議なことに、EventSetter のイベントを独自のものから Mouse.MouseEnter に変更すると、xaml デザイナー (MainWindow.g[.i].cs 内) によって自動的に生成されるコードが異なります。

2) が AddXYZHandler を呼び出さない理由がわかりません。MSDNは、これが機能するはずであることを示しているようです。

最後に私の質問:

  1. どうすればこれを機能させることができますか?それはまったく可能ですか?

  2. シナリオに添付イベントと添付プロパティのどちらを使用するのがよいでしょうか?

  3. プロパティの場合: OnDrag 文字列を適切な RoutedEventHandler に変換するように Style Setter を修正するにはどうすればよいですか?

  4. イベントの場合: ここで何が問題になっていますか? これを修正する方法はありますか?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; }
        }
    }
}
4

1 に答える 1

2

を使用して、ドラッグ アンド ドロップ機能を完全に実装することに成功しましたAttached Properties。もし私があなただったら、カスタム イベントを使用するのは避けたいと思います。なぜなら、あなたはそのパラメーターに行き詰まっているからです。個人的にはICommand代わりに行きましたが、s を使用することもできますdelegate

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

正直なところ、これは大変な作業でした。しかし、いくつかのプロパティを設定するだけで視覚的なフィードバックを伴うドラッグ アンド ドロップ操作を実装できるようになったので、それだけの価値があるように思えます。

于 2013-09-10T09:11:35.277 に答える